Please see the original day 10 post for the Rust and good_lp solution I initially coded up. For other Kotlin solutions see the cheesy day 12 post.

The Code#

This is an implementation of the ideas in a post by u/tenthmascot on Reddit.

The Reddit post linked above has a good description of the algorithm. I won’t repeat it here as it’s not my work, but the post’s title “Bifurcate your way to victory!” sums it up quite well.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import java.io.File

data class Machine(
    val indicators: Int,
    val buttons: List<List<Int>>,
    val joltage: List<Int>
)

fun String.extractNums() = drop(1).dropLast(1).split(",").map(String::toInt)
fun addV(a: List<Int>, b: List<Int>) = a.zip(b) { x, y -> x + y }
fun leqV(a: List<Int>, b: List<Int>) = a.zip(b).all { (x, y) -> x <= y }

fun combos(buttons: List<Int>) = buttons.fold(listOf(0 to 0)) { acc, b ->
    acc + acc.map { (m, c) -> (m xor b) to (c + 1) }
}

fun String.toMachine() = split(" ").let { parts ->
    val diagram = parts.first().filter { it == '.' || it == '#' }
    val indicators = diagram.fold(0 to 1) { (mask, bit), c ->
        (mask or if (c == '#') bit else 0) to (bit shl 1)
    }.first
    val tuples = parts.drop(1).map(String::extractNums)
    Machine(indicators, tuples.dropLast(1), tuples.last())
}

fun solve1(buttons: List<List<Int>>, indicators: Int): Int {
    val buttonMasks = buttons.map {
        it.fold(0) { acc, bit -> acc or (1 shl bit) }
    }
    val leftLen = buttonMasks.size / 2
    val leftButtons = buttonMasks.subList(0, leftLen)
    val rightButtons = buttonMasks.subList(leftLen, buttons.size)

    val left = combos(leftButtons)
        .groupBy { it.first }
        .mapValues { (_, values) -> values.minOf { it.second } }
    val right = combos(rightButtons)

    return right.mapNotNull { (rv, c) ->
        left[indicators xor rv]?.let { lc -> c + lc }
    }.minOrNull() ?: 0
}

fun solve2aux(
    goal: List<Int>,
    patterns: Map<List<Int>, List<Pair<List<Int>, Int>>>,
    memo: MutableMap<List<Int>, Int?>
): Int? {
    if (goal.all { it == 0 }) return 0
    if (memo.containsKey(goal)) return memo[goal]

    val parity = goal.map { it % 2 }
    val candidates = patterns[parity]?.filter { (pVec, _) -> leqV(pVec, goal) }

    val best = candidates?.mapNotNull { (p, cost) ->
        val nextGoal = goal.zip(p) { x, y -> (x - y) / 2 }
        solve2aux(nextGoal, patterns, memo)?.let { cost + 2 * it }
    }?.minOrNull()

    memo[goal] = best
    return best
}

fun solve2(buttons: List<List<Int>>, joltage: List<Int>): Int {
    val dim = joltage.size
    val coeffs = buttons.map { idxs -> List(dim) { if (it in idxs) 1 else 0 } }
    val patterns = (0 until coeffs.size)
        .fold(listOf(emptyList<Int>())) { acc, x -> acc + acc.map { it + x } }
        .map { idxs ->
            val vec = idxs.fold(List(dim) { 0 }) { acc, i ->
                addV(acc, coeffs[i])
            }
            val parity = vec.map { it % 2 }
            parity to (vec to idxs.size)
        }
        .groupBy { it.first }
        .mapValues { (_, pairs) -> pairs.map { it.second } }
    val memo: MutableMap<List<Int>, Int?> = mutableMapOf()

    return solve2aux(joltage, patterns, memo) ?: 0
}

val machines = File("y25d10.txt").readLines().map(String::toMachine)

val result1 = machines.sumOf { (inds, bms, _) -> solve1(bms, inds) }
println("Result1: $result1")

val result2 = machines.sumOf { (_, bms, joltage) -> solve2(bms, joltage)  }
println("Result2: $result2")

Install Kotlin and run#

The recommended way to install Kotlin is by installing IntelliJ IDEA. If you only want to run this script, it might be easier to just install the kotlin package if your package manager has it.

# I installed from Homebrew
brew install kotlin

# kotlinlang.org recommends installing IntelliJ IDEA or Android Studio
# https://kotlinlang.org/docs/getting-started.html#install-kotlin

# Execute the code (or launch from the IDE)
kotlin d10.kts