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