Skip to content

Commit 9b2bf62

Browse files
committed
Day 24
1 parent caee02b commit 9b2bf62

File tree

3 files changed

+273
-0
lines changed

3 files changed

+273
-0
lines changed

src/main/kotlin/common/ext/Map.kt

+7
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ fun <T> MutableMap<T, Long>.mergeAll(other: Map<T, Long>) {
77
fun <Key> MutableMap<Key, Int>.addOrSet(key: Key, toAdd: Int) {
88
this[key] = this.getOrDefault(key, 0) + toAdd
99
}
10+
11+
fun <K, V> MutableMap<K, V>.swap(a: K, b: K) {
12+
val aValue = getValue(a)
13+
val bValue = getValue(b)
14+
this[a] = bValue
15+
this[b] = aValue
16+
}

src/main/kotlin/y24/Day24.kt

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package y24
2+
3+
import common.puzzle.solvePuzzle
4+
import common.puzzle.Input
5+
import common.puzzle.Puzzle
6+
import common.datastructures.*
7+
import common.ext.*
8+
import common.puzzle.splitToInts
9+
import common.util.*
10+
import java.util.*
11+
import kotlin.math.*
12+
import kotlin.system.exitProcess
13+
14+
15+
fun main() = solvePuzzle(year = 2024, day = 24, dryRun = true) { Day24(it) }
16+
17+
private typealias Operator = (Boolean, Boolean) -> Boolean
18+
private val and: Operator = { a, b -> a && b }
19+
private val or: Operator = { a, b -> a || b }
20+
private val xor: Operator = { a, b -> a xor b }
21+
22+
class Day24(val input: Input, val swaps: Int = 4) : Puzzle {
23+
private val nodes = mutableMapOf<String, Node>()
24+
sealed class Node {
25+
class Wire(val value: Boolean) : Node()
26+
class Gate(val a: String, val b: String, val operator: Operator) : Node()
27+
}
28+
29+
fun Node.value(visited: Set<String>): Boolean? {
30+
return when (this) {
31+
is Node.Wire -> value
32+
is Node.Gate -> {
33+
if (a in visited || b in visited) {
34+
null
35+
} else {
36+
val aValue = nodes.getValue(a).value(visited + listOf(a, b)) ?: return null
37+
val bValue = nodes.getValue(b).value(visited + listOf(a, b)) ?: return null
38+
operator(aValue, bValue)
39+
}
40+
}
41+
}
42+
}
43+
44+
init {
45+
var isGate = false
46+
input.lines.forEach { line ->
47+
if (line.isEmpty()) {
48+
isGate = true
49+
return@forEach
50+
}
51+
52+
if (isGate) {
53+
val (a, op, b, _, c) = line.split(" ")
54+
nodes[c] = Node.Gate(
55+
a = a,
56+
b = b,
57+
operator = when (op) {
58+
"AND" -> and
59+
"OR" -> or
60+
"XOR" -> xor
61+
else -> error("unknown operator $op")
62+
}
63+
)
64+
} else {
65+
val (a, value) = line.split(" ")
66+
nodes[a.substringBefore(":")] = Node.Wire(value == "1")
67+
}
68+
}
69+
}
70+
71+
private fun longValue(nodeKeys: List<String>): Long? {
72+
val resultValues = nodeKeys.sorted()
73+
return resultValues.foldRightIndexed(0L) { i, z, acc ->
74+
val b = nodes.getValue(z).value(mutableSetOf()) ?: return null
75+
acc + (b.toInt().toLong() shl i)
76+
}
77+
}
78+
79+
override fun solveLevel1(): Any {
80+
return longValue(nodes.keys.filter { it.startsWith("z") })!!
81+
}
82+
83+
// dgr x
84+
// dtv x
85+
// fgc x
86+
// gdr - (AND of 2 xors)
87+
// gdv - (fine, goes into xor and and)
88+
// jkb - same as above
89+
// mtj x
90+
// vtc: - dgr AND rrd -> vtc (AND before OR, carry out)
91+
// wng(AND) OR gtw(AND) -> rrd
92+
// y33 AND x33 -> dgr x
93+
// vvm x
94+
// wrd - (depends on vtc)
95+
// z12 x
96+
// z29 x
97+
// z37 x
98+
99+
override fun solveLevel2(): Any {
100+
val gates = nodes.filter { it.value is Node.Gate }
101+
.mapValues { it.value as Node.Gate }
102+
103+
fun isXY(a: String) = a.startsWith('x') || a.startsWith('y')
104+
fun isZ(a: String) = a.startsWith('z')
105+
106+
val largestZ = gates.filter { it.key.startsWith('z') }.keys.maxOf { it }
107+
108+
val wrong = mutableSetOf<String>()
109+
gates.forEach { (out, gate) ->
110+
val in1 = gate.a
111+
val in2 = gate.b
112+
val usedInGates = gates.entries.filter { (_, v) -> v.a == out || v.b == out }
113+
when (gate.operator) {
114+
xor -> {
115+
if (!isZ(out)) {
116+
if (!isXY(in1) || !isXY(in2)) {
117+
wrong += out
118+
} else {
119+
// Should go to another xor
120+
if (usedInGates.none { it.value.operator == xor }) {
121+
println("xy xor not going to xor $out")
122+
wrong += out
123+
}
124+
}
125+
} else {
126+
// Output is z
127+
if (isXY(in1) || isXY(in2)) {
128+
// Ignore x00 XOR y00 -> z00
129+
if (out != "z00") {
130+
wrong += out
131+
}
132+
}
133+
}
134+
}
135+
or -> {
136+
if (isZ(out) && out != largestZ) {
137+
wrong += out
138+
return@forEach
139+
}
140+
if (isXY(in1) || isXY(in2)) {
141+
wrong += out
142+
return@forEach
143+
}
144+
val gate1 = nodes[in1] as Node.Gate
145+
val gate2 = nodes[in2] as Node.Gate
146+
if (gate1.operator != and) {
147+
wrong += in1
148+
}
149+
if (gate2.operator != and) {
150+
wrong += in2
151+
}
152+
153+
// Check or goes into AND or XOR
154+
if (usedInGates.any { it.value.operator == or }) {
155+
println("or going into or")
156+
wrong += out
157+
}
158+
}
159+
and -> {
160+
if (isXY(in1) && isXY(in2)) {
161+
if (isZ(out)) {
162+
wrong += out
163+
}
164+
165+
// should go to or
166+
if (usedInGates.none { it.value.operator == or }) {
167+
if (in1 !in listOf("x00", "y00")) {
168+
println("xy and not going to or $out")
169+
wrong += out
170+
}
171+
}
172+
return@forEach
173+
}
174+
if (isXY(in1) || isXY(in2)) {
175+
wrong += out
176+
return@forEach
177+
}
178+
if (isZ(out)) {
179+
wrong += out
180+
return@forEach
181+
}
182+
183+
// Check that out is used in OR gate
184+
if (gates.entries.any { (_, v) ->
185+
(v.a == out || v.b == out) && v.operator != or
186+
}) {
187+
println("and not going into or $out")
188+
wrong += out
189+
return@forEach
190+
}
191+
}
192+
}
193+
}
194+
195+
println(wrong.size)
196+
return wrong.sorted().joinToString(",")
197+
}
198+
}

src/test/kotlin/y24/Day24Test.kt

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package y24
2+
3+
import common.puzzle.Input
4+
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Test
6+
7+
internal class Day24Test {
8+
private val sample = Input("""
9+
x00: 1
10+
x01: 0
11+
x02: 1
12+
x03: 1
13+
x04: 0
14+
y00: 1
15+
y01: 1
16+
y02: 1
17+
y03: 1
18+
y04: 1
19+
20+
ntg XOR fgs -> mjb
21+
y02 OR x01 -> tnw
22+
kwq OR kpj -> z05
23+
x00 OR x03 -> fst
24+
tgd XOR rvg -> z01
25+
vdt OR tnw -> bfw
26+
bfw AND frj -> z10
27+
ffh OR nrd -> bqk
28+
y00 AND y03 -> djm
29+
y03 OR y00 -> psh
30+
bqk OR frj -> z08
31+
tnw OR fst -> frj
32+
gnj AND tgd -> z11
33+
bfw XOR mjb -> z00
34+
x03 OR x00 -> vdt
35+
gnj AND wpb -> z02
36+
x04 AND y00 -> kjc
37+
djm OR pbm -> qhw
38+
nrd AND vdt -> hwm
39+
kjc AND fst -> rvg
40+
y04 OR y02 -> fgs
41+
y01 AND x02 -> pbm
42+
ntg OR kjc -> kwq
43+
psh XOR fgs -> tgd
44+
qhw XOR tgd -> z09
45+
pbm OR djm -> kpj
46+
x03 XOR y03 -> ffh
47+
x00 XOR y04 -> ntg
48+
bfw OR bqk -> z06
49+
nrd XOR fgs -> wpb
50+
frj XOR qhw -> z04
51+
bqk OR frj -> z07
52+
y03 OR x01 -> nrd
53+
hwm AND bqk -> z03
54+
tgd XOR rvg -> z12
55+
tnw OR pbm -> gnj
56+
""".trimIndent())
57+
58+
private val day = Day24(sample)
59+
60+
@Test
61+
fun solveLevel1() {
62+
assertEquals(2024L, day.solveLevel1())
63+
}
64+
65+
@Test
66+
fun solveLevel2() {
67+
}
68+
}

0 commit comments

Comments
 (0)