Add cl-interpol
[adventofcode2020.git] / src / day04.lisp
CommitLineData
bb9e60f5
JK
1(asdf:load-system :adventofcode2020)
2(in-package #:adventofcode2020)
59b2be10 3(named-readtables:in-readtable :adventofcode2020)
bb9e60f5
JK
4
5(defun parse-passport (str-list)
6 (flet ((parser (str)
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)))
11
12(defun four-digits-test (pred)
13 (lambda (s)
14 (cl-ppcre:register-groups-bind
15 ((#'parse-integer value)) ("^([0-9]{4})$" s)
16 (funcall pred value))))
17
18(defparameter *required-field-tests*
a7a78c7a
JK
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)$" _)
24 (cond
25 ((string= unit "in") (<= 59 value 76))
26 ((string= unit "cm") (<= 150 value 193))
27 (t nil)))
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}$" _)
bb9e60f5
JK
31 'cid (constantly t)))
32
33(defun simple-validate-passport (passport)
34 (let ((required-fields '(byr iyr eyr hgt hcl ecl pid)))
a7a78c7a 35 (every #'identity (mapcar λ(assoc _ passport) required-fields))))
bb9e60f5
JK
36
37(defun complex-validate-passport (passport)
38 (flet ((check (pair)
39 (destructuring-bind (field value) pair
40 (funcall (getf *required-field-tests* field) value))))
41 (and
42 (simple-validate-passport passport)
43 (every #'identity (mapcar #'check passport)))))
44
45(day 04 input
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)))))
51
52(def-suite day04)
53(in-suite day04)
54
55(defvar *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")))
64
65(test parse-passports
66 (is (equal
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")
74 (hgt "59in")))
75 (mapcar #'parse-passport *passports*))))
76
77(test simple-validate-passports
78 (is (equal
79 '(t nil t nil)
80 (mapcar (compose #'simple-validate-passport #'parse-passport) *passports*))))
81
82(test passport-field-validators
83 (flet ((check (pair)
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")))
88
89 (is-true (check '(hgt "60in")))
90 (is-true (check '(hgt "190cm")))
91 (is-false (check '(hgt "190in")))
92 (is-false (check '(hgt "190")))
93
94 (is-true (check '(hcl "#123abc")))
95 (is-false (check '(hcl "#123abz")))
96 (is-false (check '(hcl "123abc")))
97
98 (is-true (check '(ecl "brn")))
99 (is-false (check '(ecl "wat")))
100
101 (is-true (check '(pid "000000001")))
102 (is-false (check '(pid "0123456789")))))
103
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"))))
113
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"))))
123
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))))
129
130(run! 'day04)