Day 24. Solution earlier than working algorithm that does not require manual steps.
This commit is contained in:
parent
e00e002cd6
commit
e7d733846f
345
src/aoc2024/Day24.kt
Normal file
345
src/aoc2024/Day24.kt
Normal file
@ -0,0 +1,345 @@
|
||||
package aoc2024
|
||||
|
||||
import println
|
||||
import readInput
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayDeque
|
||||
|
||||
/*
|
||||
--- Day 24: Crossed Wires ---
|
||||
https://adventofcode.com/2024/day/24
|
||||
*/
|
||||
fun main() {
|
||||
|
||||
val inlineTestInput = """
|
||||
x00: 1
|
||||
x01: 1
|
||||
x02: 1
|
||||
y00: 0
|
||||
y01: 1
|
||||
y02: 0
|
||||
|
||||
x00 AND y00 -> z00
|
||||
x01 XOR y01 -> z01
|
||||
x02 OR y02 -> z02
|
||||
"""
|
||||
|
||||
val inlineTestInput2 = """
|
||||
x00: 1
|
||||
x01: 0
|
||||
x02: 1
|
||||
x03: 1
|
||||
x04: 0
|
||||
y00: 1
|
||||
y01: 1
|
||||
y02: 1
|
||||
y03: 1
|
||||
y04: 1
|
||||
|
||||
ntg XOR fgs -> mjb
|
||||
y02 OR x01 -> tnw
|
||||
kwq OR kpj -> z05
|
||||
x00 OR x03 -> fst
|
||||
tgd XOR rvg -> z01
|
||||
vdt OR tnw -> bfw
|
||||
bfw AND frj -> z10
|
||||
ffh OR nrd -> bqk
|
||||
y00 AND y03 -> djm
|
||||
y03 OR y00 -> psh
|
||||
bqk OR frj -> z08
|
||||
tnw OR fst -> frj
|
||||
gnj AND tgd -> z11
|
||||
bfw XOR mjb -> z00
|
||||
x03 OR x00 -> vdt
|
||||
gnj AND wpb -> z02
|
||||
x04 AND y00 -> kjc
|
||||
djm OR pbm -> qhw
|
||||
nrd AND vdt -> hwm
|
||||
kjc AND fst -> rvg
|
||||
y04 OR y02 -> fgs
|
||||
y01 AND x02 -> pbm
|
||||
ntg OR kjc -> kwq
|
||||
psh XOR fgs -> tgd
|
||||
qhw XOR tgd -> z09
|
||||
pbm OR djm -> kpj
|
||||
x03 XOR y03 -> ffh
|
||||
x00 XOR y04 -> ntg
|
||||
bfw OR bqk -> z06
|
||||
nrd XOR fgs -> wpb
|
||||
frj XOR qhw -> z04
|
||||
bqk OR frj -> z07
|
||||
y03 OR x01 -> nrd
|
||||
hwm AND bqk -> z03
|
||||
tgd XOR rvg -> z12
|
||||
tnw OR pbm -> gnj
|
||||
"""
|
||||
|
||||
data class Node(val n1: String, val n2: String, val op: Int, var target: String, var val1: Boolean? = null, var val2: Boolean? = null) {
|
||||
fun output() = when (op) {
|
||||
0 -> val1!! || val2!!
|
||||
1 -> val1!! && val2!!
|
||||
2 -> val1!! != val2!!
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Node
|
||||
|
||||
return target == other.target
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return target.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
val nodes = ArrayList<Node>()
|
||||
val nodesWaiting = HashMap<String, MutableList<Node>>()
|
||||
var satisfiedNodes = ArrayList<Node>()
|
||||
val zTargets = TreeSet<String>()
|
||||
val zState = HashMap<String, Boolean>()
|
||||
val targetParent = HashMap<String, Node>()
|
||||
val initialStates = HashMap<String, Boolean>()
|
||||
val zInvolvedNodes = HashMap<String, MutableSet<Node>>()
|
||||
val random = kotlin.random.Random(1337)
|
||||
|
||||
fun readCircuit(input: List<String>) {
|
||||
nodes.clear()
|
||||
nodesWaiting.clear()
|
||||
satisfiedNodes.clear()
|
||||
zTargets.clear()
|
||||
zState.clear()
|
||||
targetParent.clear()
|
||||
initialStates.clear()
|
||||
var i = 0
|
||||
while (input[i++].isNotEmpty()) {
|
||||
val (n, st) = input[i - 1].split(": ")
|
||||
initialStates[n] = (st == "1")
|
||||
}
|
||||
while (i < input.size) {
|
||||
val (n1, op, n2, _, target) = input[i++].split(" ")
|
||||
val opCode = when (op) {
|
||||
"OR" -> 0
|
||||
"AND" -> 1
|
||||
"XOR" -> 2
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
if (target.startsWith("z")) {
|
||||
zTargets.add(target)
|
||||
}
|
||||
val node = Node(n1, n2, opCode, target)
|
||||
nodes.add(node)
|
||||
targetParent[target] = node
|
||||
}
|
||||
}
|
||||
|
||||
fun outputState(target: String, output: Boolean, newSatisfiedNodes: MutableCollection<Node>) {
|
||||
if (target.startsWith("z")) {
|
||||
zState[target] = output
|
||||
}
|
||||
val targetList = nodesWaiting[target] ?: return
|
||||
while (targetList.isNotEmpty()) {
|
||||
val tNode = targetList.removeFirst()
|
||||
if (tNode.n1 == target) {
|
||||
tNode.val1 = output
|
||||
if (tNode.val2 != null) {
|
||||
newSatisfiedNodes.add(tNode)
|
||||
}
|
||||
} else {
|
||||
tNode.val2 = output
|
||||
if (tNode.val1 != null) {
|
||||
newSatisfiedNodes.add(tNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun runCircuit(): Boolean {
|
||||
while (zState.size != zTargets.size) {
|
||||
if (satisfiedNodes.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
val newSatisfiedNodes = ArrayList<Node>()
|
||||
for (node in satisfiedNodes) {
|
||||
val output = node.output()
|
||||
node.val1 = null
|
||||
node.val2 = null
|
||||
nodesWaiting.getOrPut(node.n1) { ArrayList() }.add(node)
|
||||
nodesWaiting.getOrPut(node.n2) { ArrayList() }.add(node)
|
||||
|
||||
outputState(node.target, output, newSatisfiedNodes)
|
||||
}
|
||||
satisfiedNodes = newSatisfiedNodes
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
satisfiedNodes.clear()
|
||||
nodesWaiting.clear()
|
||||
zState.clear()
|
||||
for (node in nodes) {
|
||||
node.val1 = null
|
||||
node.val2 = null
|
||||
nodesWaiting.getOrPut(node.n1) { ArrayDeque() }.add(node)
|
||||
nodesWaiting.getOrPut(node.n2) { ArrayDeque() }.add(node)
|
||||
}
|
||||
}
|
||||
|
||||
fun setXY(n: String, value: Long) {
|
||||
var v = value
|
||||
for (i in 0..zTargets.size - 2) {
|
||||
outputState("%s%02d".format(n, i), (v and 1L) != 0L, satisfiedNodes)
|
||||
v = v shr 1
|
||||
}
|
||||
}
|
||||
|
||||
fun readZ(): Long {
|
||||
var result = 0L
|
||||
for (i in zTargets.reversed()) {
|
||||
result *= 2
|
||||
if (zState[i] == true) result++
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun calcInvolvedNodes() {
|
||||
zInvolvedNodes.clear()
|
||||
for (target in zTargets) {
|
||||
val set = HashSet<Node>()
|
||||
val queue = ArrayDeque<Node>()
|
||||
queue.add(targetParent[target]!!)
|
||||
while (queue.isNotEmpty()) {
|
||||
val node = queue.removeFirst()
|
||||
set.add(node)
|
||||
val t1 = targetParent[node.n1]
|
||||
val t2 = targetParent[node.n2]
|
||||
if (t1 != null && !set.contains(t1)) queue.add(t1)
|
||||
if (t2 != null && !set.contains(t2)) queue.add(t2)
|
||||
}
|
||||
zInvolvedNodes[target] = set
|
||||
}
|
||||
}
|
||||
|
||||
fun swapOutputs(t1: String, t2: String) {
|
||||
val n1 = targetParent[t1]!!
|
||||
val n2 = targetParent[t2]!!
|
||||
n1.target = t2
|
||||
n2.target = t1
|
||||
targetParent[t1] = n2
|
||||
targetParent[t2] = n1
|
||||
}
|
||||
|
||||
fun part1(input: List<String>): Long {
|
||||
readCircuit(input)
|
||||
reset()
|
||||
initialStates.forEach { outputState(it.key, it.value, satisfiedNodes) }
|
||||
runCircuit()
|
||||
|
||||
return readZ()
|
||||
}
|
||||
|
||||
fun checkIt(x: Long, y: Long, sum: Long, mask: Long = -1L): Boolean {
|
||||
reset()
|
||||
setXY("x", x)
|
||||
setXY("y", y)
|
||||
return runCircuit() && (readZ() and mask) == sum
|
||||
}
|
||||
|
||||
fun countGoodBits(startbit: Int): Int {
|
||||
val inv = (1L shl (zTargets.size - 1)) - 1
|
||||
for (b in startbit..zTargets.size - 2) {
|
||||
val tv = 1L shl b
|
||||
val tvi = (1L shl (b + 1)) - 1L
|
||||
if (!(checkIt(tv, 0L, tv) &&
|
||||
checkIt(tvi, 0L, tvi) &&
|
||||
checkIt(0L, tv, tv) &&
|
||||
checkIt(0L, tvi, tvi) &&
|
||||
checkIt(tv, tv, 2 * tv) &&
|
||||
checkIt(tvi, tvi, 2 * tvi) &&
|
||||
checkIt(inv - tvi, 0L, 0L, tvi) &&
|
||||
checkIt(0L, inv - tvi, 0L, tvi)
|
||||
)
|
||||
) {
|
||||
return b
|
||||
}
|
||||
for (i in 0..(1L shl ((b - 8).coerceIn(1..6)))) {
|
||||
val rx = random.nextLong() and tvi
|
||||
val ry = random.nextLong() and tvi
|
||||
if (!checkIt(rx, ry, rx + ry, mask = tvi * 2 + 1)) return b
|
||||
}
|
||||
}
|
||||
return zTargets.size
|
||||
}
|
||||
|
||||
fun part2(input: List<String>): String {
|
||||
readCircuit(input)
|
||||
calcInvolvedNodes()
|
||||
|
||||
val goodGates = HashSet<Node>()
|
||||
|
||||
val swaps = ArrayList<Pair<String, String>>()
|
||||
|
||||
for (b in 0..zTargets.size - 3) {
|
||||
val zb0 = zInvolvedNodes["z%02d".format(b)]!!
|
||||
val zb1 = zInvolvedNodes["z%02d".format(b + 1)]!!
|
||||
val zb2 = zInvolvedNodes["z%02d".format(b + 2)]!!
|
||||
|
||||
val tv = 1L shl b
|
||||
val tvi = (1L shl (b + 1)) - 1L
|
||||
val good = checkIt(tv, 0L, tv) &&
|
||||
checkIt(tvi, 0L, tvi) &&
|
||||
checkIt(0L, tv, tv) &&
|
||||
checkIt(0L, tvi, tvi) &&
|
||||
checkIt(tv, tv, 2 * tv) &&
|
||||
checkIt(tvi, tvi, 2 * tvi)
|
||||
if (good) {
|
||||
goodGates.addAll(zb0)
|
||||
//goodGates.addAll(zb1)
|
||||
} else {
|
||||
println("Bit error at $b")
|
||||
val badList = zb0.union(zb1).union(zb2).minus(goodGates).map { it.target }.toList()
|
||||
var swapped = false
|
||||
out@ for (p1 in badList.indices) {
|
||||
for (p2 in p1 + 1..badList.lastIndex) {
|
||||
swapOutputs(badList[p1], badList[p2])
|
||||
val goodBits = countGoodBits(b)
|
||||
if (goodBits > b + 1) {
|
||||
println("**** Successfully $goodBits swapped ${badList[p1]} with ${badList[p2]}")
|
||||
swapped = true
|
||||
swaps.add(badList[p1] to badList[p2])
|
||||
break@out
|
||||
} else {
|
||||
swapOutputs(badList[p1], badList[p2])
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!swapped) throw IllegalStateException("Sob!")
|
||||
calcInvolvedNodes()
|
||||
}
|
||||
}
|
||||
return swaps.flatMap { listOf(it.first, it.second) }.sorted().joinToString(",")
|
||||
}
|
||||
|
||||
// test if implementation meets criteria from the description, like:
|
||||
val testInput = inlineTestInput.trim().reader().readLines()
|
||||
val testInput2 = inlineTestInput2.trim().reader().readLines()
|
||||
//val testInput = readInput("aoc2024/Day24_test")
|
||||
val testInputPart1Result = part1(testInput)
|
||||
println("Part 1 Test: $testInputPart1Result")
|
||||
val testInputPart1Result2 = part1(testInput2)
|
||||
println("Part 1 Test 2: $testInputPart1Result2")
|
||||
// val testInputPart2Result = part2(testInput2)
|
||||
// println("Part 2 Test: $testInputPart2Result")
|
||||
check(testInputPart1Result == 4L)
|
||||
check(testInputPart1Result2 == 2024L)
|
||||
//check(testInputPart2Result == "z00,z01,z02,z05")
|
||||
|
||||
val input = readInput("aoc2024/Day24")
|
||||
part1(input).println()
|
||||
part2(input).println()
|
||||
}
|
Loading…
Reference in New Issue
Block a user