Skip to content

Commit d264fed

Browse files
committed
2024-18 * ** Gleam (A*)
1 parent 80b7637 commit d264fed

File tree

2 files changed

+104
-26
lines changed

2 files changed

+104
-26
lines changed

src/aoc_2024/day_18.gleam

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import extra
22
import gleam/dict
3-
import gleam/io
43
import gleam/list
54
import gleam/result
65
import gleam/string
7-
import grid.{type XY, Grid}
6+
import grid.{type Dims, type XY, Grid}
87

98
pub fn parse(input: String) -> List(XY) {
109
input
@@ -17,39 +16,52 @@ pub fn parse(input: String) -> List(XY) {
1716
})
1817
}
1918

20-
pub fn pt_1(input: List(XY)) {
21-
let size = 71
22-
// let size = 7
19+
// example: size 7, take 12
20+
const size = 71
21+
22+
pub fn pt_1(input: List(XY)) -> Int {
2323
let board = grid.dims_of_size(width: size, height: size)
24+
let taken = 1024
25+
let assert Ok(cost) = attempt(input, board, taken)
26+
cost
27+
}
28+
29+
pub fn pt_2(input: List(XY)) {
30+
let board = grid.dims_of_size(width: size, height: size)
31+
pt_2_loop(input, board, 1025)
32+
}
33+
34+
fn pt_2_loop(input: List(XY), board: Dims, taken: Int) -> XY {
35+
case attempt(input, board, taken) {
36+
Ok(_) -> pt_2_loop(input, board, taken + 1)
37+
Error(last) -> last
38+
}
39+
}
40+
41+
fn attempt(input: List(XY), board: Dims, taken: Int) -> Result(Int, XY) {
2442
let first_kb =
2543
input
26-
|> list.take(1024)
27-
// |> list.take(12)
44+
|> list.take(taken)
2845
|> list.filter(grid.in_dims(board, _))
2946
|> list.map(fn(xy) { #(xy, Nil) })
3047
let grid = Grid(dims: board, data: dict.new()) |> grid.set_all(first_kb)
31-
io.println_error(
32-
grid.to_string_simple(grid, fn(_, c) {
33-
case c {
34-
Ok(Nil) -> "#"
35-
Error(Nil) -> "."
36-
}
37-
}),
38-
)
39-
let assert Ok(path) =
40-
grid.shortest_path(
48+
let found_cost =
49+
grid.astar(
4150
from: #(0, 0),
42-
to: #(grid.dims.max_x, grid.dims.max_y),
51+
to: #(size - 1, size - 1),
4352
neighbours: fn(xy) {
4453
grid.neighbours(grid, xy, grid.orthogonal_dirs)
45-
|> list.filter(fn(kv) { grid.in_grid(grid, kv.0) })
4654
|> list.filter(fn(kv) { kv.1 |> result.is_error })
4755
|> list.map(fn(kv) { kv.0 })
4856
},
57+
cost: grid.manhattan_distance,
58+
print_progress: fn(_, _, _) { Nil },
4959
)
50-
path.1
51-
}
52-
53-
pub fn pt_2(input: List(XY)) {
54-
todo as "part 2 not implemented"
60+
case found_cost {
61+
Error(Nil) -> {
62+
let assert Ok(last) = input |> list.drop(taken - 1) |> list.first
63+
Error(last)
64+
}
65+
Ok(cost) -> Ok(cost)
66+
}
5567
}

src/grid.gleam

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ pub fn shortest_path(
348348
seen: dict.new(),
349349
todos: priority_queue.from_list(
350350
[ShortestPathTodo(origin, [], 0)],
351-
compare_todo,
351+
compare_shortest_path_todo,
352352
),
353353
neighbours: neighbours,
354354
)
@@ -402,7 +402,7 @@ fn shortest_path_aux(
402402
}
403403
}
404404

405-
fn compare_todo(a: ShortestPathTodo, b: ShortestPathTodo) {
405+
fn compare_shortest_path_todo(a: ShortestPathTodo, b: ShortestPathTodo) {
406406
int.compare(a.steps_count, b.steps_count)
407407
}
408408

@@ -691,3 +691,69 @@ pub fn dims_of_size(width width: Int, height height: Int) -> Dims {
691691
max_y: height - 1,
692692
)
693693
}
694+
695+
pub fn manhattan_distance(a: XY, b: XY) -> Int {
696+
int.absolute_value(a.0 - b.0) + int.absolute_value(a.1 - b.1)
697+
}
698+
699+
pub type AstarTodo {
700+
AstarTodo(xy: XY, cost: Int)
701+
}
702+
703+
fn compare_astar_todo(a: AstarTodo, b: AstarTodo) {
704+
int.compare(a.cost, b.cost)
705+
}
706+
707+
pub fn astar(
708+
from from: XY,
709+
to to: XY,
710+
cost cost: fn(XY, XY) -> Int,
711+
neighbours neighbours: fn(XY) -> List(XY),
712+
print_progress print_progress: fn(XY, List(XY), Queue(AstarTodo)) -> Nil,
713+
) -> Result(Int, Nil) {
714+
let frontier =
715+
priority_queue.new(compare_astar_todo)
716+
|> priority_queue.push(AstarTodo(from, 0))
717+
let cost_so_far: Dict(XY, Int) = dict.from_list([#(from, 0)])
718+
astar_aux(to, cost, neighbours, frontier, cost_so_far, print_progress)
719+
}
720+
721+
fn astar_aux(
722+
to goal: XY,
723+
cost cost: fn(XY, XY) -> Int,
724+
neighbours neighbours: fn(XY) -> List(XY),
725+
frontier frontier: Queue(AstarTodo),
726+
cost_so_far cost_so_far: Dict(XY, Int),
727+
print_progress print_progress: fn(XY, List(XY), Queue(AstarTodo)) -> Nil,
728+
) -> Result(Int, Nil) {
729+
use #(current, rest) <- result.try(priority_queue.pop(frontier))
730+
use <- bool.guard(when: current.xy == goal, return: Ok(current.cost))
731+
let nexts = neighbours(current.xy)
732+
print_progress(current.xy, nexts, frontier)
733+
let #(new_frontier, new_cost_so_far) =
734+
list.fold(over: nexts, from: #(rest, cost_so_far), with: fn(acc, next) {
735+
let #(acc_frontier, acc_cost_so_far) = acc
736+
let assert Ok(current_cost) = dict.get(acc_cost_so_far, current.xy)
737+
let new_next_cost = current_cost + cost(current.xy, next)
738+
case dict.get(acc_cost_so_far, next) {
739+
Ok(old_next_cost) if old_next_cost <= new_next_cost -> acc
740+
_ -> {
741+
let new_cost_so_far =
742+
dict.insert(acc_cost_so_far, next, new_next_cost)
743+
let priority = new_next_cost + manhattan_distance(next, goal)
744+
let new_frontier =
745+
acc_frontier
746+
|> priority_queue.push(AstarTodo(xy: next, cost: priority))
747+
#(new_frontier, new_cost_so_far)
748+
}
749+
}
750+
})
751+
astar_aux(
752+
goal,
753+
cost,
754+
neighbours,
755+
new_frontier,
756+
new_cost_so_far,
757+
print_progress,
758+
)
759+
}

0 commit comments

Comments
 (0)