11package eu .sim642 .adventofcode2024
22
3- import scala .annotation .tailrec
43import scala .collection .mutable
54
5+ import eu .sim642 .adventofcodelib .IteratorImplicits ._
6+
67object Day24 {
78
89 enum Op {
@@ -16,15 +17,19 @@ object Day24 {
1617 case Gate (lhs : String , op : Op , rhs : String )
1718 }
1819
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
2025
2126 case class Circuit (wireMap : Map [String , Wire ]) {
2227 def zValue : Long = {
2328 val memo = mutable.Map .empty[String , Boolean ]
2429
2530 def evalName (name : String , called : Set [String ]): Boolean =
2631 if (called.contains(name))
27- throw new CyclicCircuit
32+ throw new CircuitCycleException
2833 else
2934 memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name))
3035
@@ -54,7 +59,7 @@ object Day24 {
5459
5560 def evalName (name : String , called : Set [String ]): Set [String ] =
5661 if (called.contains(name))
57- throw new CyclicCircuit
62+ throw new CircuitCycleException
5863 else
5964 memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name) + name)
6065
@@ -91,62 +96,57 @@ object Day24 {
9196 withXValue(xValue).withYValue(yValue).zValue
9297 }
9398
94- def findWrongBits (circuit : Circuit ): Seq [(String , String )] = {
95-
99+ def findWireSwaps (circuit : Circuit ): Seq [(String , String )] = {
96100 def isCorrect (circuit : Circuit , i : Int ): Boolean = {
97101 (for {
102+ // must also check previous bit to account for incoming carry
98103 xBit <- 0 to 3
99104 yBit <- 0 to 3
100105 xValue = xBit.toLong << i >> 1
101106 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)
104111 }
105112
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 )]] = {
107114 if (acc.sizeIs > 4 )
108- Seq .empty
115+ Iterator .empty
109116 else if (i > 44 )
110- Seq (acc)
117+ Iterator .single (acc)
111118 else if (isCorrect(circuit, i))
112119 helper(circuit, i + 1 , acc)
113120 else {
114- println(i)
115121 val depsPrev = circuit.dependencies(s " z ${i - 1 }" )
116122 val deps = circuit.dependencies(s " z $i" )
117123 val depsNext = circuit.dependencies(s " z ${i + 1 }" )
118124 val depsNext2 = circuit.dependencies(s " z ${i + 2 }" )
119125 val wrong1 = ((deps -- depsPrev) ++ (depsNext -- deps)).filterNot(_.startsWith(" x" )).filterNot(_.startsWith(" y" ))
120126 val wrong2 = (depsNext2 -- depsPrev).filterNot(_.startsWith(" x" )).filterNot(_.startsWith(" y" ))
121- println(wrong1)
122- println(wrong2)
123127 val swaps =
124128 for {
125129 name1 <- wrong1
126130 name2 <- wrong2
131+ // order names in swap to avoid duplicate checking
127132 minName = if (name1 < name2) name1 else name2
128133 maxName = if (name1 < name2) name2 else name1
129134 } yield (minName, maxName)
130135 for {
131- (name1, name2) <- swaps.toSeq
132- // name2 <- wrong2
136+ swap@ (name1, name2) <- swaps.iterator
133137 newCircuit = circuit.swapped(name1, name2)
134- // () = println((name1, name2))
135- if isCorrect(newCircuit, i - 1 )
136138 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
140141 }
141142 }
142143
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
146146 }
147147
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(" ," )
150150
151151 def parseInput (s : String ): (String , Wire .Input ) = s match {
152152 case s " $name: 0 " => name -> Wire .Input (false )
@@ -186,16 +186,6 @@ object Day24 {
186186 def main (args : Array [String ]): Unit = {
187187 val circuit = parseCircuit(input)
188188 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))
200190 }
201191}
0 commit comments