diff --git a/src/main/scala/eu/sim642/adventofcode2024/Day24.scala b/src/main/scala/eu/sim642/adventofcode2024/Day24.scala index e7f8e6c8..696a8efd 100644 --- a/src/main/scala/eu/sim642/adventofcode2024/Day24.scala +++ b/src/main/scala/eu/sim642/adventofcode2024/Day24.scala @@ -1,8 +1,9 @@ package eu.sim642.adventofcode2024 -import scala.annotation.tailrec import scala.collection.mutable +import eu.sim642.adventofcodelib.IteratorImplicits._ + object Day24 { enum Op { @@ -16,7 +17,11 @@ object Day24 { case Gate(lhs: String, op: Op, rhs: String) } - class CyclicCircuit extends RuntimeException + /** + * Exception to indicate cyclic circuit evaluation. + * When trying for swaps in part 2, cycles may be introduced, which otherwise (slowly) lead to StackOverflowError. + */ + class CircuitCycleException extends RuntimeException case class Circuit(wireMap: Map[String, Wire]) { def zValue: Long = { @@ -24,7 +29,7 @@ object Day24 { def evalName(name: String, called: Set[String]): Boolean = if (called.contains(name)) - throw new CyclicCircuit + throw new CircuitCycleException else memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name)) @@ -54,7 +59,7 @@ object Day24 { def evalName(name: String, called: Set[String]): Set[String] = if (called.contains(name)) - throw new CyclicCircuit + throw new CircuitCycleException else memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name) + name) @@ -91,62 +96,57 @@ object Day24 { withXValue(xValue).withYValue(yValue).zValue } - def findWrongBits(circuit: Circuit): Seq[(String, String)] = { - + def findWireSwaps(circuit: Circuit): Seq[(String, String)] = { def isCorrect(circuit: Circuit, i: Int): Boolean = { (for { + // must also check previous bit to account for incoming carry xBit <- 0 to 3 yBit <- 0 to 3 xValue = xBit.toLong << i >> 1 yValue = yBit.toLong << i >> 1 - //if (try {circuit.dependencies("z45"); true} catch {case e: CyclicCircuit => false}) - } yield try {circuit.add(xValue, yValue) == xValue + yValue} catch {case e: CyclicCircuit => false}).forall(identity) + } yield { + try circuit.add(xValue, yValue) == xValue + yValue + catch case _: CircuitCycleException => false + }).forall(identity) } - def helper(circuit: Circuit, i: Int, acc: Seq[(String, String)]): Seq[Seq[(String, String)]] = { + def helper(circuit: Circuit, i: Int, acc: List[(String, String)]): Iterator[List[(String, String)]] = { if (acc.sizeIs > 4) - Seq.empty + Iterator.empty else if (i > 44) - Seq(acc) + Iterator.single(acc) else if (isCorrect(circuit, i)) helper(circuit, i + 1, acc) else { - println(i) val depsPrev = circuit.dependencies(s"z${i - 1}") val deps = circuit.dependencies(s"z$i") val depsNext = circuit.dependencies(s"z${i + 1}") val depsNext2 = circuit.dependencies(s"z${i + 2}") val wrong1 = ((deps -- depsPrev) ++ (depsNext -- deps)).filterNot(_.startsWith("x")).filterNot(_.startsWith("y")) val wrong2 = (depsNext2 -- depsPrev).filterNot(_.startsWith("x")).filterNot(_.startsWith("y")) - println(wrong1) - println(wrong2) val swaps = for { name1 <- wrong1 name2 <- wrong2 + // order names in swap to avoid duplicate checking minName = if (name1 < name2) name1 else name2 maxName = if (name1 < name2) name2 else name1 } yield (minName, maxName) for { - (name1, name2) <- swaps.toSeq - //name2 <- wrong2 + swap@(name1, name2) <- swaps.iterator newCircuit = circuit.swapped(name1, name2) - //() = println((name1, name2)) - if isCorrect(newCircuit, i - 1) if isCorrect(newCircuit, i) - swap = (name1, name2) - rest <- helper(newCircuit, i + 1, acc :+ swap) - } yield rest + newAcc <- helper(newCircuit, i + 1, swap :: acc) + } yield newAcc } } - val all = helper(circuit, 0, Seq.empty) - all.foreach(println) - all.head + val swapss = helper(circuit, 0, Nil) + swapss.head } - def findWrongBitsString(circuit: Circuit): String = - findWrongBits(circuit).flatMap({ case (a, b) => Seq(a, b)}).sorted.mkString(",") + def findWireSwapsString(circuit: Circuit): String = + findWireSwaps(circuit).flatMap({ case (name1, name2) => Seq(name1, name2) }).sorted.mkString(",") def parseInput(s: String): (String, Wire.Input) = s match { case s"$name: 0" => name -> Wire.Input(false) @@ -186,16 +186,6 @@ object Day24 { def main(args: Array[String]): Unit = { val circuit = parseCircuit(input) println(circuit.zValue) - findWrongBits(circuit) - val circuit2 = circuit.swapped("z21", "nhn").swapped("tvb", "khg").swapped("z33", "gst").swapped("z12", "vdc") - //printCircuitDot(circuit2) - //println(circuit2.zValue) - //println("51401618891888") - - val circuit3 = circuit2.withXValue(0) - //println(circuit3.zValue) - - //println(Seq("z21", "nhn", "tvb", "khg", "z33", "gst", "z12", "vdc").sorted.mkString(",")) - // part 2: gst,khg,nhn,tvb,vdc,z12,z21,z33 - correct + println(findWireSwapsString(circuit)) } } diff --git a/src/test/scala/eu/sim642/adventofcode2024/Day24Test.scala b/src/test/scala/eu/sim642/adventofcode2024/Day24Test.scala index 99b11416..e08de7b2 100644 --- a/src/test/scala/eu/sim642/adventofcode2024/Day24Test.scala +++ b/src/test/scala/eu/sim642/adventofcode2024/Day24Test.scala @@ -76,6 +76,6 @@ class Day24Test extends AnyFunSuite { } test("Part 2 input answer") { - assert(findWrongBitsString(parseCircuit(input)) == "gst,khg,nhn,tvb,vdc,z12,z21,z33") + assert(findWireSwapsString(parseCircuit(input)) == "gst,khg,nhn,tvb,vdc,z12,z21,z33") } }