Day 20 part 1.

This commit is contained in:
Sven Weidauer 2024-12-26 12:24:08 +01:00
parent aeb0d108ee
commit bba6a5b5b7
2 changed files with 39 additions and 52 deletions

View file

@ -1,7 +1,10 @@
import java.util.* import java.util.*
fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair<T, Int>>): Int? { data class PathResult<T>(val steps: List<Pair<T, Int>>, val totalDistance: Int)
fun <T> dijkstraPath(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair<T, Int>>): PathResult<T>? {
val distanceFromStart = mutableMapOf(start to 0) val distanceFromStart = mutableMapOf(start to 0)
val previous = mutableMapOf<T, T>()
val visited = mutableSetOf<T>() val visited = mutableSetOf<T>()
val queue = PriorityQueue<Pair<T, Int>> { a, b -> a.second.compareTo(b.second) } val queue = PriorityQueue<Pair<T, Int>> { a, b -> a.second.compareTo(b.second) }
@ -10,7 +13,15 @@ fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair
while (true) { while (true) {
val (current, totalDistance) = queue.poll() ?: break val (current, totalDistance) = queue.poll() ?: break
if (goal(current)) { if (goal(current)) {
return totalDistance val path = generateSequence(current) { previous[it] }
.map { it to (distanceFromStart[it] ?: 0) }
.toList()
.reversed()
return PathResult(
path,
totalDistance
)
} }
visited.add(start) visited.add(start)
@ -23,10 +34,15 @@ fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair
val currentDistance = distanceFromStart[neighbor] val currentDistance = distanceFromStart[neighbor]
if (currentDistance == null || newDistance < currentDistance) { if (currentDistance == null || newDistance < currentDistance) {
distanceFromStart[neighbor] = newDistance distanceFromStart[neighbor] = newDistance
previous[neighbor] = current
queue.add(neighbor to newDistance) queue.add(neighbor to newDistance)
} }
} }
} }
return null return null
}
fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair<T, Int>>): Int? {
return dijkstraPath(start, goal, neighbors)?.totalDistance
} }

View file

@ -1,58 +1,29 @@
fun main() { fun main() {
val maze = CharGrid.read("day20-sample.txt") val maze = CharGrid.read("day20.txt")
val start = maze.find('S') ?: error("No start position") val start = maze.find('S') ?: error("No start position")
val end = maze.find('E') ?: error("No end position") val end = maze.find('E') ?: error("No end position")
val paths = maze.findPaths(start, end) val shortest = maze.findShortestPath(start, end) ?: error("No shortest path found")
val total = shortest.last().second
val total = paths.first { !it.cheated }.time val toGoal = shortest.associate { (step, fromStart) ->
step to total - fromStart
}
println("Time without cheating: $total") println("Time without cheating: $total")
val part1 = paths.count { it.time <= total - 100 }
println("Part 1: $part1")
paths.sortedBy { it.time } val part1 = shortest.flatMap { (step, fromStart) ->
.groupBy { it.time } step.neighbors()
.forEach { (time, list) -> .filter { it in maze && maze[it] == '#' }
val saving = total - time
println("${list.count()} x $saving")
}
}
data class MazeResult(val time: Int, val cheated: Boolean)
fun CharGrid.findPaths(
position: Grid.Coordinate,
goal: Grid.Coordinate,
visited: Set<Grid.Coordinate> = emptySet(),
time: Int = 0,
cheated: Boolean = false
): List<MazeResult> {
if (position == goal) {
return listOf(MazeResult(time, cheated))
}
val newVisited = visited + position
fun canVisit(position: Grid.Coordinate): Boolean =
position in this && position !in newVisited && this[position] != '#'
val paths = position.neighbors()
.filter { canVisit(it) }
.fold(mutableListOf<MazeResult>()) { list, next ->
list.addAll(findPaths(next, goal, newVisited, time + 1, cheated))
list
}
return if (cheated) {
paths
} else {
position.neighbors()
.filter { it in this && this[it] == '#' }
.flatMap { it.neighbors() } .flatMap { it.neighbors() }
.filter { canVisit(it) } .filter { it != step && it in maze && maze[it] != '#' }
.fold(paths) { list, next -> .mapNotNull { toGoal[it] }
list.addAll(findPaths(next, goal, newVisited, time + 2, true)) .map { fromStart + 2 + it }
list }.count { it <= total - 100 }
} println("Part 1: $part1")
}
} }
fun CharGrid.findShortestPath(start: Grid.Coordinate, end: Grid.Coordinate) =
dijkstraPath(start, goal = { it == end }, neighbors = { position ->
position.neighbors()
.filter { it in this && this[it] != '#' }
.map { it to 1 }
})?.steps