🏡 index : ~doyle/aoc.git

#!/usr/bin/env nix-shell

(*
#!nix-shell --pure -i ocaml -p ocaml
*)

let input =
  let rec aux () =
    try
      let parsed_line =
        read_line () |> String.to_seq
        |> Seq.map (String.make 1)
        |> Seq.map int_of_string |> List.of_seq
      in
      parsed_line :: aux ()
    with End_of_file -> []
  in
  aux ()

let input' = input |> List.map Array.of_list |> Array.of_list

let is_valid (x, y) =
  x >= 0 && y >= 0 && y < Array.length input' && x < Array.length input'.(0)

let trailheads =
  let rec aux x y = function
    | [] ->
        []
    | [] :: rows ->
        aux 0 (y + 1) rows
    | (0 :: cols) :: rows ->
        (x, y) :: aux (x + 1) y (cols :: rows)
    | (_ :: cols) :: rows ->
        aux (x + 1) y (cols :: rows)
  in
  aux 0 0 input

module RatingMap = Map.Make (struct
  type t = int * int

  let compare = compare
end)

let ratings =
  let rec find_paths curr (x, y) =
    if curr = 9 then RatingMap.singleton (x, y) 1
    else
      let next = curr + 1 in
      [(x, y - 1); (x, y + 1); (x - 1, y); (x + 1, y)]
      |> List.filter is_valid
      |> List.filter (fun (x, y) -> input'.(y).(x) = next)
      |> List.map (find_paths next)
      |> List.fold_left
           (RatingMap.union (fun _ a b -> Some (a + b)))
           RatingMap.empty
  in
  List.map (find_paths 0) trailheads

let part1 =
  List.fold_left (fun acc score -> acc + RatingMap.cardinal score) 0 ratings

let _ = print_int part1

let _ = print_newline ()

let part2 =
  List.fold_left
    (fun acc score ->
      acc + RatingMap.fold (fun _ value acc -> value + acc) score 0 )
    0 ratings

let _ = print_int part2

let _ = print_newline ()