This commit is contained in:
2024-12-22 16:39:02 +01:00
parent c808af8e9f
commit bb7ec19383

132
21/main.ml Normal file
View File

@@ -0,0 +1,132 @@
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 () = Printexc.record_backtrace true in
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