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.*
|
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,6 +34,7 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,3 +42,7 @@ fun <T> dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence<Pair
|
||||||
|
|
||||||
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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue