@@ -7,6 +7,7 @@ import jurisk.adventofcode.y2024.Advent24.Operation.Or
7
7
import jurisk .adventofcode .y2024 .Advent24 .Operation .Xor
8
8
import jurisk .algorithms .graph .GraphAlgorithms
9
9
import jurisk .collections .immutable .SetOfTwo
10
+ import jurisk .math .LongOps
10
11
import jurisk .utils .CollectionOps .OptionOps
11
12
import jurisk .utils .ConversionOps .BooleanOps
12
13
import jurisk .utils .ConversionOps .IntOps
@@ -16,6 +17,7 @@ import jurisk.utils.Parsing.StringOps
16
17
import mouse .all .booleanSyntaxMouse
17
18
18
19
import scala .annotation .tailrec
20
+ import scala .util .Random
19
21
20
22
object Advent24 extends IOApp .Simple {
21
23
private val InputBits = 45
@@ -29,11 +31,27 @@ object Advent24 extends IOApp.Simple {
29
31
def + (pair : (Wire , Boolean )): Values = new Values (map + pair)
30
32
def ++ (other : Values ): Values = new Values (map ++ other.map)
31
33
34
+ private def bitsToLong (bits : Map [Int , Boolean ]) = {
35
+ val bitsStr = bits.toSeq.sorted
36
+ .map { case (_, b) => if (b) " 1" else " 0" }
37
+ .mkString
38
+ .reverse
39
+ java.lang.Long .parseLong(bitsStr, 2 )
40
+ }
41
+
42
+ def xValue : Long = {
43
+ val z = map.collect { case (Wire .X (zIdx), v) => (zIdx, v) }
44
+ bitsToLong(z)
45
+ }
46
+
47
+ def yValue : Long = {
48
+ val z = map.collect { case (Wire .Y (zIdx), v) => (zIdx, v) }
49
+ bitsToLong(z)
50
+ }
51
+
32
52
def zValue : Long = {
33
- val z = map.collect { case (Wire .Z (zIdx), v) => (zIdx, v) }.toList
34
- val zBits =
35
- z.sorted.map { case (_, b) => if (b) " 1" else " 0" }.mkString.reverse
36
- java.lang.Long .parseLong(zBits, 2 )
53
+ val z = map.collect { case (Wire .Z (zIdx), v) => (zIdx, v) }
54
+ bitsToLong(z)
37
55
}
38
56
}
39
57
@@ -47,6 +65,15 @@ object Advent24 extends IOApp.Simple {
47
65
}.toMap
48
66
}
49
67
68
+ def randomXY : Values = Values {
69
+ (0 until InputBits ).flatMap { b =>
70
+ List (
71
+ Wire .X (b) -> Random .nextBoolean(),
72
+ Wire .Y (b) -> Random .nextBoolean(),
73
+ )
74
+ }.toMap
75
+ }
76
+
50
77
def apply (pairs : (Wire , Boolean )* ): Values = Values (pairs.toMap)
51
78
52
79
def parse (s : String ): Values = {
@@ -105,15 +132,33 @@ object Advent24 extends IOApp.Simple {
105
132
}
106
133
107
134
final case class Connections private (map : Map [Wire , Connection ]) {
108
- val allWires : Set [Wire ] = map.flatMap { case (k, v) =>
135
+ val allWires : Set [Wire ] = map.flatMap { case (k, v) =>
109
136
Set (k, v.a, v.b)
110
137
}.toSet
111
- val allOutputs : Set [Wire ] = map.keySet
138
+ private val allOutputs : Set [Wire ] = map.keySet
112
139
113
140
def foreach (f : Connection => Unit ): Unit = map.values foreach f
114
141
115
- // TODO: This doesn't do a sufficient test, as these bit-by-bit tests don't catch all issues that could happen. Consider adding random numbers.
116
- private def errorsOnAddition : Int = {
142
+ private def errorsOnAddition : Int =
143
+ // We care more about `errorsBitByBit`, but since they didn't catch everything, we also care about `errorsOnRandomAddition`
144
+ 128 * errorsBitByBit + errorsOnRandomAddition
145
+
146
+ private def errorsOnRandomAddition : Int = {
147
+ val Samples = 16
148
+ (for {
149
+ _ <- 0 until Samples
150
+ r = Values .randomXY
151
+ x = r.xValue
152
+ y = r.yValue
153
+ expectedZ = x + y
154
+ solved = propagate(r).orFail(" Failed to propagate" )
155
+ z = solved.zValue
156
+ wrongBits = z ^ expectedZ
157
+ wrongBitsCount = wrongBits.bitCount
158
+ } yield wrongBitsCount).sum
159
+ }
160
+
161
+ private def errorsBitByBit : Int = {
117
162
def errorsAddingBit (bit : Int ): Int =
118
163
List (
119
164
(false , false , false , false ),
@@ -194,15 +239,12 @@ object Advent24 extends IOApp.Simple {
194
239
(current, currentSwaps)
195
240
} else {
196
241
// TODO: Try to apply Genetic Algorithm or similar...
197
- // TODO: Have a wider set of swaps to pick from!
198
- val swaps = Set (
199
- SetOfTwo (" hbk" , " z14" ),
200
- SetOfTwo (" kvn" , " z18" ),
201
- SetOfTwo (" dbb" , " z23" ),
202
- SetOfTwo (" cvh" , " tfn" ),
203
- )
204
- val candidates = swaps.flatMap(_.toSet).map(Wire .parse).toIndexedSeq
205
- // val candidates = current.allOutputs
242
+ // Note: The swaps for our data are:
243
+ // 1. hbk <-> z14
244
+ // 2. kvn <-> z18
245
+ // 3. dbb <-> z23
246
+ // 4. cvh <-> tfn
247
+ val candidates = current.allOutputs.toIndexedSeq
206
248
(for {
207
249
aIdx <- candidates.indices
208
250
bIdx <- candidates.indices
0 commit comments