let read_file_rev name = let ic = open_in name in let try_read () = try Some (input_line ic) with End_of_file -> None in let rec loop acc line = match try_read () with | Some "" -> loop ((String.trim line)::acc) "" | Some s -> loop acc (String.concat " " [line; s]) | None -> close_in ic; (String.trim line)::acc in loop [] "" type passport = { byr: int (* Birth Year *) ; iyr: int (* Issue Year*) ; eyr: int (* Expiration Year *) ; hgt: string (* Height *) ; hcl: string (* Hair Color *) ; ecl: string (* Eye Color *) ; pid: string (* Passport ID *) ; cid: string option (* Country ID *) } let passport_of_string s = let map = String.split_on_char ' ' s |> List.map (String.split_on_char ':') |> List.map (fun [k; v] -> (k, v)) in try Some { byr = int_of_string (List.assoc "byr" map) ; iyr = int_of_string (List.assoc "iyr" map) ; eyr = int_of_string (List.assoc "eyr" map) ; hgt = List.assoc "hgt" map ; hcl = List.assoc "hcl" map ; ecl = List.assoc "ecl" map ; pid = List.assoc "pid" map ; cid = List.assoc_opt "cid" map } with Not_found -> None let is_height_ok pspt = try let h = int_of_string (List.hd (String.split_on_char 'c' pspt.hgt)) in h >= 150 && h <= 193 with _ -> try let h = int_of_string (List.hd (String.split_on_char 'i' pspt.hgt)) in h >= 59 && h <= 76 with _ -> false let part1 l = List.filter_map passport_of_string l |> List.length let part2 l = List.filter_map passport_of_string l |> List.filter (fun p -> p.byr >= 1920 && p.byr <= 2002 && p.iyr >= 2010 && p.iyr <= 2020 && p.eyr >= 2020 && p.eyr <= 2030 && Str.string_match (Str.regexp {|^[0-9]+\(cm\|in\)$|}) p.hgt 0 && is_height_ok p && Str.string_match (Str.regexp {|^#[0-9a-f]+$|}) p.hcl 0 && String.length p.hcl = 7 && List.mem p.ecl ["amb"; "blu"; "brn"; "gry"; "grn"; "hzl"; "oth"] && String.length p.pid = 9 && Str.string_match (Str.regexp {|^[0-9]+$|}) p.pid 0) |> List.length let () = print_endline (string_of_int (read_file_rev filename |> part1)) let () = print_endline (string_of_int (read_file_rev filename |> part2))