From 84edd255facef67108031a7ac70413e63ca7c064 Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Thu, 5 Dec 2024 09:06:31 +0000 Subject: [PATCH] 2024 day 5 both parts. --- bin/day2405.ml | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 bin/day2405.ml diff --git a/bin/day2405.ml b/bin/day2405.ml new file mode 100644 index 0000000..fd13e25 --- /dev/null +++ b/bin/day2405.ml @@ -0,0 +1,83 @@ +module IntSet = Set.Make (Int) +module IntMap = Map.Make (Int) + +(** [add_rule a b m] adds the rule that [a] must appear before [b] to the rule + map [m]. *) +let add_rule a b = + let update_rule = function + | None -> Some (IntSet.singleton b) + | Some s -> Some (IntSet.add b s) + in + IntMap.update a update_rule + +(** [find_rule a b m] returns [true] if the rule map [m] says that [a] should + appear before [b]. *) +let find_rule a b m = + match IntMap.find_opt a m with + | Some s -> ( match IntSet.find_opt b s with Some _ -> true | None -> false) + | None -> false + +(** [compare m a b] is a total ordering on pages in the rule map [m]. Returns + [-1] if [a] should appear before [b], [0] if [a = b], and [1] if [b] should + appear before [a]. *) +let compare m a b = + if a = b then 0 + else if find_rule a b m then -1 + else if find_rule b a m then 1 + else failwith "compare" + +(** [sort m pages] Sorts [pages] into the order required by the rule map [m]. *) +let sort m = List.sort (compare m) + +(** [is_page_order_valid m pages] returns [true] iff the page ordering given in + [pages] is valid under the rule map [m]. *) +let rec is_page_order_valid m pages = + let rec impl h = function + | h' :: t -> find_rule h h' m && impl h t + | [] -> true + in + match pages with h :: t -> impl h t && is_page_order_valid m t | [] -> true + +(** [parse_rules lst] parses the rules in the list [lst] stopping when + encountering an empty line. Returns a pair [(rule_map, tail)]. + [tail] starts the line after the empty line. *) +let parse_rules = + let re = Str.regexp_string "|" in + let rec impl acc = function + | "" :: t -> (acc, t) + | [] -> failwith "parse_rules.impl" + | h :: t -> ( + match List.map int_of_string (Str.split re h) with + | [ a; b ] -> impl (add_rule a b acc) t + | _ -> failwith "parse_rules.impl") + in + impl IntMap.empty + +(** [parse_page_orders lst] parses a list of page orders. *) +let parse_page_orders = + let re = Str.regexp_string "," in + let rec impl acc = function + | [] -> acc + | h :: t -> impl (List.map int_of_string (Str.split re h) :: acc) t + in + impl [] + +(** Read a rule map and list of pages from the file [fname]. *) +let read_file fname = + let lst = Aoc.strings_of_file fname in + let rule_map, t = parse_rules lst in + let page_orders = parse_page_orders t in + (rule_map, page_orders) + +let middle_elt lst = List.nth lst (List.length lst / 2) + +let part1 (rule_map, page_orders) = + List.filter (is_page_order_valid rule_map) page_orders + |> List.map middle_elt |> List.fold_left ( + ) 0 + +let part2 (rule_map, page_orders) = + List.filter (fun lst -> not (is_page_order_valid rule_map lst)) page_orders + |> List.map (sort rule_map) + |> List.map middle_elt |> List.fold_left ( + ) 0 + +let _ = Aoc.main read_file [ (string_of_int, part1); (string_of_int, part2) ]