From 56e0717b2a2ccd6bf721b2833959f99d4abac245 Mon Sep 17 00:00:00 2001 From: Brian Peck Date: Tue, 24 Dec 2024 08:59:15 -0800 Subject: [PATCH 1/4] manually solved --- .../peckb/aoc/_2024/calendar/day24/Day24.kt | 266 ++++++++++++++++++ .../peckb/aoc/_2024/calendar/day24/README.md | 1 + .../me/peckb/aoc/_2024/TestDayComponent.kt | 2 + .../aoc/_2024/calendar/day24/Day24Test.kt | 32 +++ 4 files changed, 301 insertions(+) create mode 100644 src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt create mode 100644 src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md create mode 100644 src/test/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24Test.kt diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt new file mode 100644 index 00000000..044ec0f2 --- /dev/null +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt @@ -0,0 +1,266 @@ +package me.peckb.aoc._2024.calendar.day24 + +import me.peckb.aoc.generators.CombinationsGenerator +import javax.inject.Inject +import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory +import java.util.LinkedList + +class Day24 @Inject constructor( + private val generatorFactory: InputGeneratorFactory, +) { + fun partOne(filename: String) = generatorFactory.forFile(filename).read { input -> + var readingBase = true + + val data = mutableMapOf>() + val zValues = mutableListOf() + + input.forEach input@{ line -> + if (line.isEmpty()) { + readingBase = false + return@input + } + if (readingBase) { + line.split(": ").let { (key, value) -> + data[key] = lazy { value.toInt() } + } + } else { + line.split(" ").let { (first, operator, second, _, result) -> + if (result.startsWith('z')) { + zValues.add(result) + } + val op = when (operator) { + "AND" -> Int::and + "OR" -> Int::or + "XOR" -> Int::xor + else -> throw IllegalArgumentException("Unknown operation $operator") + } + data[result] = lazy { op(data[first]!!.value, data[second]!!.value) } + } + } + } + + val zResults = zValues.sorted().map { data[it]!!.value }.reversed() + + zResults.joinToString("").toLong(2) + } + + fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input -> + var readingBase = true + val data = mutableMapOf>() + val xValues = mutableListOf() + val yValues = mutableListOf() + val zValues = mutableListOf() + val wirings = mutableMapOf() + + val swaps = mapOf( + "kth" to "z12", "carryChain13" to "z12", + "z12" to "kth", "z12" to "carryChain13", + "gsd" to "z26", "carry26" to "z26", + "z26" to "gsd", "z26" to "carry26", + "tbt" to "z32", "carryAfter32" to "z32", + "z32" to "tbt", "z32" to "carryAfter32", + "qnf" to "vpm", + "vpm" to "qnf", + ) + + input.forEach input@{ line -> + if (line.isEmpty()) { + readingBase = false + return@input + } + if (readingBase) { + line.split(": ").let { (key, value) -> + if (key.startsWith('x')) { xValues.add(key) } + if (key.startsWith('y')) { yValues.add(key) } + data[key] = lazy { value.toInt() } + } + } else { + line.split(" ").let { (a, operation, b, _, _result) -> + val result = swaps[_result] ?: _result + + if (result.startsWith('z')) { + zValues.add(result) + } + val op = when (operation) { + "AND" -> Int::and + "OR" -> Int::or + "XOR" -> Int::xor + else -> throw IllegalArgumentException("Unknown operation $operation") + } + wirings[result] = Wiring(a, operation, b, result) + data[result] = lazy { + op(data[a]!!.value, data[b]!!.value) + } + } + } + } + + val binaryX = xValues.sorted().map { data[it]!!.value }.reversed().joinToString("") + val binaryY = yValues.sorted().map { data[it]!!.value }.reversed().joinToString("") + + val initialBinaryZ = zValues.sorted().map { data[it]!!.value }.reversed().joinToString("") + // 1100101000010010000000110000000111000011101000 + // 1100100111001101111100110000001000000011101000 + // ^ + val expectedBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) + + val sources = mutableMapOf>() + (33 .. 45).forEach loop@ { n -> + val key = "z${n.toString().padStart(2, '0')}" + + val mySources = mutableSetOf() + val toCheck = LinkedList() + toCheck.add(key) + while(toCheck.isNotEmpty()) { + val (a, op, b, r) = wirings[toCheck.poll()]!! + if (r.length == 3) { + println("$a $op $b = $r") + } + + if (a.startsWith('x') || a.startsWith('y')) { mySources.add(a) } else { toCheck.add(a) } + if (b.startsWith('x') || b.startsWith('y')) { mySources.add(b) } else { toCheck.add(b) } + } + println() + if (expectedBinaryZ[45 - n].digitToInt() != data[key]!!.value) { + return@read -1 + } + sources[key] = mySources.toList().sorted() + } + +// val z00Sources = mutableListOf() +// val toCheck = LinkedList() +// toCheck.add("z00") +// while(toCheck.isNotEmpty()) { +// val (a, b, _) = wirings[toCheck.poll()]!! +// if (a.startsWith('x') || a.startsWith('y')) { +// z00Sources.add(a) +// } else { +// toCheck.add(a) +// } +// if (b.startsWith('x') || b.startsWith('y')) { +// z00Sources.add(b) +// } else { +// toCheck.add(b) +// } +// } + listOf("kth", "z12", "gsd", "z26", "tbt", "z32", "qnf", "vpm").sorted().joinToString(",") + } + +// fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input -> +// var readingBase = true +// +// val data = mutableMapOf Int>() +// val zValues = mutableListOf() +// val xValues = mutableListOf() +// val yValues = mutableListOf() +// val outputValues = mutableListOf() +// +// val swaps = mutableMapOf() +// +// input.forEach input@{ line -> +// if (line.isEmpty()) { +// readingBase = false +// return@input +// } +// if (readingBase) { +// line.split(": ").let { (key, value) -> +// if (key.startsWith('x')) { xValues.add(key) } +// if (key.startsWith('y')) { yValues.add(key) } +// data[key] = { value.toInt() } +// } +// } else { +// line.split(" ").let { (first, operator, second, _, result) -> +// outputValues.add(result) +// if (result.startsWith('z')) { +// zValues.add(result) +// } +// val op = when (operator) { +// "AND" -> Int::and +// "OR" -> Int::or +// "XOR" -> Int::xor +// else -> throw IllegalArgumentException("Unknown operation $operator") +// } +// data[result] = { +// val a = swaps[first] ?: first +// val b = swaps[second] ?: second +// +// op(data[a]!!(), data[b]!!()) +// } +// } +// } +// } +// +// val binaryX = xValues.sorted().map { data[it]!!() }.reversed().joinToString("") +// val binaryY = yValues.sorted().map { data[it]!!() }.reversed().joinToString("") +// +// val initialBinaryZ = zValues.sorted().map { data[it]!!() }.reversed().joinToString("") +// val expectedBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) +// +// val diff = expectedBinaryZ.zip(initialBinaryZ).map { (a, b) -> +// if (a == b) 0 else 1 +// }.joinToString("").padStart(expectedBinaryZ.length, '0') +// +// val singleSwapDiffs = CombinationsGenerator.findCombinations(outputValues.toTypedArray(), 2).mapIndexedNotNull { i, s -> +// println(i) +// val (a, b) = s +// swaps.clear() +// swaps[a] = b +// swaps[b] = a +// +// var use = false +// try { +// val newZ = zValues.sorted().map { data[it]!!() }.reversed().joinToString("") +// val myDiff = initialBinaryZ.zip(newZ).map { (a, b) -> +// if (a == b) 0 else 1.also { use = true } +// }.joinToString("").padStart(initialBinaryZ.length, '0') +// +// if (use) { +//// println("change for $a and $b") +// (a to b) to myDiff +// } else { +//// println("No change for $a and $b") +// null +// } +// } catch (e: StackOverflowError) { +//// println("Don't swap $a and $b") +// null +// } +// } +// +// -1 +// // 1100100111001101111100110000001000000011101000 +// // 1100101000010010000000110000000111000011101000 +// // 0000001111011111111100000000001111000000000000 +// +// /* +// +// +// z12 z13 z14 z15 z26 z27 z28 z29 z30 z31 z32 z33 z34 z36 z37 z38 z39 +// psw OR nng mtp XOR kth rpt XOR hbh ckk XOR sqb x26 AND y26 swt XOR cmf ghk XOR pgc fht XOR vbp nvq XOR gtd trd XOR dtj vtg AND bkh nwm XOR rpb ksf XOR bhh htb XOR qnf hpp XOR wkk phr XOR ths hhd XOR qnv +// x12 AND y12 nhb AND cdq mtp AND kth nhb XOR cdq cns OR qrs y14 XOR x14 x15 XOR y15 ctc OR skj y27 XOR x27 gsd OR kbg tbc OR nvm y28 XOR x28 y29 XOR x29 crb OR qhw drg OR wbb x30 XOR y30 x31 XOR y31 rkn OR mck nhq OR bjf x32 XOR y32 tbt OR skt x33 XOR y33 kmb OR jtq x34 XOR y34 bbb OR cnp y36 AND x36 x37 XOR y37 thg OR vpm x38 XOR y38 ggw OR gpv bdg OR kmg x39 XOR y39 +// y12 XOR x12 fkw OR mdq +// tnr AND kdw x11 AND y11 +// mjb OR knt y11 XOR x11 +// y10 AND x10 kqs AND fnm +// rjj OR qsm y10 XOR x10 +// hsq AND bpc x09 AND y09 +// x09 XOR y09 rbb OR gth +// ndh AND whc y08 AND x08 +// y08 XOR x08 hnm OR vqk +// mwg AND wnd x07 AND y07 +// mjs OR dbc x07 XOR y07 +// jrg AND rjb x06 AND y06 +// y06 XOR x06 vth OR jvv +// x05 AND y05 str AND gtt +// hwt OR mfj x05 XOR y05 +// y04 AND x04 vwf AND dft +// y04 XOR x04 nbj OR rnw +// twj AND jfr y03 AND x03 +// fph OR nkm x03 XOR y03 +// y02 AND x02 rhr AND rsk +// cbq OR gwd x02 XOR y02 +// */ +// } +} + +data class Wiring(val a: String, val op: String, val b: String, val result: String) \ No newline at end of file diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md new file mode 100644 index 00000000..80c9053d --- /dev/null +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md @@ -0,0 +1 @@ +## [](https://adventofcode.com/2024/day/24) \ No newline at end of file diff --git a/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt b/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt index ec833387..96f411e8 100644 --- a/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt +++ b/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt @@ -23,6 +23,7 @@ import me.peckb.aoc._2024.calendar.day20.Day20Test import me.peckb.aoc._2024.calendar.day21.Day21Test import me.peckb.aoc._2024.calendar.day22.Day22Test import me.peckb.aoc._2024.calendar.day23.Day23Test +import me.peckb.aoc._2024.calendar.day24.Day24Test import javax.inject.Singleton import me.peckb.aoc.DayComponent import me.peckb.aoc.InputModule @@ -54,4 +55,5 @@ internal interface TestDayComponent : DayComponent { fun inject(day21Test: Day21Test) fun inject(day22Test: Day22Test) fun inject(day23Test: Day23Test) + fun inject(day24Test: Day24Test) } diff --git a/src/test/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24Test.kt b/src/test/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24Test.kt new file mode 100644 index 00000000..da37a82e --- /dev/null +++ b/src/test/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24Test.kt @@ -0,0 +1,32 @@ +package me.peckb.aoc._2024.calendar.day24 + +import javax.inject.Inject + +import me.peckb.aoc._2024.DaggerTestDayComponent +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class Day24Test { + @Inject + lateinit var day24: Day24 + + @BeforeEach + fun setup() { + DaggerTestDayComponent.create().inject(this) + } + + @Test + fun testDay24PartOne() { + assertEquals(55544677167336, day24.partOne(DAY_24)) + } + + @Test + fun testDay24PartTwo() { + assertEquals("gsd,kth,qnf,tbt,vpm,z12,z26,z32", day24.partTwo(DAY_24)) + } + + companion object { + private const val DAY_24: String = "advent-of-code-input/2024/day24.input" + } +} From a05b6767376d2a4606118f49f291df2ad17e6d73 Mon Sep 17 00:00:00 2001 From: Brian Peck Date: Tue, 24 Dec 2024 15:29:27 -0800 Subject: [PATCH 2/4] Initial programatic solution --- .../peckb/aoc/_2024/calendar/day24/Day24.kt | 408 ++++++++++-------- 1 file changed, 237 insertions(+), 171 deletions(-) diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt index 044ec0f2..55d44860 100644 --- a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt @@ -1,6 +1,5 @@ package me.peckb.aoc._2024.calendar.day24 -import me.peckb.aoc.generators.CombinationsGenerator import javax.inject.Inject import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory import java.util.LinkedList @@ -46,21 +45,21 @@ class Day24 @Inject constructor( fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input -> var readingBase = true - val data = mutableMapOf>() + val data = mutableMapOf Int>() val xValues = mutableListOf() val yValues = mutableListOf() val zValues = mutableListOf() val wirings = mutableMapOf() - val swaps = mapOf( - "kth" to "z12", "carryChain13" to "z12", - "z12" to "kth", "z12" to "carryChain13", - "gsd" to "z26", "carry26" to "z26", - "z26" to "gsd", "z26" to "carry26", - "tbt" to "z32", "carryAfter32" to "z32", - "z32" to "tbt", "z32" to "carryAfter32", - "qnf" to "vpm", - "vpm" to "qnf", + val swaps = mutableMapOf( +// /*"kth" to "z12",*/ "carryChain13" to "z12", +// /*"z12" to "kth",*/ "z12" to "carryChain13", +// /*"gsd" to "z26",*/ "carry26" to "z26", +// /*"z26" to "gsd",*/ "z26" to "carry26", +// /*"tbt" to "z32",*/ "carryAfter32" to "z32", +// /*"z32" to "tbt",*/ "z32" to "carryAfter32", +// "qnf" to "vpm", +// "vpm" to "qnf", ) input.forEach input@{ line -> @@ -72,11 +71,12 @@ class Day24 @Inject constructor( line.split(": ").let { (key, value) -> if (key.startsWith('x')) { xValues.add(key) } if (key.startsWith('y')) { yValues.add(key) } - data[key] = lazy { value.toInt() } + data[key] = { value.toInt() } } } else { - line.split(" ").let { (a, operation, b, _, _result) -> - val result = swaps[_result] ?: _result + line.split(" ").let { (a, operation, b, _, possibleResult) -> + + val result = swaps[possibleResult] ?: possibleResult if (result.startsWith('z')) { zValues.add(result) @@ -88,179 +88,245 @@ class Day24 @Inject constructor( else -> throw IllegalArgumentException("Unknown operation $operation") } wirings[result] = Wiring(a, operation, b, result) - data[result] = lazy { - op(data[a]!!.value, data[b]!!.value) + data[result] = { + op(data[a]!!(), data[b]!!()) } } } } - val binaryX = xValues.sorted().map { data[it]!!.value }.reversed().joinToString("") - val binaryY = yValues.sorted().map { data[it]!!.value }.reversed().joinToString("") + val binaryX = xValues.sortedDescending().map { data[it]!!() }.joinToString("") + val binaryY = yValues.sortedDescending().map { data[it]!!() }.joinToString("") - val initialBinaryZ = zValues.sorted().map { data[it]!!.value }.reversed().joinToString("") - // 1100101000010010000000110000000111000011101000 - // 1100100111001101111100110000001000000011101000 - // ^ - val expectedBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) + val goodBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) +// val result222 = zValues.sortedDescending().map { data[it]!!() }.joinToString("") +// val goodBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) - val sources = mutableMapOf>() - (33 .. 45).forEach loop@ { n -> - val key = "z${n.toString().padStart(2, '0')}" + fun swap(swap: Pair) { + val oldA = wirings[swap.first]!! + val oldB = wirings[swap.second]!! + + val newB = oldB.copy(result = oldA.result) + val newA = oldA.copy(result = oldB.result) + + wirings[swap.first] = newB + wirings[swap.second] = newA + + val opA = findOperation(newA.op) + val opB = findOperation(newB.op) + + data[newA.result] = { opA(data[newA.a]!!(), data[newA.b]!!()) } + data[newB.result] = { opB(data[newB.a]!!(), data[newB.b]!!()) } - val mySources = mutableSetOf() + swaps[swap.first] = swap.second + swaps[swap.second] = swap.first + } + + val correctWirings = mutableMapOf>() + + fun populateCorrectWirings(result: String) { + val myWirings = mutableListOf() val toCheck = LinkedList() - toCheck.add(key) + toCheck.add(result) while(toCheck.isNotEmpty()) { - val (a, op, b, r) = wirings[toCheck.poll()]!! - if (r.length == 3) { - println("$a $op $b = $r") - } + val wiring = wirings[toCheck.poll()]!! + val (a, op, b, r) = wiring - if (a.startsWith('x') || a.startsWith('y')) { mySources.add(a) } else { toCheck.add(a) } - if (b.startsWith('x') || b.startsWith('y')) { mySources.add(b) } else { toCheck.add(b) } + myWirings.add(wiring) + + val aIsInput = a.startsWith('x') || a.startsWith('y') + val bIsInput = b.startsWith('x') || b.startsWith('y') + + if (!aIsInput) { toCheck.add(a) } + if (!bIsInput) { toCheck.add(b) } } - println() - if (expectedBinaryZ[45 - n].digitToInt() != data[key]!!.value) { - return@read -1 + + correctWirings[result] = myWirings + } + + (0 .. 45).forEach loop@{ n -> + val previousPreviousKey = "z${(n-2).toString().padStart(2, '0')}" + val previousKey = "z${(n-1).toString().padStart(2, '0')}" + val key = "z${n.toString().padStart(2, '0')}" + when (n) { + 0 -> checkZero(wirings)?.also { swap(it) } + 1 -> checkOne(wirings)?.also { swap(it) } + in (2..44) -> checkDefault( + wirings = wirings, + key = n to key, + previousWirings = n - 1 to previousKey, + previousPreviousWirings = n - 2 to previousPreviousKey, + correctWirings = correctWirings + )?.also { swap(it) } + // don't need to check the last - the error won't be there. } - sources[key] = mySources.toList().sorted() + populateCorrectWirings(key) + } + if (swaps.size != 8) { + throw IllegalStateException("Did not find all the swaps") } -// val z00Sources = mutableListOf() -// val toCheck = LinkedList() -// toCheck.add("z00") -// while(toCheck.isNotEmpty()) { -// val (a, b, _) = wirings[toCheck.poll()]!! -// if (a.startsWith('x') || a.startsWith('y')) { -// z00Sources.add(a) -// } else { -// toCheck.add(a) -// } -// if (b.startsWith('x') || b.startsWith('y')) { -// z00Sources.add(b) -// } else { -// toCheck.add(b) -// } -// } - listOf("kth", "z12", "gsd", "z26", "tbt", "z32", "qnf", "vpm").sorted().joinToString(",") + val result = zValues.sortedDescending().map { data[it]!!() }.joinToString("") + if (result != goodBinaryZ) throw IllegalStateException("We found eight swaps, but didn't get the right result") + + swaps.keys.sorted().joinToString(",") } -// fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input -> -// var readingBase = true -// -// val data = mutableMapOf Int>() -// val zValues = mutableListOf() -// val xValues = mutableListOf() -// val yValues = mutableListOf() -// val outputValues = mutableListOf() -// -// val swaps = mutableMapOf() -// -// input.forEach input@{ line -> -// if (line.isEmpty()) { -// readingBase = false -// return@input -// } -// if (readingBase) { -// line.split(": ").let { (key, value) -> -// if (key.startsWith('x')) { xValues.add(key) } -// if (key.startsWith('y')) { yValues.add(key) } -// data[key] = { value.toInt() } -// } -// } else { -// line.split(" ").let { (first, operator, second, _, result) -> -// outputValues.add(result) -// if (result.startsWith('z')) { -// zValues.add(result) -// } -// val op = when (operator) { -// "AND" -> Int::and -// "OR" -> Int::or -// "XOR" -> Int::xor -// else -> throw IllegalArgumentException("Unknown operation $operator") -// } -// data[result] = { -// val a = swaps[first] ?: first -// val b = swaps[second] ?: second -// -// op(data[a]!!(), data[b]!!()) -// } -// } -// } -// } -// -// val binaryX = xValues.sorted().map { data[it]!!() }.reversed().joinToString("") -// val binaryY = yValues.sorted().map { data[it]!!() }.reversed().joinToString("") -// -// val initialBinaryZ = zValues.sorted().map { data[it]!!() }.reversed().joinToString("") -// val expectedBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) -// -// val diff = expectedBinaryZ.zip(initialBinaryZ).map { (a, b) -> -// if (a == b) 0 else 1 -// }.joinToString("").padStart(expectedBinaryZ.length, '0') -// -// val singleSwapDiffs = CombinationsGenerator.findCombinations(outputValues.toTypedArray(), 2).mapIndexedNotNull { i, s -> -// println(i) -// val (a, b) = s -// swaps.clear() -// swaps[a] = b -// swaps[b] = a -// -// var use = false -// try { -// val newZ = zValues.sorted().map { data[it]!!() }.reversed().joinToString("") -// val myDiff = initialBinaryZ.zip(newZ).map { (a, b) -> -// if (a == b) 0 else 1.also { use = true } -// }.joinToString("").padStart(initialBinaryZ.length, '0') -// -// if (use) { -//// println("change for $a and $b") -// (a to b) to myDiff -// } else { -//// println("No change for $a and $b") -// null -// } -// } catch (e: StackOverflowError) { -//// println("Don't swap $a and $b") -// null -// } -// } -// -// -1 -// // 1100100111001101111100110000001000000011101000 -// // 1100101000010010000000110000000111000011101000 -// // 0000001111011111111100000000001111000000000000 -// -// /* -// -// -// z12 z13 z14 z15 z26 z27 z28 z29 z30 z31 z32 z33 z34 z36 z37 z38 z39 -// psw OR nng mtp XOR kth rpt XOR hbh ckk XOR sqb x26 AND y26 swt XOR cmf ghk XOR pgc fht XOR vbp nvq XOR gtd trd XOR dtj vtg AND bkh nwm XOR rpb ksf XOR bhh htb XOR qnf hpp XOR wkk phr XOR ths hhd XOR qnv -// x12 AND y12 nhb AND cdq mtp AND kth nhb XOR cdq cns OR qrs y14 XOR x14 x15 XOR y15 ctc OR skj y27 XOR x27 gsd OR kbg tbc OR nvm y28 XOR x28 y29 XOR x29 crb OR qhw drg OR wbb x30 XOR y30 x31 XOR y31 rkn OR mck nhq OR bjf x32 XOR y32 tbt OR skt x33 XOR y33 kmb OR jtq x34 XOR y34 bbb OR cnp y36 AND x36 x37 XOR y37 thg OR vpm x38 XOR y38 ggw OR gpv bdg OR kmg x39 XOR y39 -// y12 XOR x12 fkw OR mdq -// tnr AND kdw x11 AND y11 -// mjb OR knt y11 XOR x11 -// y10 AND x10 kqs AND fnm -// rjj OR qsm y10 XOR x10 -// hsq AND bpc x09 AND y09 -// x09 XOR y09 rbb OR gth -// ndh AND whc y08 AND x08 -// y08 XOR x08 hnm OR vqk -// mwg AND wnd x07 AND y07 -// mjs OR dbc x07 XOR y07 -// jrg AND rjb x06 AND y06 -// y06 XOR x06 vth OR jvv -// x05 AND y05 str AND gtt -// hwt OR mfj x05 XOR y05 -// y04 AND x04 vwf AND dft -// y04 XOR x04 nbj OR rnw -// twj AND jfr y03 AND x03 -// fph OR nkm x03 XOR y03 -// y02 AND x02 rhr AND rsk -// cbq OR gwd x02 XOR y02 -// */ -// } + private fun findOperation(op: String) : (Int, Int) -> Int { + return when (op) { + "AND" -> Int::and + "OR" -> Int::or + "XOR" -> Int::xor + else -> throw IllegalArgumentException("Unknown operation $op") + } + } + + private fun checkZero(wirings: MutableMap) : Pair? { + val z00Wiring = wirings["z00"]!! + val expectedInput = setOf("x00", "y00") + return if (z00Wiring.op != "XOR" || expectedInput != z00Wiring.input()) { + // find `x00 XOR y00 = ???` + val itemToSwapTo = wirings.entries.first { (_, w) -> + val (a, op, b, _) = w + op == "XOR" && expectedInput == setOf(a, b) + } + return "z00" to itemToSwapTo.key + } else { + null + } + } + + private fun checkOne(wirings: MutableMap): Pair? { + val z01Wiring = wirings["z01"]!! + + // aaa XOR bbb = z01 + // y01 XOR x01 = bbb + // x00 AND y00 = aaa + val aaaInput = setOf("x00", "y00") + val bbbInput = setOf("x01", "y01") + + val aaa = wirings.entries.first { (_, w) -> w.op == "AND" && aaaInput == setOf(w.a, w.b) } + val bbb = wirings.entries.first { (_, w) -> w.op == "XOR" && bbbInput == setOf(w.a, w.b) } + + val correctInput = setOf(aaa.key, bbb.key) + if (z01Wiring.input() == correctInput) { + return null + } else { + // is there a `aaa.key AND bbb.key` which we need to swap to z01? + val maybeZSwap = wirings.entries.find { (_, w) -> w.op == "XOR" && setOf(w.a, w.b) == setOf(aaa.key, bbb.key) } + if (maybeZSwap != null) { + return "z01" to maybeZSwap.key + } else { + // our "z01" was correct - so the input needs swapping + return if (!z01Wiring.input().contains(aaa.key) && !z01Wiring.input().contains(bbb.key)) { + // full disjointSet - this should not happen in the input + throw IllegalStateException("Input has full disjoint set!") + } else { + // one of the items is missing + if (aaa.key in z01Wiring.input()) { + // bbb needs swap + bbb.key to z01Wiring.input().minus(aaa.key).first() + } else { + // aaa needs swap + aaa.key to z01Wiring.input().minus(bbb.key).first() + } + } + } + } + } + + fun checkDefault( + wirings: MutableMap, + key: Pair, + previousWirings: Pair, + previousPreviousWirings: Pair, + correctWirings: Map>, + ): Pair? { + val myWirings = mutableListOf() + val toCheck = LinkedList() + toCheck.add(key.second) + while(toCheck.isNotEmpty()) { + val wiring = wirings[toCheck.poll()]!! + val (a, op, b, r) = wiring + + myWirings.add(wiring) + + val aIsInput = a.startsWith('x') || a.startsWith('y') + val bIsInput = b.startsWith('x') || b.startsWith('y') + + if (!aIsInput) { toCheck.add(a) } + if (!bIsInput) { toCheck.add(b) } + } + + val currentWirings = myWirings.minus(correctWirings[previousWirings.second]!!) + val previousNewWirings = correctWirings[previousWirings.second]!!.minus(correctWirings[previousPreviousWirings.second]!!) + + // the items we need ... + // for zN + // sum(N-1) AND carryChain(N-1) = carryAfter(N-1) + // y(N-1) AND x(N-1) = carry(N-1) + // carryAfter(N-1) OR carry(N-1) = carryChainN + // yN XOR yN = sumN + // sumN XOR carryChainN = zN + + // carry(N-2) is in the previousNewWirings as x(N-2) AND y(N-2) + val x2 = previousPreviousWirings.first.let { "x${it.toString().padStart(2, '0')}" } + val y2 = previousPreviousWirings.first.let { "y${it.toString().padStart(2, '0')}" } + val carryN2 = previousNewWirings.first { w -> w.op == "AND" && setOf(w.a, w.b) == setOf(x2, y2) } + + // sum(N-1) + val x1 = previousWirings.first.let { "x${it.toString().padStart(2, '0')}" } + val y1 = previousWirings.first.let { "y${it.toString().padStart(2, '0')}" } + val sumN1 = previousNewWirings.first { w -> w.op == "XOR" && setOf(w.a, w.b) == setOf(x1, y1) } + + // carry(N-1) + val carryN1 = wirings.entries.first { (_, w) -> w.op == "AND" && setOf(w.a, w.b) == setOf(x1, y1) }.value + + // sumN + val x = key.first.let { "x${it.toString().padStart(2, '0')}" } + val y = key.first.let { "y${it.toString().padStart(2, '0')}" } + val sumN = wirings.entries.first { (_, w) -> w.op == "XOR" && setOf(w.a, w.b) == setOf(x, y) }.value + + // carryChain(N-1) + val carryChainN1 = previousNewWirings.find { w -> w.op == "OR" } ?: previousNewWirings.first { w -> w.op == "AND" } + // carryAfter(N-1) + val carryAfterN1 = wirings.entries.find { (_, w) -> w.op == "AND" && setOf(w.a, w.b) == setOf(sumN1.result, carryChainN1.result) }?.value + + if (carryAfterN1 == null) { + // TODO: update to match below + throw IllegalArgumentException("Something Wrong with $carryChainN1 or $sumN1") + } + + // carryChainN + val carryChainN = wirings.entries.find { (_, w) -> w.op == "OR" && setOf(w.a, w.b) == setOf(carryAfterN1.result, carryN1.result) }?.value + if (carryChainN == null) { + // TODO: update to match below + throw IllegalArgumentException("Something Wrong with $carryAfterN1 or $carryN1") + } + + // zN + val zN = wirings.entries.find { (_, w) -> w.op == "XOR" && setOf(w.a, w.b) == setOf(sumN.result, carryChainN.result) }?.value + if (zN == null) { + val correctZN = wirings[key.second]!! + if (carryChainN.result in correctZN.input()) { + // something bad with sumN + return sumN.result to correctZN.input().minus(carryChainN.result).first() + } else { + // something bad with carryChainN + return carryChainN.result to correctZN.input().minus(sumN.result).first() + } + } + + if (setOf(carryN1, sumN, carryAfterN1, carryChainN, zN) != currentWirings.toSet()) { + // if we got this far - we need to swap out zN values + val toSwapWith = currentWirings.first { it.result == key.second } + return toSwapWith.result to zN.result + } + + return null + } } -data class Wiring(val a: String, val op: String, val b: String, val result: String) \ No newline at end of file +data class Wiring(val a: String, val op: String, val b: String, val result: String) { + fun input() = setOf(a, b) +} \ No newline at end of file From 53e42e2654d343b13f9a658653486559ca4a268f Mon Sep 17 00:00:00 2001 From: Brian Peck Date: Tue, 24 Dec 2024 16:29:35 -0800 Subject: [PATCH 3/4] Cleanup --- .../peckb/aoc/_2024/calendar/day24/Day24.kt | 153 ++++++------------ 1 file changed, 52 insertions(+), 101 deletions(-) diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt index 55d44860..26f7b1b1 100644 --- a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/Day24.kt @@ -19,28 +19,21 @@ class Day24 @Inject constructor( return@input } if (readingBase) { - line.split(": ").let { (key, value) -> - data[key] = lazy { value.toInt() } - } + line.split(": ").let { (key, value) -> data[key] = lazy { value.toInt() } } } else { - line.split(" ").let { (first, operator, second, _, result) -> - if (result.startsWith('z')) { - zValues.add(result) - } - val op = when (operator) { - "AND" -> Int::and - "OR" -> Int::or - "XOR" -> Int::xor - else -> throw IllegalArgumentException("Unknown operation $operator") - } + line.split(" ").let { (first, operations, second, _, result) -> + if (result.startsWith('z')) { zValues.add(result) } + + val op = findOperation(operations) data[result] = lazy { op(data[first]!!.value, data[second]!!.value) } } } } - val zResults = zValues.sorted().map { data[it]!!.value }.reversed() - - zResults.joinToString("").toLong(2) + zValues.sortedDescending() + .map { data[it]!!.value } + .joinToString("") + .toLong(2) } fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input -> @@ -51,17 +44,6 @@ class Day24 @Inject constructor( val zValues = mutableListOf() val wirings = mutableMapOf() - val swaps = mutableMapOf( -// /*"kth" to "z12",*/ "carryChain13" to "z12", -// /*"z12" to "kth",*/ "z12" to "carryChain13", -// /*"gsd" to "z26",*/ "carry26" to "z26", -// /*"z26" to "gsd",*/ "z26" to "carry26", -// /*"tbt" to "z32",*/ "carryAfter32" to "z32", -// /*"z32" to "tbt",*/ "z32" to "carryAfter32", -// "qnf" to "vpm", -// "vpm" to "qnf", - ) - input.forEach input@{ line -> if (line.isEmpty()) { readingBase = false @@ -74,33 +56,19 @@ class Day24 @Inject constructor( data[key] = { value.toInt() } } } else { - line.split(" ").let { (a, operation, b, _, possibleResult) -> + line.split(" ").let { (a, operation, b, _, result) -> + if (result.startsWith('z')) { zValues.add(result) } - val result = swaps[possibleResult] ?: possibleResult + val op = findOperation(operation) - if (result.startsWith('z')) { - zValues.add(result) - } - val op = when (operation) { - "AND" -> Int::and - "OR" -> Int::or - "XOR" -> Int::xor - else -> throw IllegalArgumentException("Unknown operation $operation") - } wirings[result] = Wiring(a, operation, b, result) - data[result] = { - op(data[a]!!(), data[b]!!()) - } + + data[result] = { op(data[a]!!(), data[b]!!()) } } } } - val binaryX = xValues.sortedDescending().map { data[it]!!() }.joinToString("") - val binaryY = yValues.sortedDescending().map { data[it]!!() }.joinToString("") - - val goodBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) -// val result222 = zValues.sortedDescending().map { data[it]!!() }.joinToString("") -// val goodBinaryZ = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) + val swaps = mutableMapOf() fun swap(swap: Pair) { val oldA = wirings[swap.first]!! @@ -124,26 +92,6 @@ class Day24 @Inject constructor( val correctWirings = mutableMapOf>() - fun populateCorrectWirings(result: String) { - val myWirings = mutableListOf() - val toCheck = LinkedList() - toCheck.add(result) - while(toCheck.isNotEmpty()) { - val wiring = wirings[toCheck.poll()]!! - val (a, op, b, r) = wiring - - myWirings.add(wiring) - - val aIsInput = a.startsWith('x') || a.startsWith('y') - val bIsInput = b.startsWith('x') || b.startsWith('y') - - if (!aIsInput) { toCheck.add(a) } - if (!bIsInput) { toCheck.add(b) } - } - - correctWirings[result] = myWirings - } - (0 .. 45).forEach loop@{ n -> val previousPreviousKey = "z${(n-2).toString().padStart(2, '0')}" val previousKey = "z${(n-1).toString().padStart(2, '0')}" @@ -160,15 +108,18 @@ class Day24 @Inject constructor( )?.also { swap(it) } // don't need to check the last - the error won't be there. } - populateCorrectWirings(key) - } - if (swaps.size != 8) { - throw IllegalStateException("Did not find all the swaps") + correctWirings[key] = getMyWirings(key, wirings) } - val result = zValues.sortedDescending().map { data[it]!!() }.joinToString("") - if (result != goodBinaryZ) throw IllegalStateException("We found eight swaps, but didn't get the right result") - + if (swaps.size != 8) { throw IllegalStateException("Did not find all the swaps") } + + val binaryX = xValues.sortedDescending().map { data[it]!!() }.joinToString("") + val binaryY = yValues.sortedDescending().map { data[it]!!() }.joinToString("") + val expectedResult = (binaryX.toLong(2) + binaryY.toLong(2)).toString(2) + val actualResult = zValues.sortedDescending().map { data[it]!!() }.joinToString("") + + if (actualResult != expectedResult) { throw IllegalStateException("We found eight swaps, but didn't get the right result") } + swaps.keys.sorted().joinToString(",") } @@ -242,21 +193,7 @@ class Day24 @Inject constructor( previousPreviousWirings: Pair, correctWirings: Map>, ): Pair? { - val myWirings = mutableListOf() - val toCheck = LinkedList() - toCheck.add(key.second) - while(toCheck.isNotEmpty()) { - val wiring = wirings[toCheck.poll()]!! - val (a, op, b, r) = wiring - - myWirings.add(wiring) - - val aIsInput = a.startsWith('x') || a.startsWith('y') - val bIsInput = b.startsWith('x') || b.startsWith('y') - - if (!aIsInput) { toCheck.add(a) } - if (!bIsInput) { toCheck.add(b) } - } + val myWirings = getMyWirings(key.second, wirings) val currentWirings = myWirings.minus(correctWirings[previousWirings.second]!!) val previousNewWirings = correctWirings[previousWirings.second]!!.minus(correctWirings[previousPreviousWirings.second]!!) @@ -266,14 +203,9 @@ class Day24 @Inject constructor( // sum(N-1) AND carryChain(N-1) = carryAfter(N-1) // y(N-1) AND x(N-1) = carry(N-1) // carryAfter(N-1) OR carry(N-1) = carryChainN - // yN XOR yN = sumN + // yN XOR yN = sumN // sumN XOR carryChainN = zN - // carry(N-2) is in the previousNewWirings as x(N-2) AND y(N-2) - val x2 = previousPreviousWirings.first.let { "x${it.toString().padStart(2, '0')}" } - val y2 = previousPreviousWirings.first.let { "y${it.toString().padStart(2, '0')}" } - val carryN2 = previousNewWirings.first { w -> w.op == "AND" && setOf(w.a, w.b) == setOf(x2, y2) } - // sum(N-1) val x1 = previousWirings.first.let { "x${it.toString().padStart(2, '0')}" } val y1 = previousWirings.first.let { "y${it.toString().padStart(2, '0')}" } @@ -291,16 +223,15 @@ class Day24 @Inject constructor( val carryChainN1 = previousNewWirings.find { w -> w.op == "OR" } ?: previousNewWirings.first { w -> w.op == "AND" } // carryAfter(N-1) val carryAfterN1 = wirings.entries.find { (_, w) -> w.op == "AND" && setOf(w.a, w.b) == setOf(sumN1.result, carryChainN1.result) }?.value - if (carryAfterN1 == null) { - // TODO: update to match below + // doesn't happen on input throw IllegalArgumentException("Something Wrong with $carryChainN1 or $sumN1") } // carryChainN val carryChainN = wirings.entries.find { (_, w) -> w.op == "OR" && setOf(w.a, w.b) == setOf(carryAfterN1.result, carryN1.result) }?.value if (carryChainN == null) { - // TODO: update to match below + // doesn't happen on input throw IllegalArgumentException("Something Wrong with $carryAfterN1 or $carryN1") } @@ -308,12 +239,12 @@ class Day24 @Inject constructor( val zN = wirings.entries.find { (_, w) -> w.op == "XOR" && setOf(w.a, w.b) == setOf(sumN.result, carryChainN.result) }?.value if (zN == null) { val correctZN = wirings[key.second]!! - if (carryChainN.result in correctZN.input()) { + return if (carryChainN.result in correctZN.input()) { // something bad with sumN - return sumN.result to correctZN.input().minus(carryChainN.result).first() + sumN.result to correctZN.input().minus(carryChainN.result).first() } else { // something bad with carryChainN - return carryChainN.result to correctZN.input().minus(sumN.result).first() + carryChainN.result to correctZN.input().minus(sumN.result).first() } } @@ -325,6 +256,26 @@ class Day24 @Inject constructor( return null } + + private fun getMyWirings(key: String, wirings: MutableMap): MutableList { + val myWirings = mutableListOf() + val toCheck = LinkedList() + toCheck.add(key) + while(toCheck.isNotEmpty()) { + val wiring = wirings[toCheck.poll()]!! + val (a, op, b, r) = wiring + + myWirings.add(wiring) + + val aIsInput = a.startsWith('x') || a.startsWith('y') + val bIsInput = b.startsWith('x') || b.startsWith('y') + + if (!aIsInput) { toCheck.add(a) } + if (!bIsInput) { toCheck.add(b) } + } + + return myWirings + } } data class Wiring(val a: String, val op: String, val b: String, val result: String) { From 76f986360385cd68cfb51affef4918f8a92e0ed8 Mon Sep 17 00:00:00 2001 From: Brian Peck Date: Tue, 24 Dec 2024 16:32:10 -0800 Subject: [PATCH 4/4] udate readmes --- README.md | 4 +++- src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd51cb4a..8e536fc1 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,9 @@ as the value which passed the given day/phase combination * Day 03 * Regex shining today! Sorting by the match location made the single list of all matches easy to scan through. * Day 12 - * Not a day where I had the best solution; but a solution nonetheless. Slow scan of each field's border; ain't much, but it's honest work. + * Not a day where I had the best solution; but a solution nonetheless. Slow scan of each field's border; ain't much, but it's honest work. +* Day 24 + * I had to pen + paper the finding of the wires; while renaming my output to be what they actually are (i.e. `x01 XOR y01 -> sum1`). But then coming up with the actual rules to mimic my manual process was fun. And in the process learned a lot about binary addition at the logic gate level. ### Interesting approaches: diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md index 80c9053d..68a43848 100644 --- a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day24/README.md @@ -1 +1 @@ -## [](https://adventofcode.com/2024/day/24) \ No newline at end of file +## [Day 24: Crossed Wires](https://adventofcode.com/2024/day/24) \ No newline at end of file