Remove mutable state from day 8 of 2024.

This commit is contained in:
2024-12-08 09:15:45 +00:00
parent b9af6249a3
commit 6d551f5497

View File

@@ -10,63 +10,74 @@ let map_of_file fname =
let map_length map = String.length map.map
let map_get_by_idx map idx = map.map.[idx]
let map_pos_of_idx map idx = (idx mod map.width, idx / map.width)
let map_idx_of_pos map (x, y) = x + (y * map.width)
let map_is_valid_pos map (x, y) =
x >= 0 && x < map.width && y >= 0 && y < map.height
module CharMap = Map.Make (Char)
(** [get_station_indices map] returns a list of pairs mapping station ID to the
indices in [map] where there is a station with that ID. *)
let get_station_indices map =
let arr = Array.make 256 [] in
let rec impl idx =
if idx >= map_length map then arr
else if map_get_by_idx map idx = '.' then impl (idx + 1)
let rec impl acc idx =
if idx >= map_length map then acc
else if map_get_by_idx map idx = '.' then impl acc (idx + 1)
else
let station = int_of_char (map_get_by_idx map idx) in
arr.(station) <- idx :: arr.(station);
impl (idx + 1)
let station = map_get_by_idx map idx in
let update_fn lst =
match lst with None -> Some [ idx ] | Some t -> Some (idx :: t)
in
impl 0
impl (CharMap.update station update_fn acc) (idx + 1)
in
impl CharMap.empty 0 |> CharMap.to_list
let rec get_antinodes1 acc map p =
let px, py = map_pos_of_idx map p in
function
| [] -> acc
| h :: t ->
let hx, hy = map_pos_of_idx map h in
let dx = hx - px in
let dy = hy - py in
get_antinodes1 ((px - dx, py - dy) :: (hx + dx, hy + dy) :: acc) map p t
(** Generate antinodes for part 1. *)
let get_antinodes1 acc _ (px, py) (px', py') =
let dx = px' - px in
let dy = py' - py in
(px - dx, py - dy) :: (px' + dx, py' + dy) :: acc
(** [add_antinodes lst map pos vel] adds antinodes at [pos + n * vel] to [lst]
for all non-negative [n] that are valid positions in [map]. *)
let rec add_antinodes lst map (x, y) (dx, dy) =
if map_is_valid_pos map (x, y) then
add_antinodes ((x, y) :: lst) map (x + dx, y + dy) (dx, dy)
else lst
let rec get_antinodes2 acc map p =
let px, py = map_pos_of_idx map p in
function
| [] -> acc
| h :: t ->
let hx, hy = map_pos_of_idx map h in
let dx = hx - px in
let dy = hy - py in
(** Generate antinodes for part 2. *)
let get_antinodes2 acc map (px, py) (px', py') =
let dx = px' - px in
let dy = py' - py in
let acc' = add_antinodes acc map (px, py) (dx, dy) in
let acc'' = add_antinodes acc' map (px, py) (-dx, -dy) in
get_antinodes2 acc'' map p t
acc''
(** [process_stations map fn stations] generates a list of all antinodes for the
stations in [stations] on the map [map]. [fn acc map p p'] is called to
generate the antinode list for each pair of stations [p] and [p']. It should
add the positions of antinodes to the list [acc]. *)
let process_stations map fn stations =
let rec impl acc = function [] -> acc | h :: t -> impl (fn acc map h t) t in
Array.map
(fun x ->
impl [] x
|> List.filter (map_is_valid_pos map)
|> List.map (map_idx_of_pos map))
stations
let rec impl2 acc p t =
match t with
| [] -> acc
| h :: t -> impl2 (fn acc map p (map_pos_of_idx map h)) p t
in
let rec impl acc = function
| [] -> acc
| h :: t -> impl (impl2 acc (map_pos_of_idx map h) t) t
in
List.map (impl []) stations
(** [part antifn map station_indices] process all the stations in
station_indices calling [antifn acc map p p'] on all stations. Here [acc] is
a list of antinodes which [antifn] should update and return, [p] and [p']
are positions of stations to generate antinodes for. *)
let part antifn map =
get_station_indices map
|> List.map snd (* we do not care about the station IDs *)
|> process_stations map antifn
|> Array.fold_left List.append []
|> List.concat
|> List.filter (map_is_valid_pos map)
|> List.sort_uniq Stdlib.compare
|> List.length