Skip to content

Commit 659e71a

Browse files
committed
Solve 2024 day 24 part 2 automatically
1 parent 5b54798 commit 659e71a

File tree

2 files changed

+103
-12
lines changed

2 files changed

+103
-12
lines changed

src/main/scala/eu/sim642/adventofcode2024/Day24.scala

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package eu.sim642.adventofcode2024
22

3+
import scala.annotation.tailrec
34
import scala.collection.mutable
45

56
object Day24 {
@@ -15,18 +16,23 @@ object Day24 {
1516
case Gate(lhs: String, op: Op, rhs: String)
1617
}
1718

19+
class CyclicCircuit extends RuntimeException
20+
1821
case class Circuit(wireMap: Map[String, Wire]) {
1922
def zValue: Long = {
2023
val memo = mutable.Map.empty[String, Boolean]
2124

22-
def evalName(name: String): Boolean =
23-
memo.getOrElseUpdate(name, evalWire(wireMap(name)))
25+
def evalName(name: String, called: Set[String]): Boolean =
26+
if (called.contains(name))
27+
throw new CyclicCircuit
28+
else
29+
memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name))
2430

25-
def evalWire(wire: Wire): Boolean = wire match {
31+
def evalWire(wire: Wire, called: Set[String]): Boolean = wire match {
2632
case Wire.Input(value) => value
2733
case Wire.Gate(lhs, op, rhs) =>
28-
val left = evalName(lhs)
29-
val right = evalName(rhs)
34+
val left = evalName(lhs, called)
35+
val right = evalName(rhs, called)
3036
op match {
3137
case Op.And => left && right
3238
case Op.Or => left || right
@@ -39,10 +45,30 @@ object Day24 {
3945
.toSeq
4046
.sorted
4147
.foldRight(0L)({ case (zName, acc) =>
42-
acc << 1 | (if (evalName(zName)) 1 else 0)
48+
acc << 1 | (if (evalName(zName, Set.empty)) 1 else 0)
4349
})
4450
}
4551

52+
def dependencies(name: String): Set[String] = {
53+
val memo = mutable.Map.empty[String, Set[String]]
54+
55+
def evalName(name: String, called: Set[String]): Set[String] =
56+
if (called.contains(name))
57+
throw new CyclicCircuit
58+
else
59+
memo.getOrElseUpdate(name, evalWire(wireMap(name), called + name) + name)
60+
61+
def evalWire(wire: Wire, called: Set[String]): Set[String] = wire match {
62+
case Wire.Input(value) => Set.empty
63+
case Wire.Gate(lhs, op, rhs) =>
64+
val left = evalName(lhs, called)
65+
val right = evalName(rhs, called)
66+
left ++ right
67+
}
68+
69+
evalName(name, Set.empty)
70+
}
71+
4672
def swapped(name1: String, name2: String): Circuit =
4773
Circuit(wireMap + (name1 -> wireMap(name2)) + (name2 -> wireMap(name1)))
4874

@@ -60,15 +86,75 @@ object Day24 {
6086

6187
def withXValue(value: Long): Circuit = withInputValue("x", value)
6288
def withYValue(value: Long): Circuit = withInputValue("y", value)
89+
90+
def add(xValue: Long, yValue: Long): Long =
91+
withXValue(xValue).withYValue(yValue).zValue
6392
}
6493

94+
def findWrongBits(circuit: Circuit): Seq[(String, String)] = {
95+
96+
def isCorrect(circuit: Circuit, i: Int): Boolean = {
97+
(for {
98+
xBit <- 0 to 3
99+
yBit <- 0 to 3
100+
xValue = xBit.toLong << i >> 1
101+
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)
104+
}
105+
106+
def helper(circuit: Circuit, i: Int, acc: Seq[(String, String)]): Seq[Seq[(String, String)]] = {
107+
if (acc.sizeIs > 4)
108+
Seq.empty
109+
else if (i > 44)
110+
Seq(acc)
111+
else if (isCorrect(circuit, i))
112+
helper(circuit, i + 1, acc)
113+
else {
114+
println(i)
115+
val depsPrev = circuit.dependencies(s"z${i - 1}")
116+
val deps = circuit.dependencies(s"z$i")
117+
val depsNext = circuit.dependencies(s"z${i + 1}")
118+
val depsNext2 = circuit.dependencies(s"z${i + 2}")
119+
val wrong1 = ((deps -- depsPrev) ++ (depsNext -- deps)).filterNot(_.startsWith("x")).filterNot(_.startsWith("y"))
120+
val wrong2 = (depsNext2 -- depsPrev).filterNot(_.startsWith("x")).filterNot(_.startsWith("y"))
121+
println(wrong1)
122+
println(wrong2)
123+
val swaps =
124+
for {
125+
name1 <- wrong1
126+
name2 <- wrong2
127+
minName = if (name1 < name2) name1 else name2
128+
maxName = if (name1 < name2) name2 else name1
129+
} yield (minName, maxName)
130+
for {
131+
(name1, name2) <- swaps.toSeq
132+
//name2 <- wrong2
133+
newCircuit = circuit.swapped(name1, name2)
134+
//() = println((name1, name2))
135+
if isCorrect(newCircuit, i - 1)
136+
if isCorrect(newCircuit, i)
137+
swap = (name1, name2)
138+
rest <- helper(newCircuit, i + 1, acc :+ swap)
139+
} yield rest
140+
}
141+
}
142+
143+
val all = helper(circuit, 0, Seq.empty)
144+
all.foreach(println)
145+
all.head
146+
}
147+
148+
def findWrongBitsString(circuit: Circuit): String =
149+
findWrongBits(circuit).flatMap({ case (a, b) => Seq(a, b)}).sorted.mkString(",")
150+
65151
def parseInput(s: String): (String, Wire.Input) = s match {
66152
case s"$name: 0" => name -> Wire.Input(false)
67153
case s"$name: 1" => name -> Wire.Input(true)
68154
}
69155

70156
def parseGate(s: String): (String, Wire.Gate) = s match {
71-
case s"$lhs AND $rhs -> $name" => name ->Wire.Gate(lhs, Op.And, rhs)
157+
case s"$lhs AND $rhs -> $name" => name -> Wire.Gate(lhs, Op.And, rhs)
72158
case s"$lhs OR $rhs -> $name" => name -> Wire.Gate(lhs, Op.Or, rhs)
73159
case s"$lhs XOR $rhs -> $name" => name -> Wire.Gate(lhs, Op.Xor, rhs)
74160
}
@@ -100,15 +186,16 @@ object Day24 {
100186
def main(args: Array[String]): Unit = {
101187
val circuit = parseCircuit(input)
102188
println(circuit.zValue)
189+
findWrongBits(circuit)
103190
val circuit2 = circuit.swapped("z21", "nhn").swapped("tvb", "khg").swapped("z33", "gst").swapped("z12", "vdc")
104-
printCircuitDot(circuit2)
105-
println(circuit2.zValue)
106-
println("51401618891888")
191+
//printCircuitDot(circuit2)
192+
//println(circuit2.zValue)
193+
//println("51401618891888")
107194

108195
val circuit3 = circuit2.withXValue(0)
109-
println(circuit3.zValue)
196+
//println(circuit3.zValue)
110197

111-
println(Seq("z21", "nhn", "tvb", "khg", "z33", "gst", "z12", "vdc").sorted.mkString(","))
198+
//println(Seq("z21", "nhn", "tvb", "khg", "z33", "gst", "z12", "vdc").sorted.mkString(","))
112199
// part 2: gst,khg,nhn,tvb,vdc,z12,z21,z33 - correct
113200
}
114201
}

src/test/scala/eu/sim642/adventofcode2024/Day24Test.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,8 @@ class Day24Test extends AnyFunSuite {
7474
test("Part 1 input answer") {
7575
assert(parseCircuit(input).zValue == 51410244478064L)
7676
}
77+
78+
test("Part 2 input answer") {
79+
assert(findWrongBitsString(parseCircuit(input)) == "gst,khg,nhn,tvb,vdc,z12,z21,z33")
80+
}
7781
}

0 commit comments

Comments
 (0)