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