Skip to content

Commit b4b2238

Browse files
committed
Day10
1 parent 04dd43a commit b4b2238

File tree

5 files changed

+312
-0
lines changed

5 files changed

+312
-0
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ repositories {
1212
dependencies {
1313
implementation(kotlin("reflect"))
1414
implementation("org.jetbrains:annotations:26.0.2-1")
15+
implementation("tools.aqua:z3-turnkey:4.14.1")
1516

1617
testImplementation(platform("org.junit:junit-bom:6.0.1"))
1718
testImplementation("org.junit.jupiter:junit-jupiter-params")

src/main/kotlin/Day10.kt

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import com.microsoft.z3.Context
2+
import com.microsoft.z3.IntNum
3+
import com.microsoft.z3.Status.SATISFIABLE
4+
import java.util.PriorityQueue
5+
import kotlin.io.path.readLines
6+
7+
class Day10 : Day {
8+
override fun partOne(filename: String, verbose: Boolean): Int {
9+
val machines = filename.asPath().readLines().map { it.parseMachine() }
10+
11+
if (verbose) {
12+
machines.forEach { machine ->
13+
print(machine.indicatorLights.joinToString("", "[", "]") { if (it) "#" else "." })
14+
print(" ")
15+
print(machine.buttons.joinToString(" ") { it.joinToString(",", "(", ")") })
16+
print(" ")
17+
print(machine.joltages.joinToString(",", "{", "}"))
18+
println()
19+
}
20+
}
21+
22+
return machines.sumOf { machine ->
23+
search(
24+
List(machine.indicatorLights.size) { false },
25+
{ state ->
26+
machine.buttons.map { buttons ->
27+
state.mapIndexed { index, on ->
28+
if (index in buttons) !on else on
29+
} to 1
30+
}
31+
},
32+
{ state -> state == machine.indicatorLights}
33+
)[machine.indicatorLights]!!
34+
}
35+
}
36+
37+
override fun partTwo(filename: String, verbose: Boolean): Int {
38+
val machines = filename.asPath().readLines().map { it.parseMachine() }
39+
40+
return machines.sumOf { machine ->
41+
Context().use { ctx ->
42+
val solver = ctx.mkOptimize()
43+
val zero = ctx.mkInt(0)
44+
45+
val buttons = machine.buttons.indices
46+
.map { ctx.mkIntConst("button#$it") }
47+
.onEach { button -> solver.Add(ctx.mkGe(button, zero)) }
48+
.toTypedArray()
49+
50+
machine.joltages.forEachIndexed { counter, targetValue ->
51+
val buttonsThatIncrement = machine.buttons
52+
.withIndex()
53+
.filter { (_, counters) -> counter in counters }
54+
.mapToArray { (index) -> buttons[index] }
55+
val target = ctx.mkInt(targetValue)
56+
val sumOfPresses = ctx.mkAdd(*buttonsThatIncrement)
57+
solver.Add(ctx.mkEq(sumOfPresses, target))
58+
}
59+
60+
val presses = ctx.mkIntConst("presses")
61+
solver.Add(ctx.mkEq(presses, ctx.mkAdd(*buttons)))
62+
solver.MkMinimize(presses)
63+
64+
if (solver.Check() != SATISFIABLE) error("No solution found for machine: $machine.")
65+
solver.model.evaluate(presses, false).let { it as IntNum }.int
66+
}
67+
}
68+
}
69+
70+
private fun <T : Any> search(
71+
start: T,
72+
neighbours: (T) -> Iterable<Pair<T, Int>>,
73+
goalFunction: (T) -> Boolean,
74+
maximumCost: Int = Int.MAX_VALUE,
75+
): Map<T, Int> {
76+
val distance = mutableMapOf(start to 0)
77+
val visited = mutableSetOf<Pair<T, Int>>()
78+
79+
val queue = PriorityQueue(compareBy<Triple<T, Int, Int>> { (_, _, priority) -> priority })
80+
queue.add(Triple(start, 0, 0))
81+
82+
while (queue.isNotEmpty()) {
83+
val (node, costSoFar, _) = queue.poll()
84+
if (!visited.add(node to costSoFar)) continue
85+
if (goalFunction(node)) return distance
86+
87+
for ((nextNode, nextCost) in neighbours(node)) {
88+
if (maximumCost - nextCost < costSoFar) continue
89+
90+
val totalCost = costSoFar + nextCost
91+
92+
if (totalCost > (distance[nextNode] ?: Int.MAX_VALUE)) continue
93+
94+
distance[nextNode] = totalCost
95+
96+
queue.add(Triple(nextNode, totalCost, totalCost))
97+
}
98+
}
99+
100+
return emptyMap()
101+
}
102+
103+
private data class Machine(
104+
val indicatorLights: List<Boolean>,
105+
val buttons: List<Set<Int>>,
106+
val joltages: List<Int>,
107+
)
108+
109+
private val machinePattern = Regex("""\[(.*)] (.*) \{(.*)}""")
110+
111+
private fun String.parseMachine() =
112+
machinePattern.matchEntire(this)!!.destructured.let { (indicatorLights, buttons, joltages) ->
113+
Machine(
114+
indicatorLights.map { it == '#' },
115+
buttons.split(" ").map { button ->
116+
button.removeSurrounding("(", ")").split(',').mapToSet { it.toInt() }
117+
},
118+
joltages.split(",").map { it.toInt() }
119+
)
120+
}
121+
122+
companion object : Day.Main("Day10.txt") {
123+
@JvmStatic
124+
fun main(args: Array<String>) = main()
125+
}
126+
}

0 commit comments

Comments
 (0)