1 (asdf:load-system :adventofcode2020)
2 (in-package #:adventofcode2020)
3 (named-readtables:in-readtable :adventofcode2020)
5 (defun parse-passport (str-list)
7 (->> (split-sequence #\Space str)
8 (mapcar (fn* (destructuring-bind (a b) (split-sequence #\: _)
9 (list (read-from-string a) b)))))))
10 (mapcan #'parser str-list)))
12 (defun four-digits-test (pred)
14 (cl-ppcre:register-groups-bind
15 ((#'parse-integer value)) ("^([0-9]{4})$" s)
16 (funcall pred value))))
18 (defparameter *required-field-tests*
19 (list 'byr (four-digits-test λ(<= 1920 _ 2002))
20 'iyr (four-digits-test λ(<= 2010 _ 2020))
21 'eyr (four-digits-test λ(<= 2020 _ 2030))
22 'hgt λ(cl-ppcre:register-groups-bind
23 ((#'parse-integer value) unit) ("^([0-9]+)(in|cm)$" _)
25 ((string= unit "in") (<= 59 value 76))
26 ((string= unit "cm") (<= 150 value 193))
28 'hcl λ(cl-ppcre:scan "^#[0-9a-f]{6}$" _)
29 'ecl λ(cl-ppcre:scan "^(amb|blu|brn|gry|grn|hzl|oth)$" _)
30 'pid λ(cl-ppcre:scan "^[0-9]{9}$" _)
33 (defun simple-validate-passport (passport)
34 (let ((required-fields '(byr iyr eyr hgt hcl ecl pid)))
35 (every #'identity (mapcar λ(assoc _ passport) required-fields))))
37 (defun complex-validate-passport (passport)
39 (destructuring-bind (field value) pair
40 (funcall (getf *required-field-tests* field) value))))
42 (simple-validate-passport passport)
43 (every #'identity (mapcar #'check passport)))))
46 (let ((passports (-<>> (list-from input)
47 (split-sequence "" <> :test #'string=)
48 (mapcar #'parse-passport))))
49 (part1 (count t (mapcar #'simple-validate-passport passports)))
50 (part2 (count t (mapcar #'complex-validate-passport passports)))))
56 '(("ecl:gry pid:860033327 eyr:2020 hcl:#fffffd"
57 "byr:1937 iyr:2017 cid:147 hgt:183cm")
58 ("iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884"
59 "hcl:#cfa07d byr:1929")
60 ("hcl:#ae17e1 iyr:2013" "eyr:2024"
61 "ecl:brn pid:760753108 byr:1931" "hgt:179cm")
62 ("hcl:#cfa07d eyr:2025 pid:166559648"
63 "iyr:2011 ecl:brn hgt:59in")))
67 '(((ecl "gry") (pid "860033327") (eyr "2020") (hcl "#fffffd") (byr "1937")
68 (iyr "2017") (cid "147") (hgt "183cm"))
69 ((iyr "2013") (ecl "amb") (cid "350") (eyr "2023") (pid "028048884")
70 (hcl "#cfa07d") (byr "1929"))
71 ((hcl "#ae17e1") (iyr "2013") (eyr "2024") (ecl "brn") (pid "760753108")
72 (byr "1931") (hgt "179cm"))
73 ((hcl "#cfa07d") (eyr "2025") (pid "166559648") (iyr "2011") (ecl "brn")
75 (mapcar #'parse-passport *passports*))))
77 (test simple-validate-passports
80 (mapcar (compose #'simple-validate-passport #'parse-passport) *passports*))))
82 (test passport-field-validators
84 (destructuring-bind (field value) pair
85 (funcall (getf *required-field-tests* field) value))))
86 (is-true (check '(byr "2002")))
87 (is-false (check '(byr "2003")))
89 (is-true (check '(hgt "60in")))
90 (is-true (check '(hgt "190cm")))
91 (is-false (check '(hgt "190in")))
92 (is-false (check '(hgt "190")))
94 (is-true (check '(hcl "#123abc")))
95 (is-false (check '(hcl "#123abz")))
96 (is-false (check '(hcl "123abc")))
98 (is-true (check '(ecl "brn")))
99 (is-false (check '(ecl "wat")))
101 (is-true (check '(pid "000000001")))
102 (is-false (check '(pid "0123456789")))))
104 (defvar *valid-passports*
105 '(((pid "087499704") (hgt "74in") (ecl "grn") (iyr "2012") (eyr "2030")
106 (byr "1980") (hcl "#623a2f"))
107 ((eyr "2029") (ecl "blu") (cid "129") (byr "1989") (iyr "2014")
108 (pid "896056539") (hcl "#a97842") (hgt "165cm"))
109 ((hcl "#888785") (hgt "164cm") (byr "2001") (iyr "2015") (cid "88")
110 (pid "545766238") (ecl "hzl") (eyr "2022"))
111 ((iyr "2010") (hgt "158cm") (hcl "#b6652a") (ecl "blu") (byr "1944")
112 (eyr "2021") (pid "093154719"))))
114 (defvar *invalid-passports*
115 '(((eyr "1972") (cid "100") (hcl "#18171d") (ecl "amb") (hgt "170")
116 (pid "186cm") (iyr "2018") (byr "1926"))
117 ((iyr "2019") (hcl "#602927") (eyr "1967") (hgt "170cm") (ecl "grn")
118 (pid "012533040") (byr "1946"))
119 ((hcl "dab227") (iyr "2012") (ecl "brn") (hgt "182cm") (pid "021572410")
120 (eyr "2020") (byr "1992") (cid "277"))
121 ((hgt "59cm") (ecl "zzz") (eyr "2038") (hcl "74454a") (iyr "2023")
122 (pid "3556412378") (byr "2007"))))
124 (test complex-validate-passports
125 (loop for passport in *valid-passports*
126 do (is-true (complex-validate-passport passport)))
127 (loop for passport in *invalid-passports*
128 do (is-false (complex-validate-passport passport))))