@@ -4,9 +4,9 @@ import de.ronny_h.aoc.AdventOfCode
44import de.ronny_h.aoc.extensions.toBoolean
55import 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
0 commit comments