132 lines
4.4 KiB
OCaml
132 lines
4.4 KiB
OCaml
open Printf;;
|
|
|
|
let rec list_of_lines in_file =
|
|
try
|
|
let line = input_line in_file in
|
|
line :: list_of_lines(in_file)
|
|
with End_of_file ->
|
|
close_in in_file;
|
|
[]
|
|
|
|
let is_valid_position_numpad (x, y) =
|
|
x >= 0 && x < 3 && y >= 0 && y < 4 && not (x = 0 && y = 3)
|
|
|
|
let is_valid_position_keypad (x, y) =
|
|
x >= 0 && x < 3 && y >= 0 && y < 2 && not (x = 0 && y = 0)
|
|
|
|
let get_keypad_coords symbol =
|
|
match symbol with
|
|
| '<' -> (0, 1)
|
|
| '>' -> (2, 1)
|
|
| '^' -> (1, 0)
|
|
| 'v' -> (1, 1)
|
|
| 'A' -> (2, 0)
|
|
| _ -> raise (Invalid_argument "Invalid symbol")
|
|
|
|
let get_numpad_coords symbol =
|
|
if symbol = 'A' then
|
|
(2, 3)
|
|
else
|
|
let n = (Char.code symbol) - (Char.code '0') in
|
|
if n = 0 then
|
|
(1, 3)
|
|
else
|
|
((n - 1) mod 3, (9 - n) / 3)
|
|
|
|
let get_dir_vector dir_symbol =
|
|
match dir_symbol with
|
|
| '<' -> (-1, 0)
|
|
| '>' -> (1, 0)
|
|
| '^' -> (0, -1)
|
|
| 'v' -> (0, 1)
|
|
| _ -> raise (Invalid_argument "Invalid direction")
|
|
|
|
let allowed_moves prev =
|
|
match prev with
|
|
| Some 'A' | None -> ['<'; '^'; '>'; 'v']
|
|
| Some '<' -> ['<'; '^'; 'v']
|
|
| Some '>' -> ['^'; '>'; 'v']
|
|
| Some '^' -> ['<'; '^'; '>']
|
|
| Some 'v' -> ['<'; '>'; 'v']
|
|
| _ -> raise (Invalid_argument "Invalid move")
|
|
|
|
module CombinationSearch =
|
|
struct
|
|
type t = int * (int * (int * int) * char * char list)
|
|
let compare a b =
|
|
Stdlib.compare a b
|
|
end
|
|
module CombinationSearchSet = Set.Make(CombinationSearch)
|
|
|
|
let rec find_best_combination_dijkstra lower_level_cost_fun is_valid_fun to_pos queue =
|
|
match CombinationSearchSet.min_elt_opt queue with
|
|
| None -> raise Not_found
|
|
| Some (score, (partial_score, pos, prev_dir, history)) ->
|
|
if pos = to_pos then begin
|
|
score
|
|
end
|
|
else
|
|
let queue_removed = CombinationSearchSet.remove (score, (partial_score, pos, prev_dir, history)) queue in
|
|
Some prev_dir
|
|
|> allowed_moves
|
|
|> List.filter_map (fun dir ->
|
|
let vec = get_dir_vector dir in
|
|
let new_pos = (fst pos + fst vec, snd pos + snd vec) in
|
|
let lower_cost = lower_level_cost_fun prev_dir dir in
|
|
let finish_cost = lower_level_cost_fun dir 'A' in
|
|
let new_partial_score = partial_score + lower_cost in
|
|
if is_valid_fun new_pos then
|
|
Some (new_partial_score + finish_cost, (new_partial_score, new_pos, dir, dir :: history))
|
|
else
|
|
None)
|
|
|> List.fold_left (fun acc state -> CombinationSearchSet.add state acc) queue_removed
|
|
|> find_best_combination_dijkstra lower_level_cost_fun is_valid_fun to_pos
|
|
|
|
let find_best_combination lower_level_cost_fun is_valid_fun get_coords_fun from_symbol to_symbol =
|
|
let coords1 = get_coords_fun from_symbol in
|
|
let coords2 = get_coords_fun to_symbol in
|
|
find_best_combination_dijkstra lower_level_cost_fun is_valid_fun coords2 ([(1, (0, coords1, 'A', []))] |> CombinationSearchSet.of_list)
|
|
|
|
let get_numerical_value str =
|
|
int_of_string (String.sub str 0 (String.length str - 1))
|
|
|
|
let rec get_keypad_combination_length cache depth from_symbol to_symbol =
|
|
match Hashtbl.find_opt cache (from_symbol, to_symbol, depth) with
|
|
| Some score -> score
|
|
| None ->
|
|
let best = find_best_combination (get_keypad_combination_length cache (depth - 1))
|
|
is_valid_position_keypad get_keypad_coords from_symbol to_symbol in
|
|
Hashtbl.add cache (from_symbol, to_symbol, depth) best;
|
|
best
|
|
|
|
let get_numpad_combination_length cache keypad_depth from_symbol to_symbol =
|
|
let result = find_best_combination (get_keypad_combination_length cache keypad_depth)
|
|
is_valid_position_numpad get_numpad_coords from_symbol to_symbol in
|
|
result
|
|
|
|
let get_numpad_str_combination_length cache keypad_depth str =
|
|
str
|
|
|> String.fold_left (fun (cost, prev) c -> (cost + get_numpad_combination_length cache keypad_depth prev c, c)) (0, 'A')
|
|
|> fst
|
|
|
|
let sum_solutions cache keypad_depth lst =
|
|
lst
|
|
|> List.map (fun str -> (get_numpad_str_combination_length cache keypad_depth str, get_numerical_value str))
|
|
|> List.map (fun (solution, value) -> solution * value)
|
|
|> List.fold_left Int.add 0
|
|
|
|
let init_cache =
|
|
let cache = Hashtbl.create 256 in
|
|
let key_options = ['<'; '>'; '^'; 'v'; 'A'] in
|
|
key_options |> List.iter (fun c1 ->
|
|
key_options |> List.iter (fun c2 -> Hashtbl.add cache (c1, c2, 0) 1));
|
|
cache
|
|
|
|
let () =
|
|
let f = open_in "input.txt" in
|
|
let lines = list_of_lines f in
|
|
let cache = init_cache in
|
|
let result = sum_solutions cache 2 lines in
|
|
let result2 = sum_solutions cache 25 lines in
|
|
printf "%d\n%d\n" result result2
|