view 2023/day04.ml @ 41:d4baa11fdac8

Day 04 Part 1
author Lewin Bormann <lbo@spheniscida.de>
date Mon, 04 Dec 2023 22:31:00 +0100
parents
children 95af5cf548c5
line wrap: on
line source

open Angstrom
open Base
open Core
module Hashtbl = Base.Hashtbl

type card = { id : int; winning : int list; have : int list } [@@deriving sexp]

module Parse = struct
  (* Parse cards such as

     Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
     Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
     Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
     Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
     Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
     Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
  *)
  let intP =
    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 sepP = string " | "

  let card =
    let open Angstrom.Let_syntax in
    let%bind id = card_headP in
    let%bind winning = int_listP in
    let%bind _ = sepP in
    let%bind have = int_listP in
    return { id; winning; have }

  exception ParseExn of string

  let parse_card line =
    match parse_string ~consume:All card line with
    | Ok card -> card
    | Error e -> raise (ParseExn e)
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 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 _test_parse ch =
    let process l =
      let card = Parse.parse_card l in
      let sexp = Sexp.to_string_hum (sexp_of_card (Parse.parse_card l)) in
      let value = card_value card in
      Printf.sprintf "%s - value: %d" sexp value
    in
    let f l = Out_channel.print_endline (process l) in
    In_channel.iter_lines ch ~f

  let solve ch =
    let f acc line =
      let card = Parse.parse_card line in
      acc + card_value card
    in
    let result = In_channel.fold_lines ch ~init:0 ~f in
    Out_channel.printf "Total value is %d\n" result
end

let () = Part1.solve In_channel.stdin