type grid = { grid : char array; width : int; height : int } let grid_make strs = let grid = List.fold_left String.cat "" strs |> String.to_seq |> Array.of_seq in let height = List.length strs in let width = Array.length grid / height in { grid; width; height } let grid_copy grid = { grid with grid = Array.copy grid.grid } let grid_height grid = grid.height let[@warning "-32"] grid_length grid = grid.width * grid.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 let grid = grid_make grid in ( grid, List.fold_left String.cat "" moves |> String.to_seq |> List.of_seq, find_start_idx grid ) let rec move_robot grid i di = match grid.grid.(i + di) with | '#' -> i | 'O' -> if move_robot grid (i + di) di = i + di then i else move_robot grid i di | '.' -> grid.grid.(i + di) <- grid.grid.(i); grid.grid.(i) <- '.'; i + di | _ -> failwith "move_robot" let process_move grid robot dir = match dir with | '^' -> move_robot grid robot ~-(grid_height grid) | 'v' -> move_robot grid robot (grid_height grid) | '<' -> move_robot grid robot ~-1 | '>' -> move_robot 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' then (idx mod grid.width) + (100 * (idx / grid.height)) else 0) grid.grid |> Array.fold_left ( + ) 0 let part (grid, moves, robot) = let grid = grid_copy grid in let _ = process_moves grid robot moves in calc_score grid let _ = Aoc.main instrs_of_file [ (string_of_int, part) ]