Comment and tidy up 2024 day 18

This commit is contained in:
2024-12-18 10:17:56 +00:00
parent dcdd2bab4d
commit fe94d8b371

View File

@@ -3,12 +3,6 @@ let pairs_of_ints = function
| [ h; h' ] -> (h, h') | [ h; h' ] -> (h, h')
| _ -> raise (Invalid_argument "pairs_of_ints") | _ -> raise (Invalid_argument "pairs_of_ints")
(** [pairs_of_file fname] returns a list of pairs from [fname]. *)
let pairs_of_file fname =
Aoc.strings_of_file fname
|> List.map (Aoc.ints_of_string ~sep:",")
|> List.map pairs_of_ints |> List.to_seq
(** [dijkstra visit check_end states] executes Dijkstra's algorithm. (** [dijkstra visit check_end states] executes Dijkstra's algorithm.
[visit cost state] is called to visit [state] with [cost]. It should mark [visit cost state] is called to visit [state] with [cost]. It should mark
@@ -35,74 +29,105 @@ let rec dijkstra visit check_end =
let new_states = visit cost state |> List.merge compare_costs t in let new_states = visit cost state |> List.merge compare_costs t in
dijkstra visit check_end new_states dijkstra visit check_end new_states
type grid = { grid : bool array; width : int } type 'a grid = { grid : 'a array; width : int }
(** [grid_is_valid_pos grid (x, y)] returns true if (x, y) is a valid position
*)
let grid_is_valid_pos grid (x, y) = let grid_is_valid_pos grid (x, y) =
x >= 0 && x < grid.width && y >= 0 && y < grid.width x >= 0 && x < grid.width && y >= 0 && y < grid.width
(** Get the index into the grid from an x, y position. *)
let grid_idx_by_pos grid (x, y) = x + (y * grid.width) let grid_idx_by_pos grid (x, y) = x + (y * grid.width)
(** Set the value of the position (x, y) to v in grid. *)
let grid_set_by_pos grid p v = let grid_set_by_pos grid p v =
assert (grid_is_valid_pos grid p); assert (grid_is_valid_pos grid p);
let idx = grid_idx_by_pos grid p in let idx = grid_idx_by_pos grid p in
grid.grid.(idx) <- v grid.grid.(idx) <- v
(** Get the value of the position (x, y) in grid. *)
let grid_get_by_pos grid p = let grid_get_by_pos grid p =
assert (grid_is_valid_pos grid p); assert (grid_is_valid_pos grid p);
let idx = grid_idx_by_pos grid p in let idx = grid_idx_by_pos grid p in
grid.grid.(idx) grid.grid.(idx)
(** [grid_of_rocks w rocks] returns a [w * w] grid with [grid.(x + y * w)]
indicating whether the space is empty ([=max_int]) or which rock it is (0
based). *)
let grid_of_rocks width rocks = let grid_of_rocks width rocks =
let grid = { grid = Array.make (width * width) false; width } in let grid = { grid = Array.make (width * width) max_int; width } in
let rec impl rocks = let add_rock idx p = grid_set_by_pos grid p idx in
match Seq.uncons rocks with List.iteri add_rock rocks;
| None -> grid grid
| Some (p, t) ->
grid_set_by_pos grid p true;
impl t
in
impl rocks
let visit grid has_visited cost state = (** [visit grid has_visited count cost pos] visits the location pos marking it
as visited and returning a list of [(cost, pos)] pairs of next locations to
examine.
[grid] is the grid of rocks, [has_visited] is an array of bools indicating
whether a position has already been visited, and [count] is how many rocks
have fallen. *)
let visit grid has_visited count cost state =
if not (grid_is_valid_pos grid state) then [] if not (grid_is_valid_pos grid state) then []
else if has_visited.(grid_idx_by_pos grid state) then [] else if has_visited.(grid_idx_by_pos grid state) then []
else if grid_get_by_pos grid state then [] else if grid_get_by_pos grid state < count then []
else else
let x, y = state in let x, y = state in
has_visited.(grid_idx_by_pos grid state) <- true; has_visited.(grid_idx_by_pos grid state) <- true;
[ [
(cost + 1, (x + 1, y)); (cost + 1, (x + 1, y));
(cost + 1, (x, y + 1));
(cost + 1, (x - 1, y)); (cost + 1, (x - 1, y));
(cost + 1, (x, y + 1));
(cost + 1, (x, y - 1)); (cost + 1, (x, y - 1));
] ]
let width = 71 (** [grid_of_file w fname] returns a grid of width & height [w] populated with
rocks described in the file [fname]. *)
let grid_of_file width fname =
Aoc.strings_of_file fname
|> List.map (Aoc.ints_of_string ~sep:",")
|> List.map pairs_of_ints |> grid_of_rocks width
let common count rocks = (** [find_route_length count grid] calculates the route from the top-left
let grid = rocks |> Seq.take count |> grid_of_rocks width in position in [grid] to the bottom right if [count] rocks have fallen. It
let has_visited = Array.make (width * width) false in returns [None] if no route is possible or [Some (cost, pos)] if the route is
dijkstra (visit grid has_visited) possible. *)
(( = ) (width - 1, width - 1)) let find_route_length count grid =
let has_visited = Array.make (Array.length grid.grid) false in
dijkstra
(visit grid has_visited count)
(( = ) (grid.width - 1, grid.width - 1))
[ (0, (0, 0)) ] [ (0, (0, 0)) ]
(** [part1 count rocks] returns how long it takes to navigate the grid [rocks]
when [count] rocks have fallen. *)
let part1 count rocks = let part1 count rocks =
match common count rocks with match find_route_length count rocks with
| None -> failwith "part1" | None -> failwith "part1"
| Some (cost, _) -> cost | Some (cost, _) -> cost
let part2 start_count rocks = (** [part2 start_count grid] returns the location of the first rock to fall into
[grid] which makes it impossible to get from the top-left to bottom-right.
*)
let part2 start_count grid =
let rec impl count = let rec impl count =
match common count rocks with None -> count | Some _ -> impl (count + 1) match find_route_length count grid with
| None -> count
| Some _ -> impl (count + 1)
in in
let count = impl start_count in let count = impl start_count in
match Seq.uncons (Seq.drop (count - 1) rocks) with match Array.find_index (( = ) (count - 1)) grid.grid with
| None -> failwith "part2" | None -> failwith "part2"
| Some ((x, y), _) -> x + (y * width) | Some idx -> idx
(** [string_of_idx width idx] prints the (x, y) location for a given index in a
grid. *)
let string_of_idx width idx = let string_of_idx width idx =
Printf.sprintf "%d,%d" (idx mod width) (idx / width) Printf.sprintf "%d,%d" (idx mod width) (idx / width)
(** Width of grid *)
let width = 71
let _ = let _ =
Aoc.main pairs_of_file Aoc.main (grid_of_file width)
[ (string_of_int, part1 1024); (string_of_idx width, part2 1024) ] [ (string_of_int, part1 1024); (string_of_idx width, part2 1024) ]