You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
3.2 KiB
OCaml

(* Common util. Should probably put this in a module or something.. *)
let get_one_line file =
try Some (input_line file) with
End_of_file -> None
let get_lines file =
let rec input lines =
match get_one_line file with
Some line -> input (line :: lines)
| None -> List.rev lines
in input []
let read_file file =
let channel = open_in(file) in
get_lines channel
let explode s = (* convert a string to a list of chars *)
let rec exp i l =
if i < 0 then l else exp (i - 1) (s.[i] :: l) in
exp (String.length s - 1) []
let implode l = String.of_seq (List.to_seq l)
(* --- *)
let fix_lines lines = (* puts each passport on 1 line *)
let glue s = if String.equal s "" then "\n" else " " in (* which char to concat the lines *)
let fix_line acc cur = acc ^ (glue cur) ^ cur in (* our reducer: concat lines with either space or newline *)
List.fold_left fix_line "" lines
let read_passport line = (* converts a line to an alist *)
let chunks = String.(split_on_char ' ' (trim line)) in (* split each string on space *)
let read_chunk chunk =
let parts = String.split_on_char ':' chunk in (* split each chunk on colon *)
List.(nth parts 0, nth parts 1) in (* return a tuple for each chunk *)
List.map read_chunk chunks
let required_fields = ["byr"; "iyr"; "eyr"; "hgt"; "hcl"; "ecl"; "pid";]
let passport_has_required_fields passport =
let has_key key = (* does a passport have a particular key *)
match List.assoc_opt key passport with
| Some _ -> true
| _ -> false
in
List.for_all has_key required_fields (* does it have ALL the keys *)
let is_in_range (min, max) n = int_of_string n >= min && int_of_string n <= max
let is_height_valid h =
let len = String.length h in
let units = String.sub h (len - 2) 2 in
let value = String.sub h 0 (len - 2) in
match units with
| "cm" -> is_in_range (150, 193) value
| "in" -> is_in_range (59, 76) value
| _ -> false
let is_char_hex c =
let hex_chars = "0123456789abcdef" in
String.contains hex_chars c
let is_hair_valid h =
let color = (String.sub h 1 (String.length h - 1)) in
String.length h = 7 &&
String.sub h 0 1 = "#" &&
List.for_all is_char_hex (explode color)
let is_eye_color_valid c =
List.mem c ["amb"; "blu"; "brn"; "gry"; "grn"; "hzl"; "oth";]
let is_pid_valid p =
String.length p = 9 &&
List.for_all (fun c -> List.mem c (explode "0123456789")) (explode p)
let is_passport_valid p =
is_in_range (1920, 2002) (List.assoc "byr" p) &&
is_in_range (2010, 2020) (List.assoc "iyr" p) &&
is_in_range (2020, 2030) (List.assoc "eyr" p) &&
is_height_valid (List.assoc "hgt" p) &&
is_hair_valid (List.assoc "hcl" p) &&
is_eye_color_valid (List.assoc "ecl" p) &&
is_pid_valid (List.assoc "pid" p)
let lines = read_file "day4.txt" |> fix_lines |> String.split_on_char '\n'
let passports = List.map read_passport lines
let passports_with_required_fields = List.filter passport_has_required_fields passports
let valid_passports = List.filter is_passport_valid passports_with_required_fields;;
Printf.printf "Number of passports with all required fields: %i\n" (List.length passports_with_required_fields);;
Printf.printf "Number of valid passports: %i\n" (List.length valid_passports);;