Compare commits
2 Commits
76dc338c9a
...
b2e56f802e
Author | SHA1 | Date | |
---|---|---|---|
b2e56f802e | |||
fe93f65f6a |
@@ -1,50 +1,26 @@
|
|||||||
type map = { map : string; width : int; height : int }
|
|
||||||
(** [map] represents a map with layout [map] and a [width] and [height].
|
|
||||||
[map.map] is a string of length [width * height] with the [i]th character
|
|
||||||
describing the position [(i mod map.width, i / map.width)]. *)
|
|
||||||
|
|
||||||
(** [find_start strs] returns the location [(x, y)] of the starting position. *)
|
(** [find_start strs] returns the location [(x, y)] of the starting position. *)
|
||||||
let find_start map =
|
let find_start map =
|
||||||
match String.index_from_opt map.map 0 '^' with
|
match Aoc.Grid.idx_from_opt map 0 '^' with
|
||||||
| Some i -> (i mod map.width, i / map.width)
|
| Some i -> Aoc.Grid.pos_of_idx map i
|
||||||
| None -> failwith "find_start"
|
| None -> failwith "find_start"
|
||||||
|
|
||||||
(** [map_of_strings strs] returns the map generated from the input map described
|
|
||||||
by the list of strings [strs] *)
|
|
||||||
let map_of_strings strs =
|
|
||||||
let width = String.length (List.hd strs) in
|
|
||||||
let map = List.fold_left String.cat "" strs in
|
|
||||||
let height = String.length map / width in
|
|
||||||
{ map; width; height }
|
|
||||||
|
|
||||||
(** [read_file fname] reads the input map from [fname]. It returns a
|
(** [read_file fname] reads the input map from [fname]. It returns a
|
||||||
[(map, pos, vel)] tuple, consisting of the obsticle map, initial position,
|
[(map, pos, vel)] tuple, consisting of the obsticle map, initial position,
|
||||||
and initial velocity. *)
|
and initial velocity. *)
|
||||||
let read_file fname =
|
let read_file fname =
|
||||||
let lst = Aoc.strings_of_file fname in
|
let map = Aoc.Grid.of_file fname in
|
||||||
let map = map_of_strings lst in
|
|
||||||
let pos = find_start map in
|
let pos = find_start map in
|
||||||
(map, pos, (0, -1))
|
(map, pos, (0, -1))
|
||||||
|
|
||||||
(** [is_valid_pos map pos] returns true if the position [pos] is valid for the
|
|
||||||
map [map]. *)
|
|
||||||
let is_valid_pos map (x, y) =
|
|
||||||
if y < 0 || y >= map.height then false
|
|
||||||
else if x < 0 || x >= map.width then false
|
|
||||||
else true
|
|
||||||
|
|
||||||
(** [is_block map pos] returns [true] iff the location [pos] is a blockages in
|
(** [is_block map pos] returns [true] iff the location [pos] is a blockages in
|
||||||
[map]. *)
|
[map]. *)
|
||||||
let is_block map (x, y) =
|
let is_block map pos =
|
||||||
if is_valid_pos map (x, y) then map.map.[x + (y * map.width)] = '#' else false
|
if Aoc.Grid.pos_is_valid map pos then Aoc.Grid.get_by_pos map pos = '#'
|
||||||
|
else false
|
||||||
|
|
||||||
(** [insert_block map pos] inserts a blockage at [pos] into [map] and returns
|
(** [insert_block map pos] inserts a blockage at [pos] into [map] and returns
|
||||||
the new map. *)
|
the new map. *)
|
||||||
let insert_block map (x, y) =
|
let insert_block map pos = Aoc.Grid.update_pos map pos '#'
|
||||||
let idx = x + (y * map.width) in
|
|
||||||
let start = String.sub map.map 0 idx in
|
|
||||||
let e = String.sub map.map (idx + 1) (String.length map.map - idx - 1) in
|
|
||||||
{ map with map = start ^ "#" ^ e }
|
|
||||||
|
|
||||||
(** [move map (pos, vel)] moves [pos] one step forward on the [map]. [vel] gives
|
(** [move map (pos, vel)] moves [pos] one step forward on the [map]. [vel] gives
|
||||||
the movement vector. If the movement will cause an obstacle to be hit then
|
the movement vector. If the movement will cause an obstacle to be hit then
|
||||||
@@ -60,7 +36,8 @@ let rec move map ((x, y), (dx, dy)) =
|
|||||||
off one of the sides. *)
|
off one of the sides. *)
|
||||||
let walk_map map (pos, vel) =
|
let walk_map map (pos, vel) =
|
||||||
let rec impl acc (pos, vel) =
|
let rec impl acc (pos, vel) =
|
||||||
if is_valid_pos map pos then impl (pos :: acc) (move map (pos, vel))
|
if Aoc.Grid.pos_is_valid map pos then
|
||||||
|
impl (pos :: acc) (move map (pos, vel))
|
||||||
else acc
|
else acc
|
||||||
in
|
in
|
||||||
impl [] (pos, vel)
|
impl [] (pos, vel)
|
||||||
@@ -75,7 +52,7 @@ let has_cycles map start =
|
|||||||
let rec impl agent1 ((pos', _) as agent2) =
|
let rec impl agent1 ((pos', _) as agent2) =
|
||||||
(* Only need to check pos' for validity because if pos is not valid then
|
(* Only need to check pos' for validity because if pos is not valid then
|
||||||
pos' must also be invalid, and have been invalid before this. *)
|
pos' must also be invalid, and have been invalid before this. *)
|
||||||
if not (is_valid_pos map pos') then false
|
if not (Aoc.Grid.pos_is_valid map pos') then false
|
||||||
else if agent1 = agent2 then true
|
else if agent1 = agent2 then true
|
||||||
else impl (move map agent1) (move map (move map agent2))
|
else impl (move map agent1) (move map (move map agent2))
|
||||||
in
|
in
|
||||||
|
@@ -1,29 +1,13 @@
|
|||||||
type map = { map : string; width : int; height : int }
|
|
||||||
|
|
||||||
let map_of_file fname =
|
|
||||||
let strs = Aoc.strings_of_file fname in
|
|
||||||
let width = String.length (List.hd strs) in
|
|
||||||
let map = List.fold_left ( ^ ) "" strs in
|
|
||||||
let height = String.length map / width in
|
|
||||||
{ map; width; height }
|
|
||||||
|
|
||||||
let map_length map = String.length map.map
|
|
||||||
let map_get_by_idx map idx = map.map.[idx]
|
|
||||||
let map_pos_of_idx map idx = (idx mod map.width, idx / map.width)
|
|
||||||
|
|
||||||
let map_is_valid_pos map (x, y) =
|
|
||||||
x >= 0 && x < map.width && y >= 0 && y < map.height
|
|
||||||
|
|
||||||
module CharMap = Map.Make (Char)
|
module CharMap = Map.Make (Char)
|
||||||
|
|
||||||
(** [get_station_indices map] returns a list of pairs mapping station ID to the
|
(** [get_station_indices map] returns a list of pairs mapping station ID to the
|
||||||
indices in [map] where there is a station with that ID. *)
|
indices in [map] where there is a station with that ID. *)
|
||||||
let get_station_indices map =
|
let get_station_indices map =
|
||||||
let rec impl acc idx =
|
let rec impl acc idx =
|
||||||
if idx >= map_length map then acc
|
if idx >= Aoc.Grid.length map then acc
|
||||||
else if map_get_by_idx map idx = '.' then impl acc (idx + 1)
|
else if Aoc.Grid.get_by_idx map idx = '.' then impl acc (idx + 1)
|
||||||
else
|
else
|
||||||
let station = map_get_by_idx map idx in
|
let station = Aoc.Grid.get_by_idx map idx in
|
||||||
let update_fn lst =
|
let update_fn lst =
|
||||||
match lst with None -> Some [ idx ] | Some t -> Some (idx :: t)
|
match lst with None -> Some [ idx ] | Some t -> Some (idx :: t)
|
||||||
in
|
in
|
||||||
@@ -40,7 +24,7 @@ let get_antinodes1 acc _ (px, py) (px', py') =
|
|||||||
(** [add_antinodes lst map pos vel] adds antinodes at [pos + n * vel] to [lst]
|
(** [add_antinodes lst map pos vel] adds antinodes at [pos + n * vel] to [lst]
|
||||||
for all non-negative [n] that are valid positions in [map]. *)
|
for all non-negative [n] that are valid positions in [map]. *)
|
||||||
let rec add_antinodes lst map (x, y) (dx, dy) =
|
let rec add_antinodes lst map (x, y) (dx, dy) =
|
||||||
if map_is_valid_pos map (x, y) then
|
if Aoc.Grid.pos_is_valid map (x, y) then
|
||||||
add_antinodes ((x, y) :: lst) map (x + dx, y + dy) (dx, dy)
|
add_antinodes ((x, y) :: lst) map (x + dx, y + dy) (dx, dy)
|
||||||
else lst
|
else lst
|
||||||
|
|
||||||
@@ -60,11 +44,11 @@ let process_stations map fn stations =
|
|||||||
let rec impl2 acc p t =
|
let rec impl2 acc p t =
|
||||||
match t with
|
match t with
|
||||||
| [] -> acc
|
| [] -> acc
|
||||||
| h :: t -> impl2 (fn acc map p (map_pos_of_idx map h)) p t
|
| h :: t -> impl2 (fn acc map p (Aoc.Grid.pos_of_idx map h)) p t
|
||||||
in
|
in
|
||||||
let rec impl acc = function
|
let rec impl acc = function
|
||||||
| [] -> acc
|
| [] -> acc
|
||||||
| h :: t -> impl (impl2 acc (map_pos_of_idx map h) t) t
|
| h :: t -> impl (impl2 acc (Aoc.Grid.pos_of_idx map h) t) t
|
||||||
in
|
in
|
||||||
List.map (impl []) stations
|
List.map (impl []) stations
|
||||||
|
|
||||||
@@ -77,12 +61,12 @@ let part antifn map =
|
|||||||
|> List.map snd (* we do not care about the station IDs *)
|
|> List.map snd (* we do not care about the station IDs *)
|
||||||
|> process_stations map antifn
|
|> process_stations map antifn
|
||||||
|> List.concat
|
|> List.concat
|
||||||
|> List.filter (map_is_valid_pos map)
|
|> List.filter (Aoc.Grid.pos_is_valid map)
|
||||||
|> List.sort_uniq Stdlib.compare
|
|> List.sort_uniq Stdlib.compare
|
||||||
|> List.length
|
|> List.length
|
||||||
|
|
||||||
let _ =
|
let _ =
|
||||||
Aoc.main map_of_file
|
Aoc.main Aoc.Grid.of_file
|
||||||
[
|
[
|
||||||
(string_of_int, part get_antinodes1); (string_of_int, part get_antinodes2);
|
(string_of_int, part get_antinodes1); (string_of_int, part get_antinodes2);
|
||||||
]
|
]
|
||||||
|
@@ -1,33 +1,3 @@
|
|||||||
type grid = { grid : string; width : int; height : int }
|
|
||||||
(** Type to hold the grid in... We should be moving this to the library soon *)
|
|
||||||
|
|
||||||
(** [grid_of_file fname] loads a grid from the given file *)
|
|
||||||
let grid_of_file fname =
|
|
||||||
let strs = Aoc.strings_of_file fname in
|
|
||||||
let grid = List.fold_left String.cat "" strs in
|
|
||||||
let width = String.length (List.hd strs) in
|
|
||||||
let height = String.length grid / width in
|
|
||||||
assert (width * height = String.length grid);
|
|
||||||
{ grid; width; height }
|
|
||||||
|
|
||||||
(** [grid_length grid] gives the length of the grid ([width * height]). *)
|
|
||||||
let grid_length grid = String.length grid.grid
|
|
||||||
|
|
||||||
(** [grid_get_by_idx grid idx] returns the element of [grid] at [idx]. *)
|
|
||||||
let grid_get_by_idx grid idx = grid.grid.[idx]
|
|
||||||
|
|
||||||
(** [grid_get_by_idx grid pos] returns the element of [grid] at [pos]. *)
|
|
||||||
let grid_get_by_pos grid (x, y) = grid.grid.[x + (y * grid.width)]
|
|
||||||
|
|
||||||
(** [grid_pos_if_idx grid idx] returns the [(x, y)] position of [idx] in [grid].
|
|
||||||
*)
|
|
||||||
let grid_pos_of_idx grid idx = (idx mod grid.width, idx / grid.width)
|
|
||||||
|
|
||||||
(** [grid_pos_is_valid grid pos] returns [true] if and only if [pos] is a valid
|
|
||||||
position in [grid]. *)
|
|
||||||
let grid_pos_is_valid grid (x, y) =
|
|
||||||
x >= 0 && y >= 0 && x < grid.width && y < grid.height
|
|
||||||
|
|
||||||
(** [next_char ch] returns the next character after [ch]. *)
|
(** [next_char ch] returns the next character after [ch]. *)
|
||||||
let next_char ch = Char.chr (1 + Char.code ch)
|
let next_char ch = Char.chr (1 + Char.code ch)
|
||||||
|
|
||||||
@@ -42,10 +12,10 @@ let one = '1'
|
|||||||
'0'. The same endpoint may be returned multiple times if there are multiple
|
'0'. The same endpoint may be returned multiple times if there are multiple
|
||||||
routes to it. *)
|
routes to it. *)
|
||||||
let find_trail grid pos0 =
|
let find_trail grid pos0 =
|
||||||
assert (grid_get_by_pos grid pos0 = '0');
|
assert (Aoc.Grid.get_by_pos grid pos0 = '0');
|
||||||
let add_pos lst pos digit =
|
let add_pos lst pos digit =
|
||||||
if grid_pos_is_valid grid pos && grid_get_by_pos grid pos = digit then
|
if Aoc.Grid.pos_is_valid grid pos && Aoc.Grid.get_by_pos grid pos = digit
|
||||||
pos :: lst
|
then pos :: lst
|
||||||
else lst
|
else lst
|
||||||
in
|
in
|
||||||
let add_poses lst (x, y) digit =
|
let add_poses lst (x, y) digit =
|
||||||
@@ -69,10 +39,10 @@ let find_trail grid pos0 =
|
|||||||
corresponds to the trails starting at index [n]. *)
|
corresponds to the trails starting at index [n]. *)
|
||||||
let find_trails grid =
|
let find_trails grid =
|
||||||
let rec impl acc idx =
|
let rec impl acc idx =
|
||||||
if idx >= grid_length grid then acc
|
if idx >= Aoc.Grid.length grid then acc
|
||||||
else if grid_get_by_idx grid idx <> '0' then impl acc (idx + 1)
|
else if Aoc.Grid.get_by_idx grid idx <> '0' then impl acc (idx + 1)
|
||||||
else (* grid_get_by_idx grid idx = 0 *)
|
else (* grid_get_by_idx grid idx = 0 *)
|
||||||
impl (find_trail grid (grid_pos_of_idx grid idx) :: acc) (idx + 1)
|
impl (find_trail grid (Aoc.Grid.pos_of_idx grid idx) :: acc) (idx + 1)
|
||||||
in
|
in
|
||||||
impl [] 0 |> List.rev
|
impl [] 0 |> List.rev
|
||||||
|
|
||||||
@@ -83,7 +53,7 @@ let part sort_fn grid =
|
|||||||
|> List.fold_left ( + ) 0
|
|> List.fold_left ( + ) 0
|
||||||
|
|
||||||
let _ =
|
let _ =
|
||||||
Aoc.main grid_of_file
|
Aoc.main Aoc.Grid.of_file
|
||||||
[
|
[
|
||||||
(string_of_int, part (List.sort_uniq Stdlib.compare));
|
(string_of_int, part (List.sort_uniq Stdlib.compare));
|
||||||
(string_of_int, part Fun.id);
|
(string_of_int, part Fun.id);
|
||||||
|
36
lib/aoc.ml
36
lib/aoc.ml
@@ -21,7 +21,10 @@ let main prep parts =
|
|||||||
Printf.printf "Usage: %s <fname>\n" Sys.executable_name;
|
Printf.printf "Usage: %s <fname>\n" Sys.executable_name;
|
||||||
exit 2
|
exit 2
|
||||||
with e ->
|
with e ->
|
||||||
Printf.printf "An error occured: %s\n" (Printexc.to_string e);
|
Printf.fprintf stderr "An error occured: %s\n" (Printexc.to_string e);
|
||||||
|
if Printexc.backtrace_status () then (
|
||||||
|
Printf.fprintf stderr "Backtrace:\n";
|
||||||
|
Printexc.print_backtrace stderr);
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
module IntPair = struct
|
module IntPair = struct
|
||||||
@@ -32,3 +35,34 @@ module IntPair = struct
|
|||||||
end
|
end
|
||||||
|
|
||||||
module IntPairSet = Set.Make (IntPair)
|
module IntPairSet = Set.Make (IntPair)
|
||||||
|
|
||||||
|
module Grid = struct
|
||||||
|
type t = { grid : string; width : int; height : int }
|
||||||
|
|
||||||
|
let of_file fname =
|
||||||
|
let strs = strings_of_file fname in
|
||||||
|
let width = String.length (List.hd strs) in
|
||||||
|
let grid = List.fold_left ( ^ ) "" strs in
|
||||||
|
let height = String.length grid / width in
|
||||||
|
{ grid; width; height }
|
||||||
|
|
||||||
|
let length grid = String.length grid.grid
|
||||||
|
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 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) =
|
||||||
|
x >= 0 && x < grid.width && y >= 0 && y < grid.height
|
||||||
|
|
||||||
|
let idx_from_opt grid = String.index_from_opt grid.grid
|
||||||
|
|
||||||
|
let update_pos grid pos c =
|
||||||
|
let idx = idx_of_pos grid pos in
|
||||||
|
let builder = Buffer.create (length grid) in
|
||||||
|
Buffer.add_string builder (String.sub grid.grid 0 idx);
|
||||||
|
Buffer.add_char builder c;
|
||||||
|
Buffer.add_string builder
|
||||||
|
(String.sub grid.grid (idx + 1) (length grid - idx - 1));
|
||||||
|
{ grid with grid = Buffer.contents builder }
|
||||||
|
end
|
||||||
|
47
lib/aoc.mli
47
lib/aoc.mli
@@ -74,3 +74,50 @@ module IntPairSet : sig
|
|||||||
val add_seq : elt Seq.t -> t -> t
|
val add_seq : elt Seq.t -> t -> t
|
||||||
val of_seq : elt Seq.t -> t
|
val of_seq : elt Seq.t -> t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(** The [Grid] module is used to represent and manipulate a grid of characters.
|
||||||
|
Its main goals are to be non-mutable and have constant access times to
|
||||||
|
locations in the grid.
|
||||||
|
|
||||||
|
Grid locations can be accessed by index or (col, row) position. Indicies do
|
||||||
|
not guarantee an ordering on accesses - but iterating by index from 0 to
|
||||||
|
[Grid.length grid - 1] inclusive will cover the whole grid. *)
|
||||||
|
module Grid : sig
|
||||||
|
type t
|
||||||
|
(** The type used to represent a grid *)
|
||||||
|
|
||||||
|
val of_file : string -> t
|
||||||
|
(** [Grid.of_file fname] returns a grid loaded from the file [fname] *)
|
||||||
|
|
||||||
|
val length : t -> int
|
||||||
|
(** [Grid.length grid] returns the length of the grid. *)
|
||||||
|
|
||||||
|
val get_by_idx : t -> int -> char
|
||||||
|
(** [Grid.get_by_idx grid idx] returns the character at index [idx] in [grid].
|
||||||
|
*)
|
||||||
|
|
||||||
|
val get_by_pos : t -> int * int -> char
|
||||||
|
(** [Grid.get_by_pos grid pos] returns the character at position [pos] in
|
||||||
|
[grid]. *)
|
||||||
|
|
||||||
|
val pos_of_idx : t -> int -> int * int
|
||||||
|
(** [Grid.pos_of_idx grid idx] returns the [(x, y)] position mapped by [idx]
|
||||||
|
in [grid]. *)
|
||||||
|
|
||||||
|
val idx_of_pos : t -> int * int -> int
|
||||||
|
(** [Grid.pos_of_idx grid pos] returns the index corresponding to [pos] in
|
||||||
|
[grid]. *)
|
||||||
|
|
||||||
|
val pos_is_valid : t -> int * int -> bool
|
||||||
|
(** [Grid.pos_is_valid grid pos] returns [true] if and only if [pos] is a
|
||||||
|
valid position in [grid]. *)
|
||||||
|
|
||||||
|
val idx_from_opt : t -> int -> char -> int option
|
||||||
|
(** [Grid.idx_from_opt grid start c] returns [Some idx] where [idx] is the
|
||||||
|
first location in [grid] at or after the [start] index which is [c]. It
|
||||||
|
returns [None] if [c] does not appear. *)
|
||||||
|
|
||||||
|
val update_pos : t -> int * int -> char -> t
|
||||||
|
(** [Grid.update_pos grid pos c] returns a grid with the character at position
|
||||||
|
[pos] changed to [c]. *)
|
||||||
|
end
|
||||||
|
Reference in New Issue
Block a user