-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path16.rhm
196 lines (165 loc) · 4.37 KB
/
16.rhm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#lang rhombus/static/and_meta
import:
"util/advent_of_code.rhm" as aoc
"util/misc.rhm" open
"util/grid.rhm" open
fun get_puzzle_input():
aoc.fetch_input(aoc.find_session(), 2024, 16)
@example_input(test_input1a){
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############
}
@example_input(test_input1b){
#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################
}
annot.macro 'State': 'Pair.of(Posn, Posn)'
annot.macro 'Distances': 'Map.of(State, NonnegInt)'
fun initial_state(grid :: Grid) :~ State:
Pair(grid.find_char(Char"S"), Posn.E)
fun neighbors(grid :: Grid, Pair(posn, dir) :: State) :~ List.of(State):
let ns = [Pair(posn, dir.turn_left()),
Pair(posn, dir.turn_right())]
def fwd = posn $+ dir
match grid[fwd]
| #false || Char"#": ns
| ~else: ns.add(Pair(fwd, dir))
fun cost(Pair(p0, d0) :~ State, Pair(p1, d1) :~ State) :~ NonnegInt:
~who: who
cond
| p0 == p1 && d0 == d1:
error(~who: who, "shouldn't happen, same state")
| p0.dist(p1) > 1:
error(~who: who, "shouldn't happen, neighbor far away")
| d0 == d1: 1
| p0 == p1: 1000
| ~else:
error(~who: who, "shouldn't happen, illegal move")
class Heap(compare :~ Function.of_arity(2)):
field slots :~ Array = Array.make(16, #false)
field last :~ NonnegInt = 0
private method parent(i):
(i - 1) div 2
private method children(i):
[2 * i + 1, 2 * i + 2]
private method is_full():
slots.length() == last
private method grow():
// XXX: there is probably a better growth factor, but I'm too tired
// to look it up
def size = slots.length() * 2
def old = slots
slots := Array.make(size)
slots.copy_from(0, old)
private swim(i, vi):
unless i == 0
| def h = parent(i)
def vh = slots[h]
unless compare(vh, vi)
| slots[h] := vi
slots[i] := vh
swim(h, vi)
method insert(v):
when is_full()
| grow()
def i = last
slots[last] := v
last := last + 1
swim(i, v)
private sink(i, vi):
fun ref(i):
if i < last | slots[i] | #false
fun sink_child(j, vj):
when compare(vj, vi)
| slots[i] := vj
slots[j] := vi
sink(j, vi)
def [j, k] = children(i)
def vj = ref(j)
def vk = ref(k)
cond
| vj && vk:
if compare(vj, vk)
| sink_child(j, vj)
| sink_child(k, vk)
| vj: sink_child(j, vj)
| ~else: #void
method remove():
~who: who
when last == 0
| error(~who: who, "heap is empty")
def v = slots[0]
last := last - 1
def w = slots[last]
// XXX: a real implementation would probably shrink here
slots[0] := w
sink(0, w)
v
fun compare_dists(Pair(c0 :~ NonnegInt, _), Pair(c1 :~ NonnegInt, _)):
c0 < c1
fun run1(input :: String):
def grid = Grid(input)
def heap = Heap(compare_dists)
heap.insert(Pair(0, initial_state(grid)))
def posn_end :: Posn = grid.find_char(Char"E")
fun step(state :: State,
cur_dist :: NonnegInt,
distances :~ Distances)
:~ values(Distances, maybe(NonnegInt)):
for values(distances :~ Distances = distances,
end_distance = #false):
break_when end_distance
each state_n: neighbors(grid, state)
skip_when distances.get(state_n, #false)
def dist_n = cur_dist + cost(state, state_n)
heap.insert(Pair(dist_n, state_n))
values(distances ++ { state_n: dist_n },
if state_n.first == posn_end | dist_n | end_distance)
fun search(distances :~ Distances) :: NonnegInt:
def Pair(cur_dist, state) = heap.remove()
let values(distances, final_dist) = step(state, cur_dist, distances)
if final_dist
| final_dist
| search(distances)
search({})
module test:
check run1(test_input1a) ~is 7036
check run1(test_input1b) ~is 11048
module part1:
def input = get_puzzle_input()
run1(input)
fun run2(s): #void
#//
module test:
check run2(test_input1) ~is #false
module part2:
def input = get_puzzle_input()
run2(input)