@@ -2,8 +2,6 @@ package jurisk.adventofcode.y2024
2
2
3
3
import cats .effect .IO
4
4
import cats .effect .IOApp
5
- import jurisk .adventofcode .y2024 .Advent24 .Connections .InputBits
6
- import jurisk .adventofcode .y2024 .Advent24 .Connections .OutputBits
7
5
import jurisk .adventofcode .y2024 .Advent24 .Operation .And
8
6
import jurisk .adventofcode .y2024 .Advent24 .Operation .Or
9
7
import jurisk .adventofcode .y2024 .Advent24 .Operation .Xor
@@ -20,8 +18,46 @@ import mouse.all.booleanSyntaxMouse
20
18
import scala .annotation .tailrec
21
19
22
20
object Advent24 extends IOApp .Simple {
23
- private type Values = Map [Wire , Boolean ]
24
- type Input = (Values , Connections )
21
+ private val InputBits = 45
22
+ private val OutputBits = InputBits + 1
23
+
24
+ type Input = (Values , Connections )
25
+
26
+ final case class Values private (map : Map [Wire , Boolean ]) {
27
+ def getOrFalse (wire : Wire ): Boolean = map.getOrElse(wire, false )
28
+ def contains (wire : Wire ): Boolean = map.contains(wire)
29
+ def + (pair : (Wire , Boolean )): Values = new Values (map + pair)
30
+ def ++ (other : Values ): Values = new Values (map ++ other.map)
31
+
32
+ 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 )
37
+ }
38
+ }
39
+
40
+ private object Values {
41
+ val Zero : Values = Values {
42
+ (0 until InputBits ).flatMap { b =>
43
+ List (
44
+ Wire .X (b) -> false ,
45
+ Wire .Y (b) -> false ,
46
+ )
47
+ }.toMap
48
+ }
49
+
50
+ def apply (pairs : (Wire , Boolean )* ): Values = Values (pairs.toMap)
51
+
52
+ def parse (s : String ): Values = {
53
+ val map = s.splitLines
54
+ .map(
55
+ _.parsePairUnsafe(" : " , Wire .parse, _.toInt.toBooleanStrict01Unsafe)
56
+ )
57
+ .toMap
58
+ new Values (map)
59
+ }
60
+ }
25
61
26
62
sealed trait Wire extends Product with Serializable
27
63
private object Wire {
@@ -50,13 +86,7 @@ object Advent24 extends IOApp.Simple {
50
86
}
51
87
}
52
88
53
- private def replace (s : Wire , replacements : Map [Wire , Wire ]): Wire =
54
- replacements.getOrElse(s, s)
55
-
56
89
object Connections {
57
- private val InputBits = 45
58
- private val OutputBits = InputBits + 1
59
-
60
90
def parse (s : String ): Connections =
61
91
Connections .fromIterable(s.splitLines.toSet map Connection .parse)
62
92
@@ -84,32 +114,24 @@ object Advent24 extends IOApp.Simple {
84
114
85
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.
86
116
private def errorsOnAddition : Int = {
87
- def errorsAddingBit (bit : Int ): Int = {
88
- def zeroWires : Values =
89
- (0 until InputBits ).flatMap { b =>
90
- List (
91
- Wire .X (b) -> false ,
92
- Wire .Y (b) -> false ,
93
- )
94
- }.toMap
95
-
117
+ def errorsAddingBit (bit : Int ): Int =
96
118
List (
97
119
(false , false , false , false ),
98
120
(false , true , true , false ),
99
121
(true , false , true , false ),
100
122
(true , true , false , true ),
101
123
).map { case (x, y, r, c) =>
102
- val values = zeroWires ++ Map (Wire .X (bit) -> x, Wire .Y (bit) -> y)
124
+ val values = Values . Zero ++ Values (Wire .X (bit) -> x, Wire .Y (bit) -> y)
103
125
val output = propagate(values).orFail(" Failed to propagate" )
104
- val invalidR = output.getOrElse (Wire .Z (bit), false ) != r
126
+ val invalidR = output.getOrFalse (Wire .Z (bit)) != r
105
127
val carryBit = bit + 1
106
- val invalidC = output.getOrElse (Wire .Z (carryBit), false ) != c
128
+ val invalidC = output.getOrFalse (Wire .Z (carryBit)) != c
107
129
val extraBits = (0 until OutputBits )
108
130
.filter { b =>
109
131
b != bit && b != carryBit
110
132
}
111
133
.count { i =>
112
- output.getOrElse (Wire .Z (i), false )
134
+ output.getOrFalse (Wire .Z (i))
113
135
}
114
136
val DebugPrint = false
115
137
if (DebugPrint && (invalidR || invalidC || extraBits > 0 )) {
@@ -118,7 +140,6 @@ object Advent24 extends IOApp.Simple {
118
140
}
119
141
invalidR.toInt + invalidC.toInt + extraBits
120
142
}.sum
121
- }
122
143
123
144
(0 until InputBits ).map { bit =>
124
145
errorsAddingBit(bit)
@@ -211,8 +232,8 @@ object Advent24 extends IOApp.Simple {
211
232
212
233
final case class Connection (a : Wire , b : Wire , op : Operation ) {
213
234
def result (values : Values ): Boolean = {
214
- val aV = values.getOrElse(a, false )
215
- val bV = values.getOrElse(b, false )
235
+ val aV = values.getOrFalse(a )
236
+ val bV = values.getOrFalse(b )
216
237
op match {
217
238
case And => aV && bV
218
239
case Or => aV || bV
@@ -265,21 +286,14 @@ object Advent24 extends IOApp.Simple {
265
286
266
287
def parse (input : String ): Input =
267
288
input.parsePairByDoubleNewline(
268
- _.splitLines
269
- .map(
270
- _.parsePairUnsafe(" : " , Wire .parse, _.toInt.toBooleanStrict01Unsafe)
271
- )
272
- .toMap,
289
+ Values .parse,
273
290
Connections .parse,
274
291
)
275
292
276
- def part1 (data : Input ): BigInt = {
293
+ def part1 (data : Input ): Long = {
277
294
val (wires, connections) = data
278
295
val results = connections.propagate(wires).orFail(" Failed to propagate" )
279
- val z = results.collect { case (Wire .Z (zIdx), v) => (zIdx, v) }.toList
280
- val zBits =
281
- z.sorted.map { case (_, b) => if (b) " 1" else " 0" }.mkString.reverse
282
- BigInt (zBits, 2 )
296
+ results.zValue
283
297
}
284
298
285
299
private def debugWrite (connections : Connections ): IO [Unit ] = {
0 commit comments