/
Day16.kt
67 lines (58 loc) 路 2.69 KB
/
Day16.kt
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
package com.clouddjr.advent2022
class Day16(input: List<String>) {
private val valves = input.map(Valve::from).associateBy(Valve::name)
private val usefulValves = valves.filter { it.value.rate > 0 }
private val distances = computeDistances()
fun solvePart1() = traverse(minutes = 30)
fun solvePart2() = traverse(minutes = 26, elephantGoesNext = true)
private fun traverse(
minutes: Int,
current: Valve = valves.getValue("AA"),
remaining: Set<Valve> = usefulValves.values.toSet(),
cache: MutableMap<State, Int> = mutableMapOf(),
elephantGoesNext: Boolean = false
): Int {
val currentScore = minutes * current.rate
val currentState = State(current.name, minutes, remaining)
return currentScore + cache.getOrPut(currentState) {
val maxCurrent = remaining
.filter { next -> distances[current.name]!![next.name]!! < minutes }
.takeIf { it.isNotEmpty() }
?.maxOf { next ->
val remainingMinutes = minutes - 1 - distances[current.name]!![next.name]!!
traverse(remainingMinutes, next, remaining - next, cache, elephantGoesNext)
}
?: 0
maxOf(maxCurrent, if (elephantGoesNext) traverse(minutes = 26, remaining = remaining) else 0)
}
}
private fun computeDistances(): Map<String, Map<String, Int>> {
return valves.keys.map { valve ->
val distances = mutableMapOf<String, Int>().withDefault { Int.MAX_VALUE }.apply { put(valve, 0) }
val toVisit = mutableListOf(valve)
while (toVisit.isNotEmpty()) {
val current = toVisit.removeFirst()
valves[current]!!.next.forEach { neighbour ->
val newDistance = distances[current]!! + 1
if (newDistance < distances.getValue(neighbour)) {
distances[neighbour] = newDistance
toVisit.add(neighbour)
}
}
}
distances
}.associateBy { it.keys.first() }
}
private data class State(val current: String, val minutes: Int, val opened: Set<Valve>)
private data class Valve(val name: String, val rate: Int, val next: List<String>) {
companion object {
fun from(line: String): Valve {
return Valve(
name = line.substringAfter("Valve ").substringBefore(" "),
rate = line.substringAfter("rate=").substringBefore(";").toInt(),
next = line.substringAfter("to valve").substringAfter(" ").split(", ")
)
}
}
}
}