99 lines
3.4 KiB
OCaml
99 lines
3.4 KiB
OCaml
open Printf;;
|
|
|
|
let rec list_of_lines in_file =
|
|
try
|
|
let line = input_line in_file in
|
|
line :: list_of_lines(in_file)
|
|
with End_of_file ->
|
|
close_in in_file;
|
|
[]
|
|
|
|
let file_to_2d_array in_file =
|
|
let lines = list_of_lines in_file in
|
|
let size_x = String.length @@ List.hd lines in
|
|
let size_y = List.length lines in
|
|
let arr = Array.make_matrix size_x size_y '?' in
|
|
List.iteri (fun j line -> String.iteri (fun i c -> arr.(j).(i) <- c) line) lines;
|
|
arr
|
|
|
|
let get_neighbours_bitmask list8 =
|
|
list8
|
|
|> List.mapi (fun i x -> (i, x))
|
|
|> List.fold_left (fun acc (i, x) -> Int.logor acc (Int.shift_left (if x then 1 else 0) i)) 0
|
|
|
|
let rotate_8bit_left_twice mask =
|
|
let x = Int.shift_left mask 2 in
|
|
Int.logor (Int.logand x 0xff) (Int.shift_right x 8)
|
|
|
|
let count_corners_no_rotation bitmask =
|
|
let corner_bitmask = Int.logand bitmask 0x07 in
|
|
if corner_bitmask = 0x00 || corner_bitmask = 0x5 || corner_bitmask = 0x2 then 1 else 0
|
|
|
|
let rec multiple_application_list f arg n =
|
|
if n <= 0 then
|
|
[arg]
|
|
else
|
|
arg :: multiple_application_list f (f arg) (n - 1)
|
|
|
|
let count_corners bitmask =
|
|
multiple_application_list rotate_8bit_left_twice bitmask 3
|
|
|> List.map count_corners_no_rotation
|
|
|> List.fold_left Int.add 0
|
|
|
|
let is_same_value value arr (x, y) =
|
|
try
|
|
arr.(y).(x) = value
|
|
with Invalid_argument _ -> false
|
|
|
|
let add_int_triplet (a1, a2, a3) (b1, b2, b3) =
|
|
(a1 + b1, a2 + b2, a3 + b3)
|
|
|
|
let get_even_index_elements lst =
|
|
lst
|
|
|> List.mapi (fun i x -> (i, x))
|
|
|> List.filter_map (fun (i, x) -> if i mod 2 = 0 then Some x else None)
|
|
|
|
let rec grow_and_get_region_parameters arr visited (x, y) =
|
|
if visited.(y).(x) then
|
|
(0, 0, 0)
|
|
else begin
|
|
visited.(y).(x) <- true;
|
|
let value = arr.(y).(x) in
|
|
let neighbours8_coords = [(x, y - 1); (x + 1, y - 1); (x + 1, y); (x + 1, y + 1); (x, y + 1); (x - 1, y + 1); (x - 1, y); (x - 1, y - 1)] in
|
|
let neighbours8_same = List.map (is_same_value value arr) neighbours8_coords in
|
|
let corners = count_corners @@ get_neighbours_bitmask neighbours8_same in
|
|
let neighbours4_coords = get_even_index_elements neighbours8_coords in
|
|
let neighbours4_same = get_even_index_elements neighbours8_same in
|
|
let sides = List.fold_left (fun acc x -> acc - if x then 1 else 0) 4 neighbours4_same in
|
|
Seq.zip (List.to_seq neighbours4_same) (List.to_seq neighbours4_coords)
|
|
|> Seq.filter_map (fun (same, coords) -> if same then Some coords else None)
|
|
|> Seq.map (grow_and_get_region_parameters arr visited)
|
|
|> Seq.fold_left add_int_triplet (1, sides, corners)
|
|
end
|
|
|
|
let find_all_regions_cost arr =
|
|
let size_x = Array.length arr.(0) in
|
|
let size_y = Array.length arr in
|
|
let visited = Array.make_matrix size_x size_y false in
|
|
let regions = ref [] in
|
|
for j = 0 to (size_y - 1) do
|
|
for i = 0 to (size_x - 1) do
|
|
if not visited.(j).(i) then
|
|
let parameters = grow_and_get_region_parameters arr visited (i, j) in
|
|
regions := parameters :: !regions
|
|
done
|
|
done;
|
|
let price1 = !regions
|
|
|> List.map (fun (area, sides, _) -> area * sides)
|
|
|> List.fold_left Int.add 0 in
|
|
let price2 = !regions
|
|
|> List.map (fun (area, _, corners) -> area * corners)
|
|
|> List.fold_left Int.add 0 in
|
|
(price1, price2)
|
|
|
|
let () =
|
|
let f = open_in "input.txt" in
|
|
let arr = file_to_2d_array f in
|
|
let result, result2 = find_all_regions_cost arr in
|
|
printf "%d\n%d\n" result result2
|