202 lines
5.4 KiB
OCaml
202 lines
5.4 KiB
OCaml
let pos_of_numeric_grid c =
|
|
match c with
|
|
| '7' -> (0, 0)
|
|
| '8' -> (1, 0)
|
|
| '9' -> (2, 0)
|
|
| '4' -> (0, 1)
|
|
| '5' -> (1, 1)
|
|
| '6' -> (2, 1)
|
|
| '1' -> (0, 2)
|
|
| '2' -> (1, 2)
|
|
| '3' -> (2, 2)
|
|
| '0' -> (1, 3)
|
|
| 'A' -> (2, 3)
|
|
| _ -> raise (invalid_arg "pos_of_numeric_grid")
|
|
|
|
let pos_of_dir_grid c =
|
|
match c with
|
|
| '^' -> (1, 3)
|
|
| 'A' -> (2, 3)
|
|
| '<' -> (0, 4)
|
|
| 'v' -> (1, 4)
|
|
| '>' -> (2, 4)
|
|
| _ -> raise (invalid_arg "pos_of_dir_grid")
|
|
|
|
let invalid_x, invalid_y = (0, 3)
|
|
|
|
let find_shortest (sx, sy) (fx, fy) =
|
|
let b = Buffer.create 6 in
|
|
let result = [] in
|
|
let result =
|
|
if fx <> invalid_x || sy <> invalid_y then begin
|
|
Buffer.reset b;
|
|
if fx > sx then Buffer.add_string b (String.make (fx - sx) '>')
|
|
else if fx < sx then Buffer.add_string b (String.make (sx - fx) '<');
|
|
if fy > sy then Buffer.add_string b (String.make (fy - sy) 'v')
|
|
else if fy < sy then Buffer.add_string b (String.make (sy - fy) '^');
|
|
Buffer.add_char b 'A';
|
|
Buffer.contents b :: result
|
|
end
|
|
else result
|
|
in
|
|
let result =
|
|
if sx <> invalid_x || fy <> invalid_y then begin
|
|
Buffer.reset b;
|
|
if fy > sy then Buffer.add_string b (String.make (fy - sy) 'v')
|
|
else if fy < sy then Buffer.add_string b (String.make (sy - fy) '^');
|
|
if fx > sx then Buffer.add_string b (String.make (fx - sx) '>')
|
|
else if fx < sx then Buffer.add_string b (String.make (sx - fx) '<');
|
|
Buffer.add_char b 'A';
|
|
Buffer.contents b :: result
|
|
end
|
|
else result
|
|
in
|
|
List.sort_uniq compare result
|
|
|
|
let[@warning "-32"] print_list lst =
|
|
let rec impl = function
|
|
| [] -> ()
|
|
| h :: t ->
|
|
Printf.printf "; %s" h;
|
|
impl t
|
|
in
|
|
match lst with
|
|
| [] -> print_endline "[]"
|
|
| h :: t ->
|
|
Printf.printf "[%s" h;
|
|
impl t;
|
|
Printf.printf "]\n"
|
|
|
|
module CharPair = struct
|
|
type t = char * char
|
|
|
|
let compare (x, y) (x', y') =
|
|
match compare y y' with 0 -> compare x x' | c -> c
|
|
end
|
|
|
|
module CharPairMap = Map.Make (CharPair)
|
|
|
|
let routes pos_of_grid locs =
|
|
let rec impl acc pos =
|
|
let start = pos_of_grid pos in
|
|
function
|
|
| [] -> acc
|
|
| h :: t ->
|
|
impl
|
|
(CharPairMap.add (pos, h) (find_shortest start (pos_of_grid h)) acc)
|
|
pos t
|
|
in
|
|
let rec impl' acc = function
|
|
| [] -> acc
|
|
| h :: t -> impl' (impl acc h locs) t
|
|
in
|
|
impl' CharPairMap.empty locs
|
|
|
|
let num_grid = [ '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'; 'A' ]
|
|
let num_routes = routes pos_of_numeric_grid num_grid
|
|
let dir_grid = [ '<'; '>'; 'v'; '^'; 'A' ]
|
|
let dir_routes = routes pos_of_dir_grid dir_grid
|
|
|
|
let cross_product lst lst' =
|
|
let rec impl' acc app' = function
|
|
| [] -> acc
|
|
| h :: t -> impl' ((h ^ app') :: acc) app' t
|
|
in
|
|
let rec impl acc = function
|
|
| [] -> acc
|
|
| h :: t -> impl (impl' acc h lst) t
|
|
in
|
|
if List.is_empty lst then lst'
|
|
else if List.is_empty lst' then lst
|
|
else impl [] lst'
|
|
|
|
let find_route route_map presses =
|
|
let rec impl acc from presses =
|
|
match Seq.uncons presses with
|
|
| None -> acc
|
|
| Some (h, t) ->
|
|
let routes = CharPairMap.find (from, h) route_map in
|
|
impl (cross_product acc routes) h t
|
|
in
|
|
impl [] 'A' (String.to_seq presses)
|
|
|
|
let find_routes route_map press_list =
|
|
let rec impl acc = function
|
|
| [] -> acc
|
|
| h :: t -> impl (acc @ find_route route_map h) t
|
|
in
|
|
impl [] press_list
|
|
|
|
let min_length = List.fold_left (fun acc x -> min acc (String.length x)) max_int
|
|
|
|
let human_insns2 count code =
|
|
let rec robot_round codes n =
|
|
if n >= count then codes
|
|
else robot_round (find_routes dir_routes codes) (n + 1)
|
|
in
|
|
let first_robot_insns = find_routes num_routes [ code ] in
|
|
let human = robot_round first_robot_insns ~-1 in
|
|
min_length human
|
|
|
|
let complexity2 count code =
|
|
let len = human_insns2 count code in
|
|
let num = int_of_string (String.sub code 0 (String.length code - 1)) in
|
|
num * len
|
|
|
|
let part1a count codes =
|
|
List.map (complexity2 count) codes |> List.fold_left ( + ) 0
|
|
|
|
let initial_cost_map grid =
|
|
let rec impl' acc f = function
|
|
| [] -> acc
|
|
| h :: t -> impl' (CharPairMap.add (f, h) 1 acc) f t
|
|
in
|
|
let rec impl acc = function
|
|
| [] -> acc
|
|
| h :: t -> impl (impl' acc h grid) t
|
|
in
|
|
impl CharPairMap.empty grid
|
|
|
|
let calc_costs cost_map steps =
|
|
let rec impl acc from seq =
|
|
match Seq.uncons seq with
|
|
| None -> acc
|
|
| Some (h, t) -> impl (acc + CharPairMap.find (from, h) cost_map) h t
|
|
in
|
|
impl 0 'A' (String.to_seq steps)
|
|
|
|
let get_next_level_costs route_map cost_map =
|
|
let impl routes =
|
|
List.fold_left (fun acc x -> min acc (calc_costs cost_map x)) max_int routes
|
|
in
|
|
CharPairMap.map impl route_map
|
|
|
|
let human_insns3 count code =
|
|
let first_round = initial_cost_map dir_grid in
|
|
let rec robot_round cost_map n =
|
|
if n >= count then cost_map
|
|
else robot_round (get_next_level_costs dir_routes cost_map) (n + 1)
|
|
in
|
|
let number_costs = robot_round first_round 0 in
|
|
let number_costs = get_next_level_costs num_routes number_costs in
|
|
let human = calc_costs number_costs code in
|
|
human
|
|
|
|
let complexity3 count code =
|
|
let len = human_insns3 count code in
|
|
let num = int_of_string (String.sub code 0 (String.length code - 1)) in
|
|
num * len
|
|
|
|
let part1b count codes =
|
|
List.map (complexity3 count) codes |> List.fold_left ( + ) 0
|
|
|
|
let _ =
|
|
Aoc.main Aoc.strings_of_file
|
|
[
|
|
(string_of_int, part1a 1);
|
|
(string_of_int, part1b 2);
|
|
(string_of_int, part1b 25);
|
|
]
|
|
|
|
(* too high 524110008179112 *)
|