summaryrefslogtreecommitdiffstats
path: root/lib/day11.ml
blob: bd5ef285312a5757bf579240638cff9cbb4e4f1d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
(*
 * SPDX-FileCopyrightText: Copyright 2025 Alexandre Jesus <https://adbjesus.com>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *)

let parse ch =
  let parse_line s =
    match String.split_on_char ':' s with
    | [ u; vs ] ->
        let vs =
          String.trim vs |> String.split_on_char ' ' |> List.filter (( <> ) "")
        in
        (u, vs)
    | _ -> failwith ("Invalid line: " ^ s)
  in
  In_channel.input_lines ch
  |> List.filter (( <> ) "")
  |> List.map parse_line
  |> List.to_seq
  |> Hashtbl.of_seq

let solve ch source target over =
  (* this assumes the graph is a DAG *)
  let adj = parse ch in
  let len = List.length over in
  let over = List.to_seq over |> Seq.map (fun u -> (u, true)) |> Hashtbl.of_seq in
  let nodes =
    adj
    |> Hashtbl.to_seq
    |> Seq.flat_map (fun (u, vs) -> List.to_seq (u :: vs))
    |> List.of_seq
    |> List.sort_uniq compare
  in
  let rem = List.to_seq nodes |> Seq.map (fun n -> (n, 0)) |> Hashtbl.of_seq in
  let cntf () = List.to_seq nodes |> Seq.map (fun n -> (n, 0)) |> Hashtbl.of_seq in
  let cnt = Seq.forever cntf |> Seq.take (len + 1) |> Array.of_seq in
  let incrrem u = Hashtbl.replace rem u (Hashtbl.find rem u + 1) in
  let decrrem u =
    let nc = Hashtbl.find rem u - 1 in
    Hashtbl.replace rem u nc;
    nc
  in
  let incrcnt i u j v =
    Hashtbl.replace cnt.(i) u (Hashtbl.find cnt.(i) u + Hashtbl.find cnt.(j) v)
  in
  let rec fn q =
    match q with
    | [] -> Hashtbl.find cnt.(len) target
    | u :: q -> (
        match Hashtbl.find_opt adj u with
        | None -> fn q
        | Some vs ->
            List.fold_left
              (fun q v ->
                (if Hashtbl.mem over v then
                  for i = 1 to len do
                    incrcnt i v (i-1) u;
                  done
                else
                  for i = 0 to len do
                    incrcnt i v i u;
                  done);
                if decrrem v = 0 then v :: q else q)
              q vs
            |> fn)
  in
  Hashtbl.to_seq adj
  |> Seq.iter (fun (u, vs) -> List.iter (fun v -> incrrem v) vs);
  let q =
    Hashtbl.to_seq rem
    |> Seq.filter_map (fun (u, c) -> if c = 0 then Some u else None)
    |> List.of_seq
  in
  (if Hashtbl.mem over source then
    Hashtbl.replace cnt.(1) source 1
  else
    Hashtbl.replace cnt.(0) source 1);
  fn q

let part1 ch = solve ch "you" "out" [] |> Printf.printf "%d\n"
let part2 ch = solve ch "svr" "out" ["dac"; "fft"] |> Printf.printf "%d\n"