1
1
package eu .sim642 .adventofcode2024
2
2
3
- import scala .annotation .tailrec
4
3
import scala .collection .mutable
5
4
5
+ import eu .sim642 .adventofcodelib .IteratorImplicits ._
6
+
6
7
object Day24 {
7
8
8
9
enum Op {
@@ -16,15 +17,19 @@ object Day24 {
16
17
case Gate (lhs : String , op : Op , rhs : String )
17
18
}
18
19
19
- class CyclicCircuit extends RuntimeException
20
+ /**
21
+ * Exception to indicate cyclic circuit evaluation.
22
+ * When trying for swaps in part 2, cycles may be introduced, which otherwise (slowly) lead to StackOverflowError.
23
+ */
24
+ class CircuitCycleException extends RuntimeException
20
25
21
26
case class Circuit (wireMap : Map [String , Wire ]) {
22
27
def zValue : Long = {
23
28
val memo = mutable.Map .empty[String , Boolean ]
24
29
25
30
def evalName (name : String , called : Set [String ]): Boolean =
26
31
if (called.contains(name))
27
- throw new CyclicCircuit
32
+ throw new CircuitCycleException
28
33
else
29
34
memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name))
30
35
@@ -54,7 +59,7 @@ object Day24 {
54
59
55
60
def evalName (name : String , called : Set [String ]): Set [String ] =
56
61
if (called.contains(name))
57
- throw new CyclicCircuit
62
+ throw new CircuitCycleException
58
63
else
59
64
memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name) + name)
60
65
@@ -91,62 +96,57 @@ object Day24 {
91
96
withXValue(xValue).withYValue(yValue).zValue
92
97
}
93
98
94
- def findWrongBits (circuit : Circuit ): Seq [(String , String )] = {
95
-
99
+ def findWireSwaps (circuit : Circuit ): Seq [(String , String )] = {
96
100
def isCorrect (circuit : Circuit , i : Int ): Boolean = {
97
101
(for {
102
+ // must also check previous bit to account for incoming carry
98
103
xBit <- 0 to 3
99
104
yBit <- 0 to 3
100
105
xValue = xBit.toLong << i >> 1
101
106
yValue = yBit.toLong << i >> 1
102
- // if (try {circuit.dependencies("z45"); true} catch {case e: CyclicCircuit => false})
103
- } yield try {circuit.add(xValue, yValue) == xValue + yValue} catch {case e : CyclicCircuit => false }).forall(identity)
107
+ } yield {
108
+ try circuit.add(xValue, yValue) == xValue + yValue
109
+ catch case _ : CircuitCycleException => false
110
+ }).forall(identity)
104
111
}
105
112
106
- def helper (circuit : Circuit , i : Int , acc : Seq [(String , String )]): Seq [ Seq [(String , String )]] = {
113
+ def helper (circuit : Circuit , i : Int , acc : List [(String , String )]): Iterator [ List [(String , String )]] = {
107
114
if (acc.sizeIs > 4 )
108
- Seq .empty
115
+ Iterator .empty
109
116
else if (i > 44 )
110
- Seq (acc)
117
+ Iterator .single (acc)
111
118
else if (isCorrect(circuit, i))
112
119
helper(circuit, i + 1 , acc)
113
120
else {
114
- println(i)
115
121
val depsPrev = circuit.dependencies(s " z ${i - 1 }" )
116
122
val deps = circuit.dependencies(s " z $i" )
117
123
val depsNext = circuit.dependencies(s " z ${i + 1 }" )
118
124
val depsNext2 = circuit.dependencies(s " z ${i + 2 }" )
119
125
val wrong1 = ((deps -- depsPrev) ++ (depsNext -- deps)).filterNot(_.startsWith(" x" )).filterNot(_.startsWith(" y" ))
120
126
val wrong2 = (depsNext2 -- depsPrev).filterNot(_.startsWith(" x" )).filterNot(_.startsWith(" y" ))
121
- println(wrong1)
122
- println(wrong2)
123
127
val swaps =
124
128
for {
125
129
name1 <- wrong1
126
130
name2 <- wrong2
131
+ // order names in swap to avoid duplicate checking
127
132
minName = if (name1 < name2) name1 else name2
128
133
maxName = if (name1 < name2) name2 else name1
129
134
} yield (minName, maxName)
130
135
for {
131
- (name1, name2) <- swaps.toSeq
132
- // name2 <- wrong2
136
+ swap@ (name1, name2) <- swaps.iterator
133
137
newCircuit = circuit.swapped(name1, name2)
134
- // () = println((name1, name2))
135
- if isCorrect(newCircuit, i - 1 )
136
138
if isCorrect(newCircuit, i)
137
- swap = (name1, name2)
138
- rest <- helper(newCircuit, i + 1 , acc :+ swap)
139
- } yield rest
139
+ newAcc <- helper(newCircuit, i + 1 , swap :: acc)
140
+ } yield newAcc
140
141
}
141
142
}
142
143
143
- val all = helper(circuit, 0 , Seq .empty)
144
- all.foreach(println)
145
- all.head
144
+ val swapss = helper(circuit, 0 , Nil )
145
+ swapss.head
146
146
}
147
147
148
- def findWrongBitsString (circuit : Circuit ): String =
149
- findWrongBits (circuit).flatMap({ case (a, b ) => Seq (a, b) }).sorted.mkString(" ," )
148
+ def findWireSwapsString (circuit : Circuit ): String =
149
+ findWireSwaps (circuit).flatMap({ case (name1, name2 ) => Seq (name1, name2) }).sorted.mkString(" ," )
150
150
151
151
def parseInput (s : String ): (String , Wire .Input ) = s match {
152
152
case s " $name: 0 " => name -> Wire .Input (false )
@@ -186,16 +186,6 @@ object Day24 {
186
186
def main (args : Array [String ]): Unit = {
187
187
val circuit = parseCircuit(input)
188
188
println(circuit.zValue)
189
- findWrongBits(circuit)
190
- val circuit2 = circuit.swapped(" z21" , " nhn" ).swapped(" tvb" , " khg" ).swapped(" z33" , " gst" ).swapped(" z12" , " vdc" )
191
- // printCircuitDot(circuit2)
192
- // println(circuit2.zValue)
193
- // println("51401618891888")
194
-
195
- val circuit3 = circuit2.withXValue(0 )
196
- // println(circuit3.zValue)
197
-
198
- // println(Seq("z21", "nhn", "tvb", "khg", "z33", "gst", "z12", "vdc").sorted.mkString(","))
199
- // part 2: gst,khg,nhn,tvb,vdc,z12,z21,z33 - correct
189
+ println(findWireSwapsString(circuit))
200
190
}
201
191
}
0 commit comments