Skip to content

Commit 059ed29

Browse files
committed
Merge branch 'feature/day24' into develop
2 parents 5b318b1 + 2df1fde commit 059ed29

File tree

1 file changed

+228
-0
lines changed
  • year2024/src/main/kotlin/net/olegg/aoc/year2024/day24

1 file changed

+228
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package net.olegg.aoc.year2024.day24
2+
3+
import net.olegg.aoc.someday.SomeDay
4+
import net.olegg.aoc.utils.toPair
5+
import net.olegg.aoc.year2024.DayOf2024
6+
import net.olegg.aoc.year2024.day24.Day24.Arg.X
7+
import net.olegg.aoc.year2024.day24.Day24.Arg.Y
8+
import net.olegg.aoc.year2024.day24.Day24.Arg.Z
9+
10+
/**
11+
* See [Year 2024, Day 24](https://adventofcode.com/2024/day/24)
12+
*/
13+
object Day24 : DayOf2024(24) {
14+
private const val DEBUG = false
15+
private val rulePattern = "(\\w+) (OR|XOR|AND) (\\w+) -> (\\w+)".toRegex()
16+
override fun first(): Any? {
17+
val (rawGates, rawRules) = data.split("\n\n")
18+
19+
val inputs = rawGates.lines()
20+
.associate { line ->
21+
val rawPair = line.split(": ").toPair()
22+
Arg.parse(rawPair.first) to (rawPair.second == "1")
23+
}
24+
25+
val rules = rawRules.lines()
26+
.mapNotNull { rulePattern.matchEntire(it) }
27+
.map { match ->
28+
val (a, op, b, res) = match.destructured
29+
Triple(op, Arg.parse(a), Arg.parse(b)) to Arg.parse(res)
30+
}
31+
32+
val nodes = (inputs.keys + rules.flatMap { listOf(it.first.first, it.first.second, it.second) }).toSet()
33+
val finalValues = inputs.toMutableMap()
34+
val zs = nodes.filterIsInstance<Z>().toSortedSet()
35+
var remainingRules = rules
36+
37+
while (zs.any { it !in finalValues }) {
38+
val (applicable, nonApplicable) = remainingRules.partition { (rule, _) ->
39+
rule.second in finalValues && rule.third in finalValues
40+
}
41+
42+
applicable.forEach { (rule, res) ->
43+
val (op, a, b) = rule
44+
finalValues[res] = op.calc(finalValues[a]!!, finalValues[b]!!)
45+
}
46+
47+
remainingRules = nonApplicable
48+
}
49+
50+
return zs
51+
.reversed()
52+
.joinToString("") { if (finalValues[it]!!) "1" else "0" }
53+
.toBigInteger(2)
54+
}
55+
56+
override fun second(): Any? {
57+
val (rawGates, rawRules) = data.split("\n\n")
58+
59+
val inputs = rawGates.lines().map { Arg.parse(it.substringBefore(": ")) }
60+
val xs = inputs.filterIsInstance<X>().toSortedSet()
61+
val ys = inputs.filterIsInstance<Y>().toSortedSet()
62+
63+
val rules = rawRules.lines()
64+
.mapNotNull { rulePattern.matchEntire(it) }
65+
.map { match ->
66+
val (a, op, b, res) = match.destructured
67+
Triple(op, Arg.parse(a), Arg.parse(b)) to Arg.parse(res)
68+
}
69+
70+
val nodes = (inputs + rules.flatMap { listOf(it.first.first, it.first.second, it.second) }).toSet()
71+
72+
val zs = nodes.filterIsInstance<Z>().toSortedSet()
73+
74+
val rawSwaps = listOf(
75+
"z12" to "fgc",
76+
"z29" to "mtj",
77+
"vvm" to "dgr",
78+
"z37" to "dtv",
79+
)
80+
val swaps = buildMap(rawSwaps.size * 2) {
81+
rawSwaps.forEach { (first, second) ->
82+
put(Arg.parse(first), Arg.parse(second))
83+
put(Arg.parse(second), Arg.parse(first))
84+
}
85+
}
86+
87+
val fixedRules = rules.map { (rule, res) ->
88+
rule to (swaps[res] ?: res)
89+
}
90+
91+
if (DEBUG) {
92+
println(
93+
buildString {
94+
appendLine()
95+
appendLine("########################################")
96+
appendLine("digraph G {")
97+
98+
appendLine(" subgraph input_x {")
99+
appendLine(" node [style=filled,color=lightgrey];")
100+
xs.joinTo(this, separator = " -> ", prefix = " ", postfix = ";\n")
101+
appendLine(" }")
102+
103+
appendLine(" subgraph input_y {")
104+
appendLine(" node [style=filled,color=lightgrey];")
105+
ys.joinTo(this, separator = " -> ", prefix = " ", postfix = ";\n")
106+
appendLine(" }")
107+
108+
appendLine(" subgraph gates_and {")
109+
appendLine(" node [style=filled,color=lightgreen];")
110+
fixedRules.filter { it.first.first == "AND" }
111+
.map { it.second }
112+
.joinTo(this, separator = "; ", prefix = " ", postfix = ";\n")
113+
appendLine(" }")
114+
115+
appendLine(" subgraph gates_or {")
116+
appendLine(" node [style=filled,color=yellow];")
117+
fixedRules.filter { it.first.first == "OR" }
118+
.map { it.second }
119+
.joinTo(this, separator = "; ", prefix = " ", postfix = ";\n")
120+
appendLine(" }")
121+
122+
appendLine(" subgraph gates_xor {")
123+
appendLine(" node [style=filled,color=lightskyblue];")
124+
fixedRules.filter { it.first.first == "XOR" }
125+
.map { it.second }
126+
.joinTo(this, separator = "; ", prefix = " ", postfix = ";\n")
127+
appendLine(" }")
128+
129+
appendLine(" subgraph output_z {")
130+
zs.joinTo(this, separator = " -> ", prefix = " ", postfix = ";\n")
131+
appendLine(" }")
132+
133+
fixedRules.joinTo(this, separator = "\n", postfix = "\n") {
134+
" ${it.first.second} -> ${it.second}; ${it.first.third} -> ${it.second};"
135+
}
136+
137+
appendLine("}")
138+
appendLine("########################################")
139+
appendLine()
140+
}
141+
)
142+
}
143+
144+
return rawSwaps.flatMap { it.toList() }.sorted().joinToString(",")
145+
}
146+
147+
sealed interface Arg: Comparable<Arg> {
148+
val original: String
149+
get() = toString()
150+
val mapped: String
151+
get() = toString()
152+
val order: Int
153+
154+
sealed interface Input: Arg {
155+
val num: Int
156+
}
157+
158+
data class X(
159+
override val num: Int,
160+
): Input {
161+
override val order = 0
162+
163+
override fun toString() = "x%02d".format(num)
164+
165+
override fun compareTo(other: Arg): Int = when (other) {
166+
is X -> num - other.num
167+
else -> order - other.order
168+
}
169+
}
170+
171+
data class Y(
172+
override val num: Int,
173+
): Input {
174+
override val order = 1
175+
176+
override fun toString() = "y%02d".format(num)
177+
178+
override fun compareTo(other: Arg): Int = when (other) {
179+
is Y -> num - other.num
180+
else -> order - other.order
181+
}
182+
}
183+
184+
data class Z(
185+
val num: Int,
186+
): Arg {
187+
override val order = 2
188+
189+
override fun toString() = "z%02d".format(num)
190+
191+
override fun compareTo(other: Arg) = when (other) {
192+
is Z -> num - other.num
193+
else -> order - other.order
194+
}
195+
}
196+
197+
data class Unmapped(
198+
val raw: String,
199+
): Arg {
200+
override val order = 100
201+
202+
override fun toString() = raw
203+
204+
override fun compareTo(other: Arg) = when (other) {
205+
is Unmapped -> raw.compareTo(other.raw)
206+
else -> order - other.order
207+
}
208+
}
209+
210+
companion object {
211+
fun parse(raw: String): Arg = when (raw[0]) {
212+
'x' -> raw.drop(1).toIntOrNull()?.let { X(it) }
213+
'y' -> raw.drop(1).toIntOrNull()?.let { Y(it) }
214+
'z' -> raw.drop(1).toIntOrNull()?.let { Z(it) }
215+
else -> Unmapped(raw)
216+
} ?: Unmapped(raw)
217+
}
218+
}
219+
220+
private fun String.calc(a: Boolean, b: Boolean) = when (this) {
221+
"OR" -> a or b
222+
"XOR" -> a xor b
223+
"AND" -> a and b
224+
else -> error("Unknown operation $this")
225+
}
226+
}
227+
228+
fun main() = SomeDay.mainify(Day24)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy