|
| 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