Skip to content

Commit d7757b5

Browse files
committed
Solution Day 24, part two (Crossed Wires)
The final solution is not really implemented out in Kotlin. * The Kotlin Notebook delivered a graphical representation of the circuit network * Considerations of how a binary adder works have led to the discovery of the swapped wires in the circuit network * Actually swapping these wires is implemented. For some sample addends, including some edge cases, the fixed circuit calculates the right results.
1 parent 8a5fb37 commit d7757b5

File tree

2 files changed

+73
-17
lines changed

2 files changed

+73
-17
lines changed

src/main/kotlin/de/ronny_h/aoc/year24/day24/CrossedWires.kt

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import de.ronny_h.aoc.AdventOfCode
44
import de.ronny_h.aoc.extensions.toBoolean
55
import de.ronny_h.aoc.extensions.toDigit
66

7-
fun main() = CrossedWires().run(66055249060558, 0)
7+
fun main() = CrossedWires().run("66055249060558", "fcd,fhp,hmk,rvf,tpc,z16,z20,z33")
88

9-
class CrossedWires : AdventOfCode<Long>(2024, 24) {
9+
class CrossedWires : AdventOfCode<String>(2024, 24) {
1010

1111
class And : (Boolean, Boolean) -> Boolean {
1212
override fun invoke(a: Boolean, b: Boolean) = a && b
@@ -51,12 +51,13 @@ class CrossedWires : AdventOfCode<Long>(2024, 24) {
5151
Gate(in1.trim(), in2.trim(), operation, result.trim())
5252
}
5353

54-
override fun part1(input: List<String>): Long {
54+
override fun part1(input: List<String>): String {
5555
val gates = parseGates(input)
5656
return parseWires(input)
5757
.associateBy(Wire::name)
5858
.simulateGates(gates)
5959
.withPrefixAsDecimal("z")
60+
.toString()
6061
}
6162

6263
private fun Map<String, Wire>.withPrefixAsDecimal(prefix: String): Long = withPrefixAsBinary(prefix)
@@ -83,31 +84,84 @@ class CrossedWires : AdventOfCode<Long>(2024, 24) {
8384
return wires
8485
}
8586

86-
override fun part2(input: List<String>): Long {
87+
override fun part2(input: List<String>): String {
8788
// inputs: x00..x44, y00..y44
8889
// outputs: z00..z45
89-
val gates = parseGates(input)
9090
val wires = parseWires(input).associateBy(Wire::name)
91-
91+
val gates = parseGates(input)
9292
simulateGates(wires, gates)
93-
add(0, 0, wires, gates)
94-
add("1".repeat(45).toLong(2), 0, wires, gates)
95-
add(0, "1".repeat(45).toLong(2), wires, gates)
96-
add("1".repeat(45).toLong(2), "1".repeat(45).toLong(2), wires, gates)
9793

98-
return 0
94+
val fixedGates = fixBySwappingTheRightOutputWires(gates)
95+
simulateGates(wires, fixedGates)
96+
97+
val largest45BitNumber = "1".repeat(45).toLong(2)
98+
listOf(
99+
Pair(0L, 0L),
100+
Pair(1000L, 0L),
101+
Pair(0L, 1000L),
102+
Pair(largest45BitNumber, 0L),
103+
Pair(0L, largest45BitNumber),
104+
Pair(largest45BitNumber, largest45BitNumber),
105+
).forEach { (x, y) ->
106+
check(add(x, y, wires, fixedGates) == x + y)
107+
}
108+
109+
return listOf("z16", "hmk", "z20", "fhp", "rvf", "tpc", "z33", "fcd").sorted().joinToString(",")
110+
}
111+
112+
/*
113+
Using only XOR, AND and OR gates, a simple adder works like this:
114+
115+
The first digit:
116+
- the digit: z_0 = x_0 XOR y_0
117+
- carryover: c_1 = x_0 AND y_0
118+
119+
For higher digits:
120+
- the digit: z_i = x_i XOR y_i XOR c_i
121+
- carryover: c_i+1 = (x_i AND y_i) OR ((x_i XOR y_i) AND c_i)
122+
123+
Looking at the graph generated by PlotCrossedWires.ipynb from top to bottom, the first z_i that is not a
124+
successor of an XOR node, is z_16.
125+
Its predecessor is AND, in the input: y16 AND x16 -> z16
126+
It should have been the XOR node on the same level as z16: vmr XOR bnc -> hmk.
127+
=> swap `z16,hmk`
128+
129+
The same with z_20
130+
=> swap `z20,fhp`
131+
132+
z_27 has only one XOR as predecessor
133+
=> swap `rvf,tpc`
134+
135+
z_33 has an OR as predecessor
136+
=> swap `z33,fcd`
137+
*/
138+
fun fixBySwappingTheRightOutputWires(gates: List<Gate>): List<Gate> = gates
139+
.swapOutWires("z16", "hmk")
140+
.swapOutWires("z20", "fhp")
141+
.swapOutWires("rvf", "tpc")
142+
.swapOutWires("z33", "fcd")
143+
144+
private fun List<Gate>.swapOutWires(
145+
firstOut: String,
146+
secondOut: String
147+
): List<Gate> {
148+
val first = first { it.out == firstOut }
149+
val second = first { it.out == secondOut }
150+
val oneSwapped = first.copy(out = secondOut)
151+
val twoSwapped = second.copy(out = firstOut)
152+
return filter { it.out !in listOf(firstOut, secondOut) } + oneSwapped + twoSwapped
99153
}
100154

101-
private fun add(
155+
fun add(
102156
x: Long,
103157
y: Long,
104158
wires: Map<String, Wire>,
105159
gates: List<Gate>
106-
) {
160+
): Long {
107161
val modifiedWires = wires.toMutableMap()
108162
modifiedWires.putAll(x.toWires("x"))
109163
modifiedWires.putAll(y.toWires("y"))
110-
simulateGates(modifiedWires, gates)
164+
return simulateGates(modifiedWires, gates)
111165
}
112166

113167
private fun Long.toWires(prefix: String): Map<String, Wire> = toString(2)
@@ -121,7 +175,7 @@ class CrossedWires : AdventOfCode<Long>(2024, 24) {
121175
private fun simulateGates(
122176
wires: Map<String, Wire>,
123177
gates: List<Gate>
124-
) {
178+
): Long {
125179
println("--- simulating gates ---")
126180
val x0 = wires.withPrefixAsDecimal("x")
127181
val y0 = wires.withPrefixAsDecimal("y")
@@ -164,6 +218,8 @@ class CrossedWires : AdventOfCode<Long>(2024, 24) {
164218

165219
val gatesToChange = gates.filter { it.out in wrongWires }
166220
println("gates to change: $gatesToChange")
221+
222+
return sumSimulated
167223
}
168224
}
169225

src/test/kotlin/de/ronny_h/aoc/year24/day24/CrossedWiresTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ class CrossedWiresTest : StringSpec({
104104
}
105105

106106
"part1 small: The decimal number output on the wires starting with z" {
107-
CrossedWires().part1(smallInput) shouldBe 4
107+
CrossedWires().part1(smallInput) shouldBe "4"
108108
}
109109

110110
"part1: The decimal number output on the wires starting with z" {
111-
CrossedWires().part1(input) shouldBe 2024
111+
CrossedWires().part1(input) shouldBe "2024"
112112
}
113113
})

0 commit comments

Comments
 (0)