@@ -24,9 +24,7 @@ import com.cluddles.aoc.util.MutableGrid
2424 *
2525 * Improvements that could be made:
2626 * * Use faster pathfinding algo, e.g. A*
27- * * Don't just brute force the answer for part 2. Ideas include:
28- * 1. check successful paths and recalculate for the lowest tick that an obstruction would appear on it
29- * 2. binary search to divide search space, e.g. tick 100 is good, tick 200 is bad, check 150 and so on
27+ * * Part 2: when there's a successful path, next scan using the lowest tick that an obstruction would block it
3028 */
3129object Day18: Solver<Grid<Int>, String> {
3230
@@ -67,16 +65,20 @@ object Day18: Solver<Grid<Int>, String> {
6765 }
6866
6967 override fun solvePart2 (input : Grid <Int >): String {
70- // It should be brute forceable
71- // It's slow, but probably less so than me implementing a quicker algo
72- // I'll make nice later
73- for (i in 0 .. input.max()) {
74- val result = solvePart1(input, i)
75- if (result == Int .MAX_VALUE ) {
76- return with (input.iterableWithPos().first { it.data == i- 1 }) { " $x ,$y " }
68+ // Unless inputs are seriously janky, assume tick=0 is always good and tick=max is always bad
69+ var min = 0
70+ var max = input.filter { it != Int .MAX_VALUE }.max()
71+ // Quickest/dirtiest binary search I've ever written
72+ while (max - min > 1 ) {
73+ val current = (min + max) / 2
74+ val valid = (solvePart1(input, current) != Int .MAX_VALUE )
75+ if (valid) {
76+ min = maxOf(current, min + 1 )
77+ } else {
78+ max = minOf(current, max - 1 )
7779 }
7880 }
79- error( " No solution " )
81+ return with (input.iterableWithPos().first { it.data == max - 1 }) { " $x , $y " }
8082 }
8183
8284}
0 commit comments