Compare commits
2 Commits
46755cea34
...
932b2c926c
Author | SHA1 | Date | |
---|---|---|---|
932b2c926c | |||
7e0e6d3770 |
@@ -1,35 +1,75 @@
|
|||||||
let find_regions grid =
|
(** [perimeter grid pos] returns the number of sides of [pos] that contribute to
|
||||||
let working = Array.make (Aoc.Grid.length grid) ~-1 in
|
the perimeter of the region that [pos] is part of in [grid]. *)
|
||||||
let add_pos region pos perimeter lst =
|
let perimeter grid (x, y) =
|
||||||
if Aoc.Grid.pos_is_valid grid pos && Aoc.Grid.get_by_pos grid pos = region
|
let region = Aoc.Grid.get_by_pos grid (x, y) in
|
||||||
then (Aoc.Grid.idx_of_pos grid pos :: lst, perimeter - 1)
|
let check (dx, dy) =
|
||||||
else (lst, perimeter)
|
Aoc.Grid.get_by_pos_opt grid (x + dx, y + dy) = Some region
|
||||||
in
|
in
|
||||||
let rec scan_pos perimeter area id = function
|
[ (-1, 0); (1, 0); (0, 1); (0, -1) ]
|
||||||
|
|> List.map check |> List.map Bool.to_int |> List.fold_left ( + ) 0
|
||||||
|
|
||||||
|
(** [is_corner grid pos dir] returns true if there is a region corner on the
|
||||||
|
[dir] side of [pos] in grid. *)
|
||||||
|
let is_corner grid (x, y) (dx, dy) =
|
||||||
|
let region = Aoc.Grid.get_by_pos grid (x, y) in
|
||||||
|
if
|
||||||
|
Aoc.Grid.get_by_pos_opt grid (x + dx, y) <> Some region
|
||||||
|
&& Aoc.Grid.get_by_pos_opt grid (x, y + dy) <> Some region
|
||||||
|
then true
|
||||||
|
else if
|
||||||
|
Aoc.Grid.get_by_pos_opt grid (x + dx, y) = Some region
|
||||||
|
&& Aoc.Grid.get_by_pos_opt grid (x, y + dy) = Some region
|
||||||
|
&& Aoc.Grid.get_by_pos_opt grid (x + dx, y + dy) <> Some region
|
||||||
|
then true
|
||||||
|
else false
|
||||||
|
|
||||||
|
(** [corners grid pos] returns the count of the number of corners that [pos] is
|
||||||
|
on for its region in [grid]. *)
|
||||||
|
let corners grid pos =
|
||||||
|
[ (-1, -1); (-1, 1); (1, 1); (1, -1) ]
|
||||||
|
|> List.map (is_corner grid pos)
|
||||||
|
|> List.map Bool.to_int |> List.fold_left ( + ) 0
|
||||||
|
|
||||||
|
(** [find_regions calc grid] finds all the regions in [grid] and returns a list
|
||||||
|
containing an element for each region. The element is a pair [(p, a)] where
|
||||||
|
[p] is the sum of calling [calc grid pos] for every element in the region
|
||||||
|
and [a] is the area of the region. *)
|
||||||
|
let find_regions calc grid =
|
||||||
|
let visited = Array.make (Aoc.Grid.length grid) false in
|
||||||
|
let add_pos region pos lst =
|
||||||
|
if Aoc.Grid.get_by_pos_opt grid pos = Some region then
|
||||||
|
Aoc.Grid.idx_of_pos grid pos :: lst
|
||||||
|
else lst
|
||||||
|
in
|
||||||
|
let rec scan_pos perimeter area = function
|
||||||
| [] -> (perimeter, area)
|
| [] -> (perimeter, area)
|
||||||
| idx :: t when working.(idx) <> -1 -> scan_pos perimeter area id t
|
| idx :: t when visited.(idx) -> scan_pos perimeter area t
|
||||||
| idx :: t ->
|
| idx :: t (* when not visited.(idx) *) ->
|
||||||
working.(idx) <- id;
|
visited.(idx) <- true;
|
||||||
let x, y = Aoc.Grid.pos_of_idx grid idx in
|
let x, y = Aoc.Grid.pos_of_idx grid idx in
|
||||||
let area = succ area in
|
let area = area + 1 in
|
||||||
let perimeter = perimeter + 4 in
|
let perimeter = perimeter + calc grid (x, y) in
|
||||||
let region = Aoc.Grid.get_by_idx grid idx in
|
let region = Aoc.Grid.get_by_idx grid idx in
|
||||||
let t, perimeter = add_pos region (x - 1, y) perimeter t in
|
let t = add_pos region (x - 1, y) t in
|
||||||
let t, perimeter = add_pos region (x + 1, y) perimeter t in
|
let t = add_pos region (x + 1, y) t in
|
||||||
let t, perimeter = add_pos region (x, y - 1) perimeter t in
|
let t = add_pos region (x, y - 1) t in
|
||||||
let t, perimeter = add_pos region (x, y + 1) perimeter t in
|
let t = add_pos region (x, y + 1) t in
|
||||||
scan_pos perimeter area id t
|
scan_pos perimeter area t
|
||||||
in
|
in
|
||||||
let rec impl acc idx id =
|
let rec impl acc idx =
|
||||||
if idx >= Aoc.Grid.length grid then List.rev acc
|
if idx >= Aoc.Grid.length grid then List.rev acc
|
||||||
else
|
else
|
||||||
let perimeter, area = scan_pos 0 0 id [ idx ] in
|
let perimeter, area = scan_pos 0 0 [ idx ] in
|
||||||
if area = 0 then impl acc (idx + 1) id
|
if area = 0 then impl acc (idx + 1)
|
||||||
else impl ((perimeter, area) :: acc) (idx + 1) (id + 1)
|
else impl ((perimeter, area) :: acc) (idx + 1)
|
||||||
in
|
in
|
||||||
impl [] 0 0
|
impl [] 0
|
||||||
|
|
||||||
let part1 grid =
|
(** [part calc grid] returns the result of Part N over [grid] where [calc] is
|
||||||
find_regions grid |> List.fold_left (fun acc (p, a) -> acc + (p * a)) 0
|
the perimeter/edge counting function. *)
|
||||||
|
let part calc grid =
|
||||||
|
find_regions calc grid |> List.fold_left (fun acc (p, a) -> acc + (p * a)) 0
|
||||||
|
|
||||||
let _ = Aoc.main Aoc.Grid.of_file [ (string_of_int, part1) ]
|
let _ =
|
||||||
|
Aoc.main Aoc.Grid.of_file
|
||||||
|
[ (string_of_int, part perimeter); (string_of_int, part corners) ]
|
||||||
|
@@ -54,12 +54,16 @@ module Grid = struct
|
|||||||
let length grid = String.length grid.grid
|
let length grid = String.length grid.grid
|
||||||
let pos_of_idx grid idx = (idx mod grid.width, idx / grid.width)
|
let pos_of_idx grid idx = (idx mod grid.width, idx / grid.width)
|
||||||
let idx_of_pos grid (x, y) = x + (y * grid.width)
|
let idx_of_pos grid (x, y) = x + (y * grid.width)
|
||||||
let get_by_idx grid idx = grid.grid.[idx]
|
|
||||||
let get_by_pos grid pos = get_by_idx grid (idx_of_pos grid pos)
|
|
||||||
|
|
||||||
let pos_is_valid grid (x, y) =
|
let pos_is_valid grid (x, y) =
|
||||||
x >= 0 && x < grid.width && y >= 0 && y < grid.height
|
x >= 0 && x < grid.width && y >= 0 && y < grid.height
|
||||||
|
|
||||||
|
let get_by_idx grid idx = grid.grid.[idx]
|
||||||
|
let get_by_pos grid pos = get_by_idx grid (idx_of_pos grid pos)
|
||||||
|
|
||||||
|
let get_by_pos_opt grid pos =
|
||||||
|
if pos_is_valid grid pos then Some (get_by_pos grid pos) else None
|
||||||
|
|
||||||
let idx_from_opt grid = String.index_from_opt grid.grid
|
let idx_from_opt grid = String.index_from_opt grid.grid
|
||||||
|
|
||||||
let update_pos grid pos c =
|
let update_pos grid pos c =
|
||||||
|
@@ -114,6 +114,10 @@ module Grid : sig
|
|||||||
(** [Grid.get_by_pos grid pos] returns the character at position [pos] in
|
(** [Grid.get_by_pos grid pos] returns the character at position [pos] in
|
||||||
[grid]. *)
|
[grid]. *)
|
||||||
|
|
||||||
|
val get_by_pos_opt : t -> int * int -> char option
|
||||||
|
(** [Grid.get_by_pos_opt grid pos] returns [Some (get_by_pos grid pos)] if
|
||||||
|
[pos] is a valid position in [grid], and [None] otherwise. *)
|
||||||
|
|
||||||
val pos_of_idx : t -> int -> int * int
|
val pos_of_idx : t -> int -> int * int
|
||||||
(** [Grid.pos_of_idx grid idx] returns the [(x, y)] position mapped by [idx]
|
(** [Grid.pos_of_idx grid idx] returns the [(x, y)] position mapped by [idx]
|
||||||
in [grid]. *)
|
in [grid]. *)
|
||||||
|
Reference in New Issue
Block a user