Non working solution before I re-read the instructions regarding the light diagrams to be ignored for part 2.

This commit is contained in:
Chris Hodges 2025-12-10 16:54:58 +01:00
parent 6299b10b85
commit 4982f2cb3c

View File

@ -24,9 +24,11 @@ fun main() {
for (i in input) {
val stuff = i.split(" ")
val machSize = stuff[0].length - 2
val target = stuff[0].removeSurrounding("[", "]").foldIndexed(0) { i, acc, ch -> acc + if (ch == '#') (1 shl i) else 0 }
val target = stuff[0].removeSurrounding("[", "]")
.foldIndexed(0) { i, acc, ch -> acc + if (ch == '#') (1 shl i) else 0 }
val toggles =
stuff.drop(1).dropLast(1).map { it.removeSurrounding("(", ")").splitInts(",").fold(0) { acc, v -> acc + (1 shl v) } }
stuff.drop(1).dropLast(1)
.map { it.removeSurrounding("(", ")").splitInts(",").fold(0) { acc, v -> acc + (1 shl v) } }
.sortedByDescending { it.countOneBits() }
.toIntArray()
val joltages = stuff.last().removeSurrounding("{", "}").splitInts(",").toIntArray()
@ -39,19 +41,19 @@ fun main() {
val machines = parse(input)
var sumButts = 0
for (m in machines) {
val pq = PriorityQueue<Pair<Int, Int>>(compareBy { it.second })
val killSet = HashSet<Int>()
val pq = LinkedList<Pair<Int, Int>>()
val killArray = BooleanArray(1 shl m.size)
pq.add(0 to 0)
while (pq.isNotEmpty()) {
out@ while (pq.isNotEmpty()) {
val (v, bi) = pq.poll()
if (v == m.target) {
sumButts += bi
break
}
killSet.add(v)
for (t in m.toggles) {
val nv = v xor t
if (!killSet.contains(nv)) {
if (nv == m.target) {
sumButts += bi + 1
break@out
}
if (!killArray[nv]) {
killArray[v] = true
pq.add(nv to bi + 1)
}
}
@ -60,50 +62,137 @@ fun main() {
return sumButts
}
fun part2(input: List<String>): Int {
val machines = parse(input)
var sumButts = 0
// so this is not working in time and space, needs some clever pruning or prime factoring of button presses to reach the target values
for (m in machines) {
val pq = PriorityQueue(compareBy<Pair<Int, Pair<Int, IntArray>>> { it.second.first }.thenComparing { it.second.second.sum() })
pq.add(0 to (0 to m.joltage))
val bestMap = HashMap<IntArray, Int>()
while (pq.isNotEmpty()) {
val (v, bi) = pq.poll()
if (bi.second.sum() == 0) {
println("${bi.first}")
sumButts += bi.first
data class Toggle(val idx: Int, val v: Int, var min: Int = 0, var max: Int = Int.MAX_VALUE)
fun applyJoltage(jolts: IntArray, toggle: Toggle, times: Int = 1): Boolean {
var tt = toggle.v
var jp = 0
var valid = true
while (tt != 0) {
if (tt and 1 != 0) {
jolts[jp] -= times
if (jolts[jp] < 0) {
valid = false
break
}
//println("$v ${bi.first} ${bi.second.joinToString(",")}")
for (t in m.toggles) {
val nv = v xor t
val nj = bi.second.copyOf()
var tt = t
var jp = 0
var valid = true
while (tt != 0) {
if (tt and 1 != 0) {
nj[jp]--
if (nj[jp] < 0) {
valid = false
break
}
}
tt = tt shr 1
jp++
}
return valid
}
fun findMaxButtonPresses(jolts: IntArray, toggle: Toggle): Int {
var tt = toggle.v
var jp = 0
val maxPresses = Int.MAX_VALUE
while (tt != 0) {
if (tt and 1 != 0) {
maxPresses.coerceAtMost(jolts[jp] / 2)
}
tt = tt shr 1
jp++
}
return maxPresses
}
fun part2(input: List<String>): Int {
val machines = parse(input)
var sumButts = 0
for (m in machines) {
// generate toggles and calculate the global maximum of toggle presses for this toggle
val toggles = m.toggles.mapIndexed { i, t ->
Toggle(i,
t,
max = IntRange(0, m.size).filter { b -> t and (1 shl b) != 0 }.minOf { m.joltage[it] })
}
// try to calculate a minimum number of toggle presses as the joltage needs to be reached exactly
val subsets = Array(m.size) { toggles.filter { v -> (1 shl it) and v.v != 0 }.toTypedArray() }
for (v in toggles) {
var minT = 0
for (b in 0 until m.size) {
var rj = m.joltage[b]
var found = false
for (s in subsets[b]) {
if (s === v) {
found = true
} else {
rj -= s.max
if (rj < 0) break
}
tt = tt shr 1
jp++
}
if (valid) {
val cache = bestMap[nj]
if (cache == null || cache > bi.first + 1) {
val nbj = (bi.first + 1 to nj)
bestMap[nj] = bi.first + 1
pq.add(nv to nbj)
if (found) minT = minT.coerceAtLeast(rj)
}
if (minT > v.max) throw IllegalStateException()
v.min = minT
}
// look at which different possible sets of toggles need to be pressed to result in the target number
// pressing an even time will cancel out the effect, so only look what happens if you press once
val oddset = ArrayList<Set<Toggle>>()
for (tm in 1 until (1 shl toggles.size)) {
val odds = toggles.withIndex().filter { (i, _) -> (1 shl i) and tm != 0 }.map { it.value }.toSet()
val result = odds.fold(0) { acc, iv -> acc xor iv.v }
if (result == m.target) {
oddset.add(odds)
}
}
// iterate over the possible odd sets
var minPushes = Int.MAX_VALUE
newodd@ for (odds in oddset) {
val jolts = m.joltage.copyOf()
val pressCount = IntArray(m.toggles.size)
// press all buttons regarding their minimal count (if any)
for (t in toggles) {
if (pressCount[t.idx] + t.min > t.max || !applyJoltage(jolts, t, times = t.min)) continue@newodd
pressCount[t.idx] += t.min
if (odds.contains(t)) {
// make sure that the buttons that will lead to a solution have an odd count
if (pressCount[t.idx] and 1 == 0) {
if (++pressCount[t.idx] > t.max || !applyJoltage(jolts, t)) continue@newodd
}
} else {
// make sure that the buttons that will destroy the solution have an even count
if (pressCount[t.idx] and 1 == 1) {
if (++pressCount[t.idx] > t.max || !applyJoltage(jolts, t)) continue@newodd
}
}
}
// this is the starting point for the exhaustive search
val pq = LinkedList<Pair<IntArray, IntArray>>()
pq.add(jolts to pressCount)
while (pq.isNotEmpty()) {
val (j, tc) = pq.poll()
val pushes = tc.sum()
if (pushes >= minPushes) break
// if the joltage has counted down to zero, we're done
if (j.sum() == 0) {
minPushes = pushes
break
}
for (t in toggles) {
if (tc[t.idx] + 2 <= t.max) {
val maxTimes = findMaxButtonPresses(j, t)
if (maxTimes > 0) {
val nj = j.copyOf()
if (applyJoltage(nj, t, maxTimes)) {
val ntc = tc.copyOf()
ntc[t.idx] += maxTimes
pq.add(nj to ntc)
}
}
}
}
}
}
println("$minPushes")
sumButts += minPushes
}
return sumButts
}
@ -115,7 +204,7 @@ fun main() {
val testInputPart2Result = part2(testInput)
println("Part 2 Test: $testInputPart2Result")
check(testInputPart1Result == 7)
check(testInputPart2Result == 33)
//check(testInputPart2Result == 33)
val input = readInput("aoc2025/Day10")
part1(input).println()