Files
ocaml-aoc/bin/day2418.ml

109 lines
3.4 KiB
OCaml

(** [pairs_of_ints lst] returns a pair from a list of two elements. *)
let pairs_of_ints = function
| [ h; h' ] -> (h, h')
| _ -> 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.
[visit cost state] is called to visit [state] with [cost]. It should mark
[state] as visited, and return a list of [(cost, state)] pairs which contain
new states to examine. The returned list should be sorted by [cost].
[check_end state] should return [true] if and only if [state] is an end
state.
[states] is a list of [(cost, state)] pairs ordered by [cost].
[dijkstra] returns [None] if no path is found to the destination. It returns
[Some (cost, state, remaining_states)] if a route is found. [cost] is the
cost of getting to [state]. [remaining_states] is a list of the remaining
states which can be passed back to [dijkstra] if we want to find further
paths. *)
let rec dijkstra visit check_end =
let compare_costs (lhs, _) (rhs, _) = compare lhs rhs in
function
| [] -> None
| (cost, state) :: t ->
if check_end state then Some (cost, state)
else
let new_states = visit cost state |> List.merge compare_costs t in
dijkstra visit check_end new_states
type grid = { grid : bool array; width : int }
let grid_is_valid_pos grid (x, y) =
x >= 0 && x < grid.width && y >= 0 && y < grid.width
let grid_idx_by_pos grid (x, y) = x + (y * grid.width)
let grid_set_by_pos grid p v =
assert (grid_is_valid_pos grid p);
let idx = grid_idx_by_pos grid p in
grid.grid.(idx) <- v
let grid_get_by_pos grid p =
assert (grid_is_valid_pos grid p);
let idx = grid_idx_by_pos grid p in
grid.grid.(idx)
let grid_of_rocks width rocks =
let grid = { grid = Array.make (width * width) false; width } in
let rec impl rocks =
match Seq.uncons rocks with
| None -> grid
| Some (p, t) ->
grid_set_by_pos grid p true;
impl t
in
impl rocks
let visit grid has_visited cost state =
if not (grid_is_valid_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
let x, y = state in
has_visited.(grid_idx_by_pos grid state) <- true;
[
(cost + 1, (x + 1, y));
(cost + 1, (x, y + 1));
(cost + 1, (x - 1, y));
(cost + 1, (x, y - 1));
]
let width = 71
let common count rocks =
let grid = rocks |> Seq.take count |> grid_of_rocks width in
let has_visited = Array.make (width * width) false in
dijkstra (visit grid has_visited)
(( = ) (width - 1, width - 1))
[ (0, (0, 0)) ]
let part1 count rocks =
match common count rocks with
| None -> failwith "part1"
| Some (cost, _) -> cost
let part2 start_count rocks =
let rec impl count =
match common count rocks with None -> count | Some _ -> impl (count + 1)
in
let count = impl start_count in
match Seq.uncons (Seq.drop (count - 1) rocks) with
| None -> failwith "part2"
| Some ((x, y), _) -> x + (y * width)
let string_of_idx width idx =
Printf.sprintf "%d,%d" (idx mod width) (idx / width)
let _ =
Aoc.main pairs_of_file
[ (string_of_int, part1 1024); (string_of_idx width, part2 1024) ]