diff --git a/AdventOfCode2024/bin/main.ml b/AdventOfCode2024/bin/main.ml index f12a03e..82d5a45 100644 --- a/AdventOfCode2024/bin/main.ml +++ b/AdventOfCode2024/bin/main.ml @@ -13,7 +13,7 @@ let read_lines name = let () = print_endline "\nAdvent of Code 2024"; let part1, part2 = - read_lines "./inputs/04.txt" |> AdventOfCode2024.Day_04.solve + read_lines "./inputs/05.txt" |> AdventOfCode2024.Day_05.solve in Printf.printf "Part 1: %s\nPart 2: %s\n" (string_of_int part1) (string_of_int part2) diff --git a/AdventOfCode2024/lib/day_05.ml b/AdventOfCode2024/lib/day_05.ml new file mode 100644 index 0000000..9a3911a --- /dev/null +++ b/AdventOfCode2024/lib/day_05.ml @@ -0,0 +1,120 @@ +module IntMap = Map.Make (Int) + +let rec print_list l = + match l with + | [] -> print_endline "" + | head :: tail -> + print_int head; + print_string ","; + print_list tail + +let option_to_val op = + match op with None -> raise (Invalid_argument "assertion") | Some v -> v + +let sublist list low high = List.filteri (fun i _ -> i >= low && i < high) list + +let add_rule_to_map (m : int list IntMap.t) rule = + let num1, num2 = Scanf.sscanf rule "%d|%d" (fun n1 n2 -> (n2, n1)) in + let existing_rules = + IntMap.update num1 + (fun l -> + match l with + | None -> Some [ num2 ] + | Some existing_rules -> Some ([ num2 ] @ existing_rules)) + m + in + existing_rules + +(* +4 | 2 +2 | 3 + +1, 2, 3 + /\ + +prev_list = [1] +pages_needed = [4, 7] + + *) + +let rec is_sublist main_list sub_list = + match sub_list with + | [] -> true + | head :: tail -> + if List.exists (fun i -> head == i) main_list then + is_sublist main_list tail + else false + +let is_case_valid m case = + let rec loop prev_pages remaining_pages = + match remaining_pages with + | [] -> true + | head :: tail -> ( + let pages_needed_before = IntMap.find_opt head m in + match pages_needed_before with + | None -> loop ([ head ] @ prev_pages) tail + | Some l -> + if + is_sublist prev_pages + (List.filter (fun i -> List.exists (fun j -> j == i) case) l) + then loop ([ head ] @ prev_pages) tail + else false) + in + loop [] case + +let partial_sort m case = + List.sort + (fun i j -> + let pages_needed_i = IntMap.find_opt i m in + let pages_needed_j = IntMap.find_opt j m in + + match (pages_needed_i, pages_needed_j) with + | None, None -> 0 + | Some _, None -> 1 + | None, Some _ -> -1 + | Some pages_i, Some _ -> + if List.exists (fun i -> i == j) pages_i then 1 else -1) + case + +let solve lines = + let rule_map : int list IntMap.t = IntMap.empty in + + let empty_index = + List.find_index (fun i -> String.length i == 0) lines |> option_to_val + in + let rules = sublist lines 0 empty_index in + let cases = sublist lines (empty_index + 1) (List.length lines) in + let string_cases = + List.fold_left + (fun acc case -> + [ String.split_on_char ',' case |> List.map int_of_string ] @ acc) + [] cases + in + + let map_with_rules = List.fold_left add_rule_to_map rule_map rules in + let is_case_valid_with_map = is_case_valid map_with_rules in + + let part1 = + List.fold_left + (fun acc case -> + if is_case_valid_with_map case then + acc + List.nth case (List.length case / 2) + else acc) + 0 string_cases + in + + let invalid_cases = + List.filter (fun case -> is_case_valid_with_map case == false) string_cases + in + + let sorted_invalid_cases = + List.map (fun case -> partial_sort map_with_rules case) invalid_cases + in + + let part2 = + List.fold_left + (fun acc case -> acc + List.nth case (List.length case / 2)) + 0 sorted_invalid_cases + in + + (part1, part2)