(* * SPDX-FileCopyrightText: Copyright 2025 Alexandre Jesus * * SPDX-License-Identifier: GPL-3.0-or-later *) let parse_data ch = In_channel.input_all ch |> String.trim |> String.split_on_char '\n' |> List.mapi (fun i s -> (i, s)) |> List.to_seq |> Seq.flat_map (fun (i, s) -> String.to_seq s |> Seq.mapi (fun j v -> (j, v = '@')) |> Seq.filter_map (fun (j, p) -> if p then Some ((i, j), 0) else None)) |> Hashtbl.of_seq let neighs = [ (-1, -1); (-1, 0); (-1, 1); (0, -1); (0, 1); (1, -1); (1, 0); (1, 1) ] let neighs (i, j) = List.map (fun (a, b) -> (i + a, j + b)) neighs let count tbl k = (k, neighs k |> List.filter (Hashtbl.mem tbl) |> List.length) let counts tbl = Hashtbl.to_seq_keys tbl |> Seq.map (count tbl) let part1 ch = parse_data ch |> counts |> Seq.filter (fun (_, v) -> v < 4) |> Seq.length |> Printf.printf "%d\n" let part2 ch = let tbl = parse_data ch |> counts |> Hashtbl.of_seq in let rec fn acc s = let test k = match Hashtbl.find_opt tbl k with | None -> None | Some v when v = 4 -> Hashtbl.remove tbl k; Some k | Some v -> Hashtbl.replace tbl k (v - 1); None in match s with | [] -> acc | h :: t -> neighs h |> List.filter_map test |> List.fold_left (fun (acc, s) k -> (acc + 1, k :: s)) (acc, t) |> fun (acc, s) -> fn acc s in Hashtbl.to_seq tbl |> List.of_seq |> List.filter_map (fun (k, v) -> if v < 4 then ( Hashtbl.remove tbl k; Some k) else None) |> (fun s -> fn (List.length s) s) |> Printf.printf "%d\n"