type grid = { grid : char array; width : int; height : int } let grid_make posmap_fn strs = let grid = List.fold_left String.cat "" strs |> posmap_fn |> String.to_seq |> Array.of_seq 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 } let[@warning "-32"] grid_print grid = Array.iteri (fun i c -> print_char c; if i mod grid.width = grid.width - 1 then print_newline ()) grid.grid let find_start_idx grid = match Array.find_index (( = ) '@') grid.grid with | None -> failwith "find_start_idx" | Some x -> x let instrs_of_file fname = let strs = Aoc.strings_of_file fname in let rec impl acc = function | [] | "" :: [] -> failwith "instrs_of_file.impl" | "" :: t -> (List.rev acc, t) | h :: t -> impl (h :: acc) t in 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 = 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 | '.' -> grid.grid.(i + di) <- grid.grid.(i); grid.grid.(i) <- '.'; i + di | _ -> failwith "move_robot_x" let rec can_move_y grid i di = match grid.grid.(i + di) with | '#' -> false | '.' -> true | 'O' -> can_move_y grid (i + di) di | '[' -> can_move_y grid (i + di) di && can_move_y grid (i + di + 1) 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 = let rec do_move i = match grid.grid.(i + di) with | '#' -> failwith "move_robot_y.do_move #" | '.' -> grid.grid.(i + di) <- grid.grid.(i); grid.grid.(i) <- '.' | 'O' -> do_move (i + di); do_move i | '[' -> do_move (i + di); do_move (i + di + 1); do_move i | ']' -> do_move (i + di - 1); do_move (i + di); do_move i | _ -> failwith "move_robot_y.do_move" in if can_move_y grid i di then ( do_move i; i + di) else i 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 | _ -> failwith "process_move" let rec process_moves grid robot = function | [] -> grid | h :: t -> process_moves grid (process_move grid robot h) t let calc_score grid = Array.mapi (fun idx c -> if c = 'O' || c = '[' then (idx mod grid.width) + (100 * (idx / grid.width)) else 0) grid.grid |> Array.fold_left ( + ) 0 let expand_grid str = let b = Buffer.create (String.length str * 2) in let add_c = function | '#' -> Buffer.add_string b "##" | 'O' -> Buffer.add_string b "[]" | '.' -> Buffer.add_string b ".." | '@' -> Buffer.add_string b "@." | _ -> failwith "expand_grid" in String.iter add_c str; Buffer.contents b 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 (*grid_print grid;*) calc_score grid let _ = Aoc.main instrs_of_file [ (string_of_int, part Fun.id); (string_of_int, part expand_grid) ]