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