diff --git a/bin/day2408.ml b/bin/day2408.ml index 0ee79c2..3d33db0 100644 --- a/bin/day2408.ml +++ b/bin/day2408.ml @@ -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 (CharMap.update station update_fn acc) (idx + 1) in - impl 0 + 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 - 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 +(** 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 + 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