@@ -5,8 +5,9 @@ import com.cluddles.aoc.core.Solver
55import com.cluddles.aoc.core.SolverInput
66import com.cluddles.aoc.util.Dir4
77import com.cluddles.aoc.util.Grid
8+ import com.cluddles.aoc.util.Int2d
89import com.cluddles.aoc.util.IntGrid
9- import com.cluddles.aoc. util.MutableGrid
10+ import java. util.*
1011
1112/* *
1213 * RAM Run
@@ -21,10 +22,6 @@ import com.cluddles.aoc.util.MutableGrid
2122 * Initial implementation as quick and easy depth-first search, which is good enough to get the answers.
2223 *
2324 * Input grid contains the "tick" that each cell becomes obstructed.
24- *
25- * Improvements that could be made:
26- * * Use faster pathfinding algo, e.g. A*
27- * * Part 2: when there's a successful path, next scan using the lowest tick that an obstruction would block it
2825 */
2926object Day18: Solver<Grid<Int>, String> {
3027
@@ -41,23 +38,36 @@ object Day18: Solver<Grid<Int>, String> {
4138 return result
4239 }
4340
44- // Quick and dirty DFS
45- private fun populateCosts (input : Grid <Int >, costs : MutableGrid <Int >, x : Int , y : Int , cost : Int , tick : Int ) {
46- costs[x, y] = cost
47- val newCost = cost + 1
48- for (d in Dir4 .entries) {
49- val x = x + d.x
50- val y = y + d.y
51- if (input.getIfInBounds(x, y) { - 1 } >= tick && newCost < costs[x, y]) {
52- populateCosts(input, costs, x, y, newCost, tick)
53- }
41+ // A* to find lowest cost path from start to end
42+ private fun lowestCost (start : Int2d , end : Int2d , grid : Grid <Int >, tick : Int ): Int? {
43+ val g = mutableMapOf<Int2d , Int >()
44+ val f = mutableMapOf<Int2d , Int >()
45+ val open = PriorityQueue <Int2d >(Comparator .comparingInt { f[it]!! })
46+ g[start] = 0
47+ f[start] = (end - start).manhattan()
48+ open + = start
49+ while (open.isNotEmpty()) {
50+ val current = open.remove()
51+ val gCurrent = g[current]!!
52+ if (current == end) return gCurrent
53+ val gNext = gCurrent + 1
54+ Dir4 .entries
55+ .map { current + it.delta }
56+ .filter {
57+ grid.getIfInBounds(it.x, it.y) { - 1 } >= tick
58+ && gNext < g.getOrDefault(it, Int .MAX_VALUE )
59+ }
60+ .forEach {
61+ g[it] = gNext
62+ f[it] = gNext + (end - it).manhattan()
63+ open + = it
64+ }
5465 }
66+ return null
5567 }
5668
5769 fun solvePart1 (input : Grid <Int >, tick : Int ): Int {
58- val costs = IntGrid (input.width, input.height, Int .MAX_VALUE )
59- populateCosts(input, costs, 0 , 0 , 0 , tick)
60- return costs[input.width- 1 , input.height- 1 ]
70+ return lowestCost(Int2d (0 , 0 ), Int2d (input.width- 1 , input.height- 1 ), input, tick) ? : Int .MAX_VALUE
6171 }
6272
6373 override fun solvePart1 (input : Grid <Int >): String {
0 commit comments