Skip to content

Commit 77dfee9

Browse files
committed
Solution Day 24, part one (Crossed Wires)
1 parent b3ed1ba commit 77dfee9

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package de.ronny_h.aoc.year24.day24
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
5+
fun main() = CrossedWires().run(66055249060558, 0)
6+
7+
class CrossedWires: AdventOfCode<Long>(2024, 24) {
8+
9+
companion object {
10+
val and = { a: Boolean, b: Boolean -> a && b }
11+
val or = { a: Boolean, b: Boolean -> a || b }
12+
val xor = { a: Boolean, b: Boolean -> a xor b }
13+
}
14+
15+
fun parseWires(input: List<String>) = input
16+
.takeWhile { it.isNotEmpty() }
17+
.map {
18+
val (name, value) = it.split(": ")
19+
Wire(name, value == "1")
20+
}
21+
22+
fun parseGates(input: List<String>) = input
23+
.dropWhile { it.isNotEmpty() }
24+
.drop(1)
25+
.map {
26+
val (term, result) = it.split(" -> ")
27+
val (in1, op, in2) = term.split(" ")
28+
val operation = when(op) {
29+
"AND" -> and
30+
"OR" -> or
31+
"XOR" -> xor
32+
else -> error("Operation not supported: $op")
33+
}
34+
Gate(in1.trim(), in2.trim(), operation, result.trim())
35+
}
36+
37+
override fun part1(input: List<String>): Long {
38+
val wires = parseWires(input)
39+
.associateBy(Wire::name)
40+
.toMutableMap()
41+
val gates = parseGates(input)
42+
val zWiresToCalculate = gates.count { it.out.startsWith("z") }
43+
44+
do {
45+
val simulatableGates = gates.filter { it.in1 in wires && it.in2 in wires }
46+
val outWires = simulatableGates
47+
.map { it.simulateWith(wires) }
48+
.associateBy(Wire::name)
49+
wires.putAll(outWires)
50+
} while (zWiresToCalculate != wires.count { it.key.startsWith("z") })
51+
52+
return wires.values
53+
.filter { it.name.startsWith("z") }
54+
.sortedByDescending(Wire::name)
55+
.joinToString("") {
56+
when (it.value) {
57+
true -> "1"
58+
false -> "0"
59+
}
60+
}
61+
.toLong(2)
62+
}
63+
64+
override fun part2(input: List<String>): Long {
65+
TODO("Not yet implemented")
66+
}
67+
}
68+
69+
data class Wire(val name: String, val value: Boolean)
70+
data class Gate(val in1: String, val in2: String, val operation: (Boolean, Boolean) -> (Boolean), val out: String) {
71+
fun simulateWith(inWires: Map<String, Wire>): Wire = Wire(
72+
out,
73+
operation(inWires.valueOf(in1), inWires.valueOf(in2))
74+
)
75+
}
76+
77+
private fun Map<String, Wire>.valueOf(inWire: String): Boolean = getValue(inWire).value
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package de.ronny_h.aoc.year24.day24
2+
3+
import de.ronny_h.aoc.extensions.asList
4+
import de.ronny_h.aoc.year24.day24.CrossedWires.Companion.and
5+
import de.ronny_h.aoc.year24.day24.CrossedWires.Companion.or
6+
import de.ronny_h.aoc.year24.day24.CrossedWires.Companion.xor
7+
import io.kotest.core.spec.style.StringSpec
8+
import io.kotest.data.forAll
9+
import io.kotest.data.row
10+
import io.kotest.matchers.shouldBe
11+
12+
class CrossedWiresTest : StringSpec({
13+
14+
val verySmallInput = """
15+
y01: 1
16+
y02: 0
17+
18+
x00 AND y00 -> z00
19+
x01 XOR y01 -> z01
20+
""".asList()
21+
val smallInput = """
22+
x00: 1
23+
x01: 1
24+
x02: 1
25+
y00: 0
26+
y01: 1
27+
y02: 0
28+
29+
x00 AND y00 -> z00
30+
x01 XOR y01 -> z01
31+
x02 OR y02 -> z02
32+
""".asList()
33+
val input = """
34+
x00: 1
35+
x01: 0
36+
x02: 1
37+
x03: 1
38+
x04: 0
39+
y00: 1
40+
y01: 1
41+
y02: 1
42+
y03: 1
43+
y04: 1
44+
45+
ntg XOR fgs -> mjb
46+
y02 OR x01 -> tnw
47+
kwq OR kpj -> z05
48+
x00 OR x03 -> fst
49+
tgd XOR rvg -> z01
50+
vdt OR tnw -> bfw
51+
bfw AND frj -> z10
52+
ffh OR nrd -> bqk
53+
y00 AND y03 -> djm
54+
y03 OR y00 -> psh
55+
bqk OR frj -> z08
56+
tnw OR fst -> frj
57+
gnj AND tgd -> z11
58+
bfw XOR mjb -> z00
59+
x03 OR x00 -> vdt
60+
gnj AND wpb -> z02
61+
x04 AND y00 -> kjc
62+
djm OR pbm -> qhw
63+
nrd AND vdt -> hwm
64+
kjc AND fst -> rvg
65+
y04 OR y02 -> fgs
66+
y01 AND x02 -> pbm
67+
ntg OR kjc -> kwq
68+
psh XOR fgs -> tgd
69+
qhw XOR tgd -> z09
70+
pbm OR djm -> kpj
71+
x03 XOR y03 -> ffh
72+
x00 XOR y04 -> ntg
73+
bfw OR bqk -> z06
74+
nrd XOR fgs -> wpb
75+
frj XOR qhw -> z04
76+
bqk OR frj -> z07
77+
y03 OR x01 -> nrd
78+
hwm AND bqk -> z03
79+
tgd XOR rvg -> z12
80+
tnw OR pbm -> gnj
81+
""".asList()
82+
83+
"wires can be parsed" {
84+
CrossedWires().parseWires(verySmallInput) shouldBe listOf(Wire("y01", true), Wire("y02", false))
85+
}
86+
87+
"gates can be parsed" {
88+
CrossedWires().parseGates(verySmallInput) shouldBe listOf(
89+
Gate("x00", "y00", and, "z00"),
90+
Gate("x01", "y01", xor, "z01"),
91+
)
92+
}
93+
94+
"single gates can be simulated" {
95+
val inWires = mapOf("x00" to Wire("x00", true), "y00" to Wire("y00", false))
96+
97+
forAll(
98+
row(and, false),
99+
row(or, true),
100+
row(xor, true),
101+
) { operation, result ->
102+
Gate("x00", "y00", operation, "z00").simulateWith(inWires) shouldBe Wire("z00", result)
103+
}
104+
}
105+
106+
"part1 small: The decimal number output on the wires starting with z" {
107+
CrossedWires().part1(smallInput) shouldBe 4
108+
}
109+
110+
"part1: The decimal number output on the wires starting with z" {
111+
CrossedWires().part1(input) shouldBe 2024
112+
}
113+
})

0 commit comments

Comments
 (0)