|
| 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 | +} |
0 commit comments