1
0
Fork 0
2021/src/year2021/day15/Day15.kt
Matej Focko 6b17fef240 day(15): move Dijkstra to separate class
Signed-off-by: Matej Focko <mfocko@redhat.com>
2021-12-15 11:36:29 +01:00

113 lines
3.2 KiB
Kotlin

package year2021.day15
import readInput
import java.util.*
data class Coordinate(val y: Int, val x: Int) {
fun adjacent(): Iterable<Coordinate> =
listOf(
Coordinate(y - 1, x),
Coordinate(y + 1, x),
Coordinate(y, x + 1),
Coordinate(y, x - 1)
)
}
open class Dijkstra(
private val input: List<List<Int>>
) {
private val queue: PriorityQueue<Pair<Int, Coordinate>> =
PriorityQueue<Pair<Int, Coordinate>> { a, b ->
a.first - b.first
}
private val distances = mutableMapOf<Coordinate, Int>()
private val processed = mutableSetOf<Coordinate>()
fun getDistance(coordinate: Coordinate): Int =
distances[coordinate] ?: Int.MAX_VALUE
open fun getCost(coordinate: Coordinate): Int =
input[coordinate.y][coordinate.x]
private fun relax(distance: Int, neighbour: Coordinate): Boolean {
val alternative = distance + getCost(neighbour)
if (alternative >= getDistance(neighbour)) {
return false
}
distances[neighbour] = alternative
queue.add(Pair(alternative, neighbour))
return true
}
open fun neighbours(coordinate: Coordinate): Iterable<Coordinate> =
coordinate.adjacent().filter { (y, x) ->
(y >= 0 && y < input.size) && (x >= 0 && x < input[y].size)
}
fun run(start: Coordinate, end: Coordinate): Dijkstra {
queue.add(Pair(0, start))
distances[start] = 0
while (queue.isNotEmpty()) {
val (distance, coordinate) = queue.remove()!!
if (processed.contains(coordinate)) {
continue
}
processed.add(coordinate)
if (coordinate == end) {
return this
}
neighbours(coordinate).forEach { relax(distance, it) }
}
return this
}
}
fun part1(input: List<List<Int>>): Int =
Coordinate(input.size - 1, input.last().size - 1).let { end ->
Dijkstra(input)
.run(
Coordinate(0, 0),
end
).getDistance(end)
}
class DijkstraOnExtended(private val input: List<List<Int>>) : Dijkstra(input) {
override fun neighbours(coordinate: Coordinate): Iterable<Coordinate> =
coordinate.adjacent().filter { (y, x) ->
(y >= 0 && y < 5 * input.size) && (x >= 0 && x < 5 * input[0].size)
}
override fun getCost(coordinate: Coordinate): Int {
val (y, x) = coordinate
val added = y / input.size + x / input[0].size
val riskLevel = input[y % input.size][x % input[0].size] + added
return if (riskLevel > 9) riskLevel - 9 else riskLevel
}
}
fun part2(input: List<List<Int>>): Int =
Coordinate(5 * input.size - 1, 5 * input.last().size - 1).let { end ->
DijkstraOnExtended(input)
.run(
Coordinate(0, 0),
end
).getDistance(end)
}
fun main() {
val sample = readInput(15, "sample").map { row -> row.map { it.digitToInt() } }
val input = readInput(15, "input").map { row -> row.map { it.digitToInt() } }
check(part1(sample) == 40)
println(part1(input))
check(part2(sample) == 315)
println(part2(input))
}