Mercurial > lbo > hg > aoc22
changeset 42:95af5cf548c5
Day 04 Part 2
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Mon, 04 Dec 2023 23:02:33 +0100 |
parents | d4baa11fdac8 |
children | 865325736c6f |
files | 2023/day04.ml |
diffstat | 1 files changed, 60 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/2023/day04.ml Mon Dec 04 22:31:00 2023 +0100 +++ b/2023/day04.ml Mon Dec 04 23:02:33 2023 +0100 @@ -19,7 +19,7 @@ take_while1 (function '0' .. '9' -> true | _ -> false) >>| Int.of_string let int_listP = skip_many (char ' ') *> sep_by1 (skip_many1 (char ' ')) intP - let card_headP = (string "Card" *> skip_many1 (char ' ')) *> intP <* string ": " + let card_headP = string "Card" *> skip_many1 (char ' ') *> intP <* string ": " let sepP = string " | " let card = @@ -39,20 +39,15 @@ end module Part1 = struct - let to_set ilist = - Set.of_list - (module Int : Comparator.S - with type t = Int.t - and type comparator_witness = Int.comparator_witness) - ilist + let to_set ilist = Set.of_list (module Int) ilist let list_intersect a b = let sa = to_set a in let sb = to_set b in Set.inter sa sb - let card_value c = - Int.pow 2 (Set.length (list_intersect c.winning c.have)) / 2 + let winning_numbers card = Set.length (list_intersect card.winning card.have) + let card_value card = Int.pow 2 (winning_numbers card) / 2 let _test_parse ch = let process l = @@ -64,7 +59,7 @@ let f l = Out_channel.print_endline (process l) in In_channel.iter_lines ch ~f - let solve ch = + let _solve ch = let f acc line = let card = Parse.parse_card line in acc + card_value card @@ -73,4 +68,58 @@ Out_channel.printf "Total value is %d\n" result end -let () = Part1.solve In_channel.stdin +module Part2 = struct + let make_int_hashmap () : (int, int) Hashtbl.t = Hashtbl.create (module Int) + + (* format int/int hashmap *) + let _hm_to_string hm = + Sexp.to_string_hum (Hashtbl.sexp_of_t Int.sexp_of_t Int.sexp_of_t hm) + + (* create a counting sequence *) + let count_seq (from : int) (upto : int) : int Sequence.t = + let f c = + if Int.equal c upto then None + else Some (c, if upto > from then c + 1 else c - 1) + in + Sequence.unfold ~init:from ~f + + (* for a card, account for all won cards. *) + let process_card hm card = + (* create default entry for card *) + Hashtbl.update hm card.id ~f:(function None -> 1 | Some x -> x); + (* how many points have we won? *) + let winning = Part1.winning_numbers card in + (* how many copies does the current card have? *) + let copies_won = Option.value ~default:1 (Hashtbl.find hm card.id) in + (* updater function adding `copies_won` to each successive card *) + let updater card_id = + Hashtbl.update hm card_id ~f:(function + | None -> 1 + copies_won + | Some x -> x + copies_won) + in + (* sequence of cards to update *) + let copied_cards = count_seq (card.id + 1) (card.id + winning + 1) in + Sequence.iter copied_cards ~f:updater + + (* sequentially process available cards. *) + let process_cards ch = + let hm = make_int_hashmap () in + let f count line = + process_card hm (Parse.parse_card line); + count + 1 + in + let count = In_channel.fold_lines ch ~init:0 ~f in + (count, hm) + + (* count all scratchcards, up to the number of available cards. *) + let total_scratchcards hm cardcount = + let f ~key ~data sum = if key <= cardcount then sum + data else sum in + Hashtbl.fold hm ~init:0 ~f + + (* print solution *) + let _solve_process_cards ch = + let count, hm = process_cards ch in + Out_channel.printf "%d\n" (total_scratchcards hm count) +end + +let () = Part2._solve_process_cards In_channel.stdin