#!nix-shell --pure -i ocaml -p ocaml
*)
let input =
let rec aux () =
try
let parsed_line = read_line () |> String.to_seq |> List.of_seq in
parsed_line :: aux ()
with End_of_file -> []
in
aux () |> List.filter (fun v -> List.length v > 0)
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 points =
let rec walk_matching c x y visited =
if (not (List.mem (x, y) visited)) && is_valid (x, y) && input'.(y).(x) == c
then
(x, y) :: visited
|> walk_matching c (x - 1) y
|> walk_matching c (x + 1) y
|> walk_matching c x (y + 1)
|> walk_matching c x (y - 1)
else visited
in
let rec aux x y visited = function
| [] ->
[]
| [] :: rows ->
aux 0 (y + 1) visited rows
| (c :: cols) :: rows ->
if List.mem (x, y) visited then aux (x + 1) y visited (cols :: rows)
else
let matching = walk_matching c x y [] in
matching :: aux (x + 1) y (matching @ visited) (cols :: rows)
in
aux 0 0 [] input
let has_neighbour points (x, y) (dx, dy) =
let neighbour = (x + dx, y + dy) in
List.exists (( = ) neighbour) points
let fetch_neighbours points (x, y) =
[(0, 1); (1, 0); (0, -1); (-1, 0)]
|> List.filter (has_neighbour points (x, y))
let count_shared_edges points =
List.fold_left
(fun acc (x, y) -> acc + (fetch_neighbours points (x, y) |> List.length))
0 points
let count_corners points =
let is_corner (x, y) ((dy1, dx1), (dy2, dx2)) =
let left_match = has_neighbour points (x, y) (dx1, dy1) in
let right_match = has_neighbour points (x, y) (dx2, dy2) in
let mid_match = has_neighbour points (x, y) (dx1 + dx2, dy1 + dy2) in
((not left_match) && not right_match)
|| (left_match && right_match && not mid_match)
in
let count_corners (x, y) =
[((0, -1), (1, 0)); ((1, 0), (0, 1)); ((0, 1), (-1, 0)); ((-1, 0), (0, -1))]
|> List.filter (is_corner (x, y))
|> List.length
in
List.fold_left (fun acc (x, y) -> acc + count_corners (x, y)) 0 points
let part1 =
let calc points =
let area = List.length points in
let perimeter = (4 * area) - count_shared_edges points in
area * perimeter
in
points |> List.map calc |> List.fold_left ( + ) 0
let _ = part1 |> print_int |> print_newline
let part2 =
let calc points = List.length points * count_corners points in
points |> List.map calc |> List.fold_left ( + ) 0
let _ = part2 |> print_int |> print_newline