diff --git a/2024/kotlin/src/main/kotlin/Main.kt b/2024/kotlin/src/main/kotlin/Main.kt index f2cb821..f9dbb16 100644 --- a/2024/kotlin/src/main/kotlin/Main.kt +++ b/2024/kotlin/src/main/kotlin/Main.kt @@ -23,6 +23,7 @@ import com.tymscar.day20.solve as day20 import com.tymscar.day21.solve as day21 import com.tymscar.day22.solve as day22 import com.tymscar.day23.solve as day23 +import com.tymscar.day24.solve as day24 fun main() { day01() @@ -48,4 +49,5 @@ fun main() { day21() day22() day23() + day24() } \ No newline at end of file diff --git a/2024/kotlin/src/main/kotlin/com/tymscar/day24/part1/part1.kt b/2024/kotlin/src/main/kotlin/com/tymscar/day24/part1/part1.kt index 71b80f6..df2375c 100644 --- a/2024/kotlin/src/main/kotlin/com/tymscar/day24/part1/part1.kt +++ b/2024/kotlin/src/main/kotlin/com/tymscar/day24/part1/part1.kt @@ -1,5 +1,45 @@ package com.tymscar.day24.part1 -fun solve(input: String): String { - return input + +private enum class Gate { AND, OR, XOR, INPUT } +private data class Wire(val name: String, val value: Boolean?, val gate: Gate, val input1: String?, val input2: String?) +private typealias Circuit = MutableMap +private fun Circuit.run() = this.keys + .filter { Regex("""z\d+""").matches(it) } + .sortedDescending() + .map { if (getWireValue(this, it)) 1 else 0 } + .joinToString("") + .toLong(2) + .toString() + +private fun getCircuit(input: String): Circuit { + val circuit = Regex("""(\w+): (\d)""").findAll(input).map { + val (name, value) = it.destructured + name to Wire(name, value == "1", Gate.INPUT, null, null) + }.toMap().toMutableMap() + + Regex("""(\w+) (AND|OR|XOR) (\w+) -> (\w+)""").findAll(input).forEach { + val (input1, gate, input2, output) = it.destructured + circuit[output] = Wire(output, null, Gate.valueOf(gate), input1, input2) + } + + return circuit } + +private fun getWireValue(circuit: Circuit, wire: String): Boolean { + val wireValue = circuit[wire]?.value + if (wireValue != null) return wireValue + + val wire = circuit[wire]!! + val value = when (wire.gate) { + Gate.INPUT -> wire.value!! + Gate.AND -> getWireValue(circuit, wire.input1!!) and getWireValue(circuit, wire.input2!!) + Gate.OR -> getWireValue(circuit, wire.input1!!) or getWireValue(circuit, wire.input2!!) + Gate.XOR -> getWireValue(circuit, wire.input1!!) xor getWireValue(circuit, wire.input2!!) + } + + circuit[wire.name] = Wire(wire.name, value, wire.gate, wire.input1, wire.input2) + return value +} + +fun solve(input: String) = getCircuit(input).run() diff --git a/2024/kotlin/src/main/kotlin/com/tymscar/day24/part2/part2.kt b/2024/kotlin/src/main/kotlin/com/tymscar/day24/part2/part2.kt index af52aff..6e4663d 100644 --- a/2024/kotlin/src/main/kotlin/com/tymscar/day24/part2/part2.kt +++ b/2024/kotlin/src/main/kotlin/com/tymscar/day24/part2/part2.kt @@ -1,5 +1,100 @@ package com.tymscar.day24.part2 + +private enum class Gate { AND, OR, XOR, INPUT } +private data class Wire(var name: String, var value: Boolean?, val gate: Gate, val input1: String?, val input2: String?) +private typealias Circuit = MutableList + +private fun Circuit.run() = this + .filter { Regex("""z\d+""").matches(it.name) } + .sortedByDescending { it.name } + .map { if (getWireValue(this, it)) 1 else 0 } + .joinToString("") + .toLong(2) + +private fun getCircuit(input: String): Circuit { + val circuit = Regex("""(\w+): (\d)""").findAll(input).map { + val (name, value) = it.destructured + Wire(name, value == "1", Gate.INPUT, null, null) + }.toMutableList() + + Regex("""(\w+) (AND|OR|XOR) (\w+) -> (\w+)""").findAll(input).forEach { + val (input1, gate, input2, output) = it.destructured + circuit.add(Wire(output, null, Gate.valueOf(gate), input1, input2)) + } + + return circuit +} + +private fun getWireValue(circuit: Circuit, wire: Wire): Boolean { + if (wire.value != null) return wire.value!! + + val input1 = circuit.find { inputWire -> inputWire.name == wire.input1 } + val input2 = circuit.find { inputWire -> inputWire.name == wire.input2 } + val value = when (wire.gate) { + Gate.INPUT -> wire.value + Gate.AND -> getWireValue(circuit, input1!!) and getWireValue(circuit, input2!!) + Gate.OR -> getWireValue(circuit, input1!!) or getWireValue(circuit, input2!!) + Gate.XOR -> getWireValue(circuit, input1!!) xor getWireValue(circuit, input2!!) + } + + wire.value = value + return value!! +} + +private fun findFirstOutputFrom(circuit: Circuit, wire: String): String? { + val parents = circuit.filter { it.input1 == wire || it.input2 == wire } + + val validOutput = parents.find { it.name.first() == 'z' } + if (validOutput == null) return parents.firstNotNullOfOrNull { findFirstOutputFrom(circuit, it.name) } + + val previousOutputNumber = validOutput.name.drop(1).toInt() - 1 + return "z" + previousOutputNumber.toString().padStart(2, '0') +} + +private fun interpretWireAsNumber(start: Char, circuit: Circuit) = circuit + .filter { it.name.first() == start } + .sortedByDescending(Wire::name) + .map { if (it.value!!) '1' else '0' } + .joinToString("") + .toLong(2) + fun solve(input: String): String { - return input + val circuit = getCircuit(input) + val invalidEndWires = circuit.filter { + it.name.first() == 'z' && it.name != "z45" && it.gate != Gate.XOR + } + + val invalidMidWires = circuit.filter { + it.name.first() != 'z' + && it.input1?.first() != 'x' && it.input1?.first() != 'y' + && it.input2?.first() != 'x' && it.input2?.first() != 'y' + && it.gate == Gate.XOR + } + + invalidMidWires.forEach { wire -> + val toSwitch = invalidEndWires.first { it.name == findFirstOutputFrom(circuit, wire.name) } + val temp = wire.name + wire.name = toSwitch.name + toSwitch.name = temp + } + + val xInput = interpretWireAsNumber('x', circuit) + val yInput = interpretWireAsNumber('y', circuit) + + val diffFromActual = xInput + yInput xor circuit.run() + val zeroBits = diffFromActual + .countTrailingZeroBits() + .toString() + .padStart(2, '0') + + val invalidCarryWires = circuit.filter { + it.input1?.endsWith(zeroBits.toString()) == true + && it.input2?.endsWith(zeroBits.toString()) == true + } + + return (invalidEndWires + invalidMidWires + invalidCarryWires) + .map { it.name } + .sorted() + .joinToString(",") }