day 4
This commit is contained in:
parent
28af6b3b9e
commit
d20143a6d2
4 changed files with 1371 additions and 2 deletions
|
@ -1,6 +1,7 @@
|
|||
open Aoc2020
|
||||
|
||||
let days = [|Day1.main; Day2.main; Day3.main|]
|
||||
let days =
|
||||
[|Day1.main; Day2.main; Day3.main; Day4.main|]
|
||||
|
||||
let _ =
|
||||
match Array.to_list Sys.argv with
|
||||
|
|
237
day4.ml
Normal file
237
day4.ml
Normal file
|
@ -0,0 +1,237 @@
|
|||
|
||||
type acc = {cur: string list; all: string list}
|
||||
|
||||
let chunks seq =
|
||||
let make strs all =
|
||||
match strs with
|
||||
| [] -> all
|
||||
| _::_ -> String.concat " " (List.rev strs) :: all in
|
||||
let next acc str =
|
||||
if str = "" then
|
||||
{cur = []; all = make acc.cur acc.all}
|
||||
else
|
||||
{acc with cur = str :: acc.cur} in
|
||||
let res' = Seq.fold_left next {cur = []; all = []} seq in
|
||||
List.rev (make res'.cur res'.all)
|
||||
|
||||
let%test_module _ = (module struct
|
||||
let chunks' lst = chunks (List.to_seq lst)
|
||||
|
||||
let%test "ε" = chunks' [] = []
|
||||
let%test "a b c" = chunks' ["a"; "b"; "c"] = ["a b c"]
|
||||
let%test "a b | c" = chunks' ["a"; "b"; ""; "c"] = ["a b"; "c"]
|
||||
let%test "a b || c" = chunks' ["a"; "b"; ""; ""; "c"] = ["a b"; "c"]
|
||||
let%test "a b | c |" = chunks' ["a"; "b"; ""; "c"; ""] = ["a b"; "c"]
|
||||
end)
|
||||
|
||||
let chunk_input = Bracket.infile_lines ~line:Fun.id ~of_seq:chunks
|
||||
|
||||
|
||||
type field = string * string
|
||||
|
||||
let fields0 str =
|
||||
let drop k str = String.(sub str k (length str - k)) in
|
||||
let get str =
|
||||
if str = "" then None else
|
||||
Scanf.sscanf str "%3s : %s %n"
|
||||
(fun k v len -> Some ((k, v), drop len str)) in
|
||||
Seq.unfold get str
|
||||
|
||||
let%test_module _ = (module struct
|
||||
let fields' str = List.of_seq (fields0 str)
|
||||
|
||||
let%test "ε" = fields' "" = []
|
||||
let%test "xxx:b" = fields' "xxx:b" = [("xxx", "b")]
|
||||
let%test "abc:x def:y" = fields' "abc:x def:y" = [("abc", "x"); ("def", "y")]
|
||||
end)
|
||||
|
||||
|
||||
module StrSet = Set_ext.Make(String)
|
||||
|
||||
let optional_fields = StrSet.singleton "cid"
|
||||
|
||||
let needed_fields =
|
||||
StrSet.of_list ["byr"; "iyr"; "eyr"; "hgt"; "hcl"; "ecl"; "pid"]
|
||||
|
||||
let all_fields = StrSet.add "cid" needed_fields
|
||||
|
||||
let field_keys str =
|
||||
fields0 str |> Seq.map fst |> StrSet.of_seq
|
||||
|
||||
let valid_set set =
|
||||
StrSet.subset needed_fields set &&
|
||||
StrSet.subset set all_fields
|
||||
|
||||
let%test_module _ = (module struct
|
||||
let valid_list lst = List.to_seq lst |> StrSet.of_seq |> valid_set
|
||||
|
||||
let%test "ex1" = valid_list
|
||||
["ecl"; "pid"; "eyr"; "hcl"; "byr"; "iyr"; "cid"; "hgt"]
|
||||
|
||||
let%test "ex2" = not @@ valid_list
|
||||
["iyr"; "ecl"; "cid"; "eyr"; "pid"; "hcl"; "byr"]
|
||||
|
||||
let%test "ex3" = valid_list
|
||||
["hcl"; "iyr"; "eyr"; "ecl"; "pid"; "byr"; "hgt"]
|
||||
|
||||
let%test "ex4" = not @@ valid_list
|
||||
["hcl"; "eyr"; "pid"; "iyr"; "ecl"; "hgt"]
|
||||
end)
|
||||
|
||||
|
||||
|
||||
let main1 file =
|
||||
let sets = chunk_input file |> List.map field_keys in
|
||||
let len_all = List.length sets in
|
||||
let len_valid = List.(length (filter valid_set sets)) in
|
||||
Printf.printf "%d valid out of %d\n" len_valid len_all
|
||||
|
||||
let%expect_test "part 1" =
|
||||
main1 "../../data/day4";
|
||||
[%expect{| 233 valid out of 288 |}]
|
||||
|
||||
|
||||
module StrMap = Map.Make(String)
|
||||
|
||||
module Validators = struct
|
||||
let (=~?) str rx =
|
||||
Re.Pcre.(pmatch ~rex:(regexp rx) str)
|
||||
|
||||
let (=~) str rx =
|
||||
try Some (Re.Pcre.(extract ~rex:(regexp rx) str))
|
||||
with Not_found -> None
|
||||
|
||||
let year_range lo hi x =
|
||||
x =~? {|^\d{4}$|} &&
|
||||
match int_of_string x with
|
||||
| exception Invalid_argument _ -> false
|
||||
| x -> x >= lo && x <= hi
|
||||
|
||||
let%test _ = year_range 1900 2000 "1950"
|
||||
let%test _ = year_range 1900 2000 "1900"
|
||||
let%test _ = year_range 1900 2000 "2000"
|
||||
let%test _ = not (year_range 1900 2000 "0950")
|
||||
let%test _ = not (year_range 1900 2000 "01950")
|
||||
let%test _ = not (year_range 1900 2000 "")
|
||||
let%test _ = not (year_range 1900 2000 "lmao")
|
||||
let%test _ = not (year_range 1900 2000 "1950aaaa")
|
||||
|
||||
let height str =
|
||||
match str =~ {|^(\d+)(cm|in)$|} with
|
||||
| Some res ->
|
||||
let num = int_of_string res.(1) in
|
||||
if res.(2) = "cm" then
|
||||
num >= 150 && num <= 193
|
||||
else
|
||||
num >= 59 && num <= 76
|
||||
| None -> false
|
||||
|
||||
let%test _ = height "150cm"
|
||||
let%test _ = height "175cm"
|
||||
let%test _ = height "193cm"
|
||||
let%test _ = not (height "200cm") (* hmmmmmmmmmm *)
|
||||
let%test _ = not (height "50cm")
|
||||
let%test _ = not (height "1500mm")
|
||||
let%test _ = height "59in"
|
||||
let%test _ = height "69in"
|
||||
let%test _ = height "76in"
|
||||
let%test _ = not (height "6in")
|
||||
let%test _ = not (height "600in")
|
||||
let%test _ = not (height "5ft")
|
||||
let%test _ = not (height "")
|
||||
|
||||
let hex_col str = str =~? {|^#[0-9a-f]{6}$|}
|
||||
|
||||
let%test _ = hex_col "#000000"
|
||||
let%test _ = not (hex_col "a#000000")
|
||||
let%test _ = not (hex_col "#000000a")
|
||||
let%test _ = hex_col "#abcabc"
|
||||
let%test _ = not (hex_col "#ABCABC") (* according to the spec anyway *)
|
||||
|
||||
let eye_col str = str =~? {|^(amb|blu|brn|gry|grn|hzl|oth)$|}
|
||||
|
||||
let%test _ = eye_col "blu"
|
||||
let%test _ = not (eye_col "blue")
|
||||
let%test _ = not (eye_col "")
|
||||
|
||||
let pid str = str =~? {|^[0-9]{9}$|}
|
||||
|
||||
let%test _ = pid "012345678"
|
||||
let%test _ = pid "123456789"
|
||||
let%test _ = not (pid "12345678")
|
||||
let%test _ = not (pid "1234567890")
|
||||
|
||||
let map = StrMap.of_seq @@ List.to_seq
|
||||
["byr", year_range 1920 2002;
|
||||
"iyr", year_range 2010 2020;
|
||||
"eyr", year_range 2020 2030;
|
||||
"hgt", height;
|
||||
"hcl", hex_col;
|
||||
"ecl", eye_col;
|
||||
"pid", pid;
|
||||
"cid", fun _ -> true]
|
||||
|
||||
let validate k v =
|
||||
StrMap.find_opt k map
|
||||
|> Option.fold ~none:false ~some:(fun f -> f v)
|
||||
end
|
||||
let validate = Validators.validate
|
||||
|
||||
|
||||
let fields str = fields0 str |> StrMap.of_seq
|
||||
|
||||
let valid_keys map =
|
||||
StrMap.to_seq map |> Seq.map fst |> StrSet.of_seq |> valid_set
|
||||
|
||||
let valid_map map = valid_keys map && StrMap.for_all validate map
|
||||
|
||||
let%test_module _ = (module struct
|
||||
let valid_list lst = List.to_seq lst |> StrMap.of_seq |> valid_map
|
||||
|
||||
let%test "ex1" = not @@ valid_list
|
||||
[("eyr", "1972"); ("cid", "100"); ("hcl", "#18171d"); ("ecl", "amb");
|
||||
("hgt", "170"); ("pid", "186cm"); ("iyr", "2018"); ("byr", "1926")]
|
||||
|
||||
let%test "ex2" = not @@ valid_list
|
||||
[("iyr", "2019"); ("hcl", "#602927"); ("eyr", "1967"); ("hgt", "170cm");
|
||||
("ecl", "grn"); ("pid", "012533040"); ("byr", "1946")]
|
||||
|
||||
let%test "ex3" = not @@ valid_list
|
||||
[("hcl", "dab227"); ("iyr", "2012"); ("ecl", "brn"); ("hgt", "182cm");
|
||||
("pid", "021572410"); ("eyr", "2020"); ("byr", "1992"); ("cid", "277")]
|
||||
|
||||
let%test "ex4" = not @@ valid_list
|
||||
[("hgt", "59cm"); ("ecl", "zzz"); ("eyr", "2038"); ("hcl", "74454a");
|
||||
("iyr", "2023"); ("pid", "3556412378"); ("byr", "2007")]
|
||||
|
||||
let%test "ex5" = valid_list
|
||||
[("pid", "087499704"); ("hgt", "74in"); ("ecl", "grn"); ("iyr", "2012");
|
||||
("eyr", "2030"); ("byr", "1980"); ("hcl", "#623a2f")]
|
||||
|
||||
let%test "ex6" = valid_list
|
||||
[("eyr", "2029"); ("ecl", "blu"); ("cid", "129"); ("byr", "1989");
|
||||
("iyr", "2014"); ("pid", "896056539"); ("hcl", "#a97842");
|
||||
("hgt", "165cm")]
|
||||
|
||||
let%test "ex7" = valid_list
|
||||
[("hcl", "#888785"); ("hgt", "164cm"); ("byr", "2001"); ("iyr", "2015");
|
||||
("cid", "88"); ("pid", "545766238"); ("ecl", "hzl"); ("eyr", "2022")]
|
||||
|
||||
let%test "ex8" = valid_list
|
||||
[("iyr", "2010"); ("hgt", "158cm"); ("hcl", "#b6652a"); ("ecl", "blu");
|
||||
("byr", "1944"); ("eyr", "2021"); ("pid", "093154719")]
|
||||
end)
|
||||
|
||||
|
||||
let main2 file =
|
||||
let maps = chunk_input file |> List.map fields in
|
||||
let len_all = List.length maps in
|
||||
let len_valid = List.(length (filter valid_map maps)) in
|
||||
Printf.printf "%d valid out of %d\n" len_valid len_all
|
||||
|
||||
let%expect_test _ =
|
||||
main2 "../../data/day4";
|
||||
[%expect{| 111 valid out of 288 |}]
|
||||
|
||||
|
||||
let main = Misc.main 4 [|main1; main2|]
|
4
dune
4
dune
|
@ -7,4 +7,6 @@
|
|||
(public_name aoc2020)
|
||||
(inline_tests)
|
||||
(modules (:standard \ aoc2020))
|
||||
(preprocess (pps ppx_deriving.std ppx_inline_test ppx_expect)))
|
||||
(libraries re)
|
||||
(preprocess
|
||||
(pps ppx_deriving.std ppx_inline_test ppx_expect)))
|
||||
|
|
Loading…
Add table
Reference in a new issue