Day 20 part 1.
This commit is contained in:
parent
aeb0d108ee
commit
bba6a5b5b7
2 changed files with 39 additions and 52 deletions
|
@ -1,7 +1,10 @@
|
|||
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 previous = mutableMapOf<T, T>()
|
||||
val visited = mutableSetOf<T>()
|
||||
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) {
|
||||
val (current, totalDistance) = queue.poll() ?: break
|
||||
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)
|
||||
|
@ -23,6 +34,7 @@ fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair
|
|||
val currentDistance = distanceFromStart[neighbor]
|
||||
if (currentDistance == null || newDistance < currentDistance) {
|
||||
distanceFromStart[neighbor] = newDistance
|
||||
previous[neighbor] = current
|
||||
queue.add(neighbor to newDistance)
|
||||
}
|
||||
}
|
||||
|
@ -30,3 +42,7 @@ fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair
|
|||
|
||||
return null
|
||||
}
|
||||
|
||||
fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair<T, Int>>): Int? {
|
||||
return dijkstraPath(start, goal, neighbors)?.totalDistance
|
||||
}
|
|
@ -1,58 +1,29 @@
|
|||
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 end = maze.find('E') ?: error("No end position")
|
||||
|
||||
val paths = maze.findPaths(start, end)
|
||||
|
||||
val total = paths.first { !it.cheated }.time
|
||||
val shortest = maze.findShortestPath(start, end) ?: error("No shortest path found")
|
||||
val total = shortest.last().second
|
||||
val toGoal = shortest.associate { (step, fromStart) ->
|
||||
step to total - fromStart
|
||||
}
|
||||
println("Time without cheating: $total")
|
||||
val part1 = paths.count { it.time <= total - 100 }
|
||||
println("Part 1: $part1")
|
||||
|
||||
paths.sortedBy { it.time }
|
||||
.groupBy { it.time }
|
||||
.forEach { (time, list) ->
|
||||
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] == '#' }
|
||||
val part1 = shortest.flatMap { (step, fromStart) ->
|
||||
step.neighbors()
|
||||
.filter { it in maze && maze[it] == '#' }
|
||||
.flatMap { it.neighbors() }
|
||||
.filter { canVisit(it) }
|
||||
.fold(paths) { list, next ->
|
||||
list.addAll(findPaths(next, goal, newVisited, time + 2, true))
|
||||
list
|
||||
}
|
||||
}
|
||||
.filter { it != step && it in maze && maze[it] != '#' }
|
||||
.mapNotNull { toGoal[it] }
|
||||
.map { fromStart + 2 + it }
|
||||
}.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
|
||||
|
|
Loading…
Add table
Reference in a new issue