From 1ab16668f46d09b102d2f6bf214d4ab7121ec2cc Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Sun, 15 Dec 2024 09:01:56 +0000 Subject: [PATCH] Tidy up 2024 day 15 --- bin/day2415.ml | 74 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/bin/day2415.ml b/bin/day2415.ml index 826cc5f..6f676ea 100644 --- a/bin/day2415.ml +++ b/bin/day2415.ml @@ -1,5 +1,9 @@ -type grid = { grid : char array; width : int; height : int } +type grid = { grid : char array; width : int } +(** Grid type. Not our normal type as we want to use it in a mutable way. *) +(** [grid_map fn strs] Returns a grid built up from the list of strings [strs]. + [fn] takes a single string of all map characters and returns a string with + the appropriate replacements done. *) let grid_make posmap_fn strs = let grid = List.fold_left String.cat "" strs @@ -7,9 +11,9 @@ let grid_make posmap_fn strs = in let height = List.length strs in let width = Array.length grid / height in - Printf.printf "width %d height %d \n" width height; - { grid; width; height } + { grid; width } +(** [grid_print grid] prints [grid] to standard output. *) let[@warning "-32"] grid_print grid = Array.iteri (fun i c -> @@ -17,11 +21,16 @@ let[@warning "-32"] grid_print grid = if i mod grid.width = grid.width - 1 then print_newline ()) grid.grid -let find_start_idx grid = +(** [find_robot_idx grid] returns the index of the robot in grid. *) +let find_robot_idx grid = match Array.find_index (( = ) '@') grid.grid with - | None -> failwith "find_start_idx" + | None -> failwith "find_robot_idx" | Some x -> x +(** [instrs_of_file fname] reads from the file [fname] and returns a + [(grid, instrs)] pair. [grid] is a list of strings describing the initial + grid state. [instrs] is a list of characters giving movement instructions. +*) let instrs_of_file fname = let strs = Aoc.strings_of_file fname in let rec impl acc = function @@ -32,19 +41,29 @@ let instrs_of_file fname = let grid, moves = impl [] strs in (grid, List.fold_left String.cat "" moves |> String.to_seq |> List.of_seq) -let rec move_robot_x grid i di = +(** [move_x grid i di] moves the object in [grid] at [i] to [i + di] if + possible, returning the new location of the object (either [i] or [i + di]). + This assumes a horizontal movement of one step. *) +let rec move_x grid i di = + assert (di == -1 || di == 1); + assert (i >= 0 && i <= Array.length grid.grid); match grid.grid.(i + di) with | '#' -> i | 'O' | '[' | ']' -> - if move_robot_x grid (i + di) di = i + di then i - else move_robot_x grid i di + if move_x grid (i + di) di = i + di then i else move_x grid i di | '.' -> grid.grid.(i + di) <- grid.grid.(i); grid.grid.(i) <- '.'; i + di - | _ -> failwith "move_robot_x" + | _ -> failwith "move_x" +(** [can_move_y grid i di] returns true if and only if it is possible to move + whatever is at [i] to [i + di] in [grid]. If [i + di] is full it recursively + checks to see if that can be moved as well. [di] must be a vertical + movement. *) let rec can_move_y grid i di = + assert (i >= 0 && i < Array.length grid.grid); + assert (di = grid.width || di = -grid.width); match grid.grid.(i + di) with | '#' -> false | '.' -> true @@ -53,10 +72,15 @@ let rec can_move_y grid i di = | ']' -> can_move_y grid (i + di - 1) di && can_move_y grid (i + di) di | _ -> failwith "can_move_y" -let move_robot_y grid i di = +(** [move_x grid i di] moves the object in [grid] at [i] to [i + di] if + possible, returning the new location of the object (either [i] or [i + di]). + This assumes a vertical movement of one step. *) +let move_y grid i di = + assert (i >= 0 && i < Array.length grid.grid); + assert (di = grid.width || di = -grid.width); let rec do_move i = match grid.grid.(i + di) with - | '#' -> failwith "move_robot_y.do_move #" + | '#' -> failwith "move_y.do_move #" | '.' -> grid.grid.(i + di) <- grid.grid.(i); grid.grid.(i) <- '.' @@ -71,27 +95,31 @@ let move_robot_y grid i di = do_move (i + di - 1); do_move (i + di); do_move i - | _ -> failwith "move_robot_y.do_move" + | _ -> failwith "move_y.do_move" in if can_move_y grid i di then ( do_move i; i + di) else i +(** [process_move grid robot dir] attempts to move robot at idx [robot] in + [grid] in direction. Returns location of robot after attempt. *) let process_move grid robot dir = - (*grid_print grid; - Printf.printf "\nMove: %c\n" dir;*) match dir with - | '^' -> move_robot_y grid robot ~-(grid.width) - | 'v' -> move_robot_y grid robot grid.width - | '<' -> move_robot_x grid robot ~-1 - | '>' -> move_robot_x grid robot 1 + | '^' -> move_y grid robot (-grid.width) + | 'v' -> move_y grid robot grid.width + | '<' -> move_x grid robot ~-1 + | '>' -> move_x grid robot 1 | _ -> failwith "process_move" +(** [process_moves grid robot lst] Attempts each move in [lst] in turn given a + [grid] and initial starting position of the robot [robot]. Returns the + [grid]. *) let rec process_moves grid robot = function - | [] -> grid + | [] -> () | h :: t -> process_moves grid (process_move grid robot h) t +(** [calc_score grid] returns the score for [grid]. *) let calc_score grid = Array.mapi (fun idx c -> @@ -101,6 +129,8 @@ let calc_score grid = grid.grid |> Array.fold_left ( + ) 0 +(** [expand_grid str] given an input [str] returns the output grid string for + part 2. *) let expand_grid str = let b = Buffer.create (String.length str * 2) in let add_c = function @@ -113,10 +143,12 @@ let expand_grid str = String.iter add_c str; Buffer.contents b +(** [part posmap_fn (grid, moves)] executes the [moves] in [grid] having first + applied [posmap_fn] to grid. *) let part posmap_fn (grid, moves) = let grid = grid_make posmap_fn grid in - let robot = find_start_idx grid in - let _ = process_moves grid robot moves in + let robot = find_robot_idx grid in + process_moves grid robot moves; (*grid_print grid;*) calc_score grid