Skip to content

Commit 02b02da

Browse files
committed
Day 18 with simple A*
1 parent 4a1ac51 commit 02b02da

File tree

2 files changed

+29
-18
lines changed

2 files changed

+29
-18
lines changed

src/main/kotlin/com/cluddles/aoc/util/Int2d.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import kotlin.math.absoluteValue
1111
data class Int2d(val x: Int, val y: Int) {
1212

1313
operator fun plus(o: Int2d) = Int2d(x + o.x, y + o.y)
14+
operator fun minus(o: Int2d) = Int2d(x - o.x, y - o.y)
1415
operator fun times(o: Int2d) = Int2d(x * o.x, y * o.x)
1516
operator fun times(o: Int) = Int2d(x * o, y * o)
1617

src/main/kotlin/com/cluddles/aoc/y2024/Day18.kt

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import com.cluddles.aoc.core.Solver
55
import com.cluddles.aoc.core.SolverInput
66
import com.cluddles.aoc.util.Dir4
77
import com.cluddles.aoc.util.Grid
8+
import com.cluddles.aoc.util.Int2d
89
import 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
*/
2926
object 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

Comments
 (0)