Skip to content

Commit bff4944

Browse files
committed
2024-16 * ** Gleam
1 parent dec96d3 commit bff4944

File tree

5 files changed

+330
-62
lines changed

5 files changed

+330
-62
lines changed

src/aoc_2024/day_12.gleam

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import extra
22
import gleam/int
3-
import gleam/io
43
import gleam/list
54
import gleam/set.{type Set}
6-
import gleam/string
75
import grid.{type Grid, type XY}
86

97
pub fn parse(input: String) -> Grid(String) {

src/aoc_2024/day_14.gleam

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,8 @@ fn step_pt2(seconds: Int, robots: List(Robot)) {
112112
True -> {
113113
io.println_error("------------------------")
114114
io.debug(#(seconds, "seconds"))
115-
show(robots)
116115
step_pt2(seconds + 1, step(robots))
117116
}
118117
False -> step_pt2(seconds + 1, step(robots))
119118
}
120119
}
121-
122-
fn show(robots: List(Robot)) {
123-
robots
124-
|> list.map(fn(robot) { #(robot.p, "#") })
125-
|> grid.from_list
126-
|> grid.with_dims(Dims(
127-
width: room.0,
128-
height: room.1,
129-
min_x: 0,
130-
min_y: 0,
131-
max_x: room.0 - 1,
132-
max_y: room.1 - 1,
133-
))
134-
|> grid.to_string(fun: fn(item) { #(item, Error(Nil)) }, empty: ".")
135-
|> io.println_error
136-
}

src/aoc_2024/day_15.gleam

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import extra
21
import gleam/bool
32
import gleam/int
43
import gleam/io
@@ -63,11 +62,9 @@ pub fn pt_1(input: Input) {
6362
pub fn pt_2(input: Input) {
6463
let wider_grid = widen(input.grid)
6564
let wider_robot = grid.xy_mul(input.robot, #(2, 1))
66-
let #(final_grid, final_robot) =
65+
let #(final_grid, _) =
6766
input.moves
6867
|> list.fold(from: #(wider_grid, wider_robot), with: wider_step)
69-
//io.println_error("============== FINAL =============")
70-
//show2(final_grid, final_robot)
7168
final_grid
7269
|> grid.filter(fn(_, e) { e == BoxLeft })
7370
|> grid.keys
@@ -141,7 +138,6 @@ fn widen(grid: Grid(Entity)) -> Grid(Entity2) {
141138

142139
fn wider_step(state: #(Grid(Entity2), XY), dir: Dir) -> #(Grid(Entity2), XY) {
143140
let #(grid, robot) = state
144-
//show2(grid, robot)
145141
let next_xy = grid.step(robot, dir, 1)
146142
case grid.get(grid, next_xy) {
147143
Error(Nil) -> #(grid, next_xy)
@@ -309,33 +305,3 @@ fn left_in_dir(e: Entity2, xy: XY, dir: Dir) -> XY {
309305
_, _ -> panic as "diagonals? huh?"
310306
}
311307
}
312-
313-
fn show2(grid: Grid(Entity2), robot: XY) -> Nil {
314-
let without_robot =
315-
grid.to_string(
316-
grid,
317-
fn(e) {
318-
case e {
319-
Wall2 -> #("#", Error(Nil))
320-
BoxLeft -> #("[", Error(Nil))
321-
BoxRight -> #("]", Error(Nil))
322-
}
323-
},
324-
".",
325-
)
326-
let with_robot =
327-
without_robot
328-
|> string.split("\n")
329-
|> list.index_map(fn(s, i) {
330-
case i == robot.1 {
331-
True -> s |> extra.string_set(robot.0, "@")
332-
False -> s
333-
}
334-
})
335-
|> string.join("\n")
336-
io.println_error(" 0123456789")
337-
io.println_error(
338-
with_robot
339-
|> extra.add_line_numbers,
340-
)
341-
}

src/aoc_2024/day_16.gleam

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
import gleam/bool
2+
import gleam/dict.{type Dict}
3+
import gleam/int
4+
import gleam/io
5+
import gleam/list
6+
import gleam/option.{None, Some}
7+
import gleam/order
8+
import gleam/result
9+
import gleam/set.{type Set}
10+
import gleamy/priority_queue.{type Queue}
11+
import grid.{type Dir, type Grid, type XY}
12+
13+
pub type Input {
14+
Input(grid: Grid(Nil), reindeer: XY, exit: XY)
15+
}
16+
17+
pub fn parse(input: String) -> Input {
18+
let grid: Grid(Nil) =
19+
input
20+
|> grid.from_string
21+
|> grid.filter_map(fn(_, c) {
22+
case c {
23+
"#" -> Ok(Nil)
24+
_ -> Error(Nil)
25+
}
26+
})
27+
28+
Input(
29+
grid: grid,
30+
reindeer: #(grid.dims.min_x + 1, grid.dims.max_y - 1),
31+
exit: #(grid.dims.max_x - 1, grid.dims.min_y + 1),
32+
)
33+
}
34+
35+
pub fn pt_1(input: Input) {
36+
pt_1_aux(
37+
input,
38+
todos: priority_queue.from_list(
39+
[
40+
Todo(
41+
current_xy: input.reindeer,
42+
current_dir: grid.Right,
43+
positions: set.new(),
44+
cost: 0,
45+
),
46+
],
47+
compare_todo,
48+
),
49+
seen: dict.new(),
50+
)
51+
}
52+
53+
type Step {
54+
RotateCW
55+
RotateCCW
56+
Forward
57+
}
58+
59+
type Todo {
60+
Todo(current_xy: XY, current_dir: Dir, positions: Set(#(XY, Dir)), cost: Int)
61+
}
62+
63+
fn compare_todo(a: Todo, b: Todo) {
64+
int.compare(a.cost, b.cost)
65+
}
66+
67+
fn pt_1_neighbours(grid: Grid(Nil), xy: XY, dir: Dir) -> List(Step) {
68+
case grid.get(grid, grid.step(xy, dir, 1)) {
69+
Ok(Nil) -> [RotateCW, RotateCCW]
70+
Error(Nil) -> [RotateCW, RotateCCW, Forward]
71+
}
72+
}
73+
74+
fn apply_step(step: Step, xy: XY, dir: Dir) -> #(XY, Dir) {
75+
case step {
76+
RotateCW -> #(xy, grid.rotate_cw(dir))
77+
RotateCCW -> #(xy, grid.rotate_ccw(dir))
78+
Forward -> #(grid.step(xy, dir, 1), dir)
79+
}
80+
}
81+
82+
fn pt_1_aux(
83+
input: Input,
84+
todos todos: Queue(Todo),
85+
seen seen: Dict(#(XY, Dir), Int),
86+
) -> Result(Int, Nil) {
87+
use #(todo_, rest_of_todos) <- result.try(priority_queue.pop(todos))
88+
use <- bool.guard(
89+
when: todo_.current_xy == input.exit,
90+
return: Ok(todo_.cost),
91+
)
92+
let new_seen =
93+
dict.insert(seen, #(todo_.current_xy, todo_.current_dir), todo_.cost)
94+
let new_neighbours: List(Step) =
95+
pt_1_neighbours(input.grid, todo_.current_xy, todo_.current_dir)
96+
|> list.filter(fn(neighbour) {
97+
let neighbour_applied =
98+
apply_step(neighbour, todo_.current_xy, todo_.current_dir)
99+
case dict.get(seen, neighbour_applied) {
100+
Error(Nil) -> True
101+
Ok(previously_best_cost) ->
102+
previously_best_cost > todo_.cost + find_cost(neighbour)
103+
}
104+
})
105+
let added_todos =
106+
new_neighbours
107+
|> list.map(fn(neighbour) {
108+
let neighbour_applied =
109+
apply_step(neighbour, todo_.current_xy, todo_.current_dir)
110+
Todo(
111+
current_xy: neighbour_applied.0,
112+
current_dir: neighbour_applied.1,
113+
positions: set.insert(todo_.positions, neighbour_applied),
114+
cost: todo_.cost + find_cost(neighbour),
115+
)
116+
})
117+
let new_todos =
118+
list.fold(over: added_todos, from: rest_of_todos, with: priority_queue.push)
119+
pt_1_aux(input, todos: new_todos, seen: new_seen)
120+
}
121+
122+
fn find_cost(step: Step) -> Int {
123+
case step {
124+
RotateCCW -> 1000
125+
RotateCW -> 1000
126+
Forward -> 1
127+
}
128+
}
129+
130+
pub fn pt_2(input: Input) {
131+
let steps =
132+
pt_2_aux(
133+
input,
134+
todos: priority_queue.from_list(
135+
[
136+
Todo(
137+
current_xy: input.reindeer,
138+
current_dir: grid.Right,
139+
positions: set.new(),
140+
cost: 0,
141+
),
142+
],
143+
compare_todo,
144+
),
145+
seen: dict.new(),
146+
best: 109_496,
147+
)
148+
io.println_error(
149+
grid.to_string(input.grid, fn(xy, c) {
150+
case set.contains(steps, xy), c {
151+
True, Error(Nil) -> #("O", Error(Nil))
152+
True, Ok(Nil) -> panic as "walked over a wall?"
153+
False, Error(Nil) -> #(".", Error(Nil))
154+
False, Ok(Nil) -> #("#", Error(Nil))
155+
}
156+
}),
157+
)
158+
159+
set.size(steps)
160+
}
161+
162+
fn pt_2_final(exit: XY, seen: Dict(#(XY, Dir), #(Int, Set(XY)))) -> Set(XY) {
163+
case
164+
seen
165+
|> dict.filter(fn(k, _) { k.0 == exit })
166+
|> dict.values
167+
|> list.group(by: fn(kv) { kv.0 })
168+
|> dict.to_list
169+
|> list.sort(fn(a, b) { int.compare(a.0, b.0) })
170+
|> list.first
171+
{
172+
Ok(best) ->
173+
best.1
174+
|> list.flat_map(fn(x) {
175+
x.1
176+
|> set.to_list
177+
})
178+
|> set.from_list
179+
Error(Nil) -> panic as "no solution?"
180+
}
181+
}
182+
183+
fn pt_2_aux(
184+
input: Input,
185+
todos todos: Queue(Todo),
186+
seen seen: Dict(#(XY, Dir), #(Int, Set(XY))),
187+
best best: Int,
188+
) -> Set(XY) {
189+
case priority_queue.pop(todos) {
190+
Error(Nil) -> pt_2_final(input.exit, seen)
191+
Ok(#(todo_, rest_of_todos)) -> {
192+
case todo_.cost > best {
193+
True -> pt_2_aux(input, todos: rest_of_todos, seen: seen, best: best)
194+
False -> {
195+
case todo_.current_xy == input.exit {
196+
True -> {
197+
let new_seen =
198+
dict.upsert(
199+
seen,
200+
#(todo_.current_xy, todo_.current_dir),
201+
with: fn(existing) {
202+
case existing {
203+
None -> #(
204+
todo_.cost,
205+
set.map(todo_.positions, fn(x) { x.0 }),
206+
)
207+
Some(existing_) ->
208+
case int.compare(todo_.cost, existing_.0) {
209+
order.Lt -> #(
210+
todo_.cost,
211+
set.map(todo_.positions, fn(x) { x.0 }),
212+
)
213+
order.Eq -> #(
214+
todo_.cost,
215+
set.union(
216+
existing_.1,
217+
set.map(todo_.positions, fn(x) { x.0 }),
218+
),
219+
)
220+
order.Gt -> existing_
221+
}
222+
}
223+
},
224+
)
225+
io.debug(#(
226+
"found",
227+
todo_.cost,
228+
set.size(pt_2_final(input.exit, new_seen)),
229+
))
230+
let new_best = int.min(best, todo_.cost)
231+
pt_2_aux(
232+
input,
233+
todos: rest_of_todos,
234+
// |> list.filter(fn(t) { t.cost <= new_best }),
235+
seen: new_seen,
236+
best: new_best,
237+
)
238+
}
239+
False -> {
240+
case dict.get(seen, #(todo_.current_xy, todo_.current_dir)) {
241+
Ok(seen_) if seen_.0 < todo_.cost ->
242+
pt_2_aux(input, todos: rest_of_todos, seen: seen, best: best)
243+
_ -> {
244+
let new_neighbours: List(Step) =
245+
pt_1_neighbours(
246+
input.grid,
247+
todo_.current_xy,
248+
todo_.current_dir,
249+
)
250+
let added_todos =
251+
new_neighbours
252+
|> list.map(fn(neighbour) {
253+
let neighbour_applied =
254+
apply_step(
255+
neighbour,
256+
todo_.current_xy,
257+
todo_.current_dir,
258+
)
259+
Todo(
260+
current_xy: neighbour_applied.0,
261+
current_dir: neighbour_applied.1,
262+
positions: set.insert(
263+
todo_.positions,
264+
neighbour_applied,
265+
),
266+
cost: todo_.cost + find_cost(neighbour),
267+
)
268+
})
269+
let new_todos =
270+
list.fold(
271+
over: added_todos,
272+
from: rest_of_todos,
273+
with: priority_queue.push,
274+
)
275+
pt_2_aux(
276+
input,
277+
todos: new_todos,
278+
seen: dict.upsert(
279+
seen,
280+
#(todo_.current_xy, todo_.current_dir),
281+
with: fn(existing) {
282+
case existing {
283+
None -> #(todo_.cost, set.new())
284+
Some(existing_) -> #(
285+
todo_.cost,
286+
set.union(existing_.1, set.new()),
287+
)
288+
}
289+
},
290+
),
291+
best: best,
292+
)
293+
}
294+
}
295+
}
296+
}
297+
}
298+
}
299+
}
300+
}
301+
}

0 commit comments

Comments
 (0)