(asdf:load-system :adventofcode2020) (in-package #:adventofcode2020) (named-readtables:in-readtable :adventofcode2020) (defun parse-passport (str-list) (flet ((parser (str) (->> (split-sequence #\Space str) (mapcar (fn* (destructuring-bind (a b) (split-sequence #\: _) (list (read-from-string a) b))))))) (mapcan #'parser str-list))) (defun four-digits-test (pred) (lambda (s) (cl-ppcre:register-groups-bind ((#'parse-integer value)) ("^([0-9]{4})$" s) (funcall pred value)))) (defparameter *required-field-tests* (list 'byr (four-digits-test λ(<= 1920 _ 2002)) 'iyr (four-digits-test λ(<= 2010 _ 2020)) 'eyr (four-digits-test λ(<= 2020 _ 2030)) 'hgt λ(cl-ppcre:register-groups-bind ((#'parse-integer value) unit) ("^([0-9]+)(in|cm)$" _) (cond ((string= unit "in") (<= 59 value 76)) ((string= unit "cm") (<= 150 value 193)) (t nil))) 'hcl λ(cl-ppcre:scan "^#[0-9a-f]{6}$" _) 'ecl λ(cl-ppcre:scan "^(amb|blu|brn|gry|grn|hzl|oth)$" _) 'pid λ(cl-ppcre:scan "^[0-9]{9}$" _) 'cid (constantly t))) (defun simple-validate-passport (passport) (let ((required-fields '(byr iyr eyr hgt hcl ecl pid))) (every #'identity (mapcar λ(assoc _ passport) required-fields)))) (defun complex-validate-passport (passport) (flet ((check (pair) (destructuring-bind (field value) pair (funcall (getf *required-field-tests* field) value)))) (and (simple-validate-passport passport) (every #'identity (mapcar #'check passport))))) (day 04 input (let ((passports (-<>> (list-from input) (split-sequence "" <> :test #'string=) (mapcar #'parse-passport)))) (part1 (count t (mapcar #'simple-validate-passport passports))) (part2 (count t (mapcar #'complex-validate-passport passports))))) (def-suite day04) (in-suite day04) (defvar *passports* '(("ecl:gry pid:860033327 eyr:2020 hcl:#fffffd" "byr:1937 iyr:2017 cid:147 hgt:183cm") ("iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884" "hcl:#cfa07d byr:1929") ("hcl:#ae17e1 iyr:2013" "eyr:2024" "ecl:brn pid:760753108 byr:1931" "hgt:179cm") ("hcl:#cfa07d eyr:2025 pid:166559648" "iyr:2011 ecl:brn hgt:59in"))) (test parse-passports (is (equal '(((ecl "gry") (pid "860033327") (eyr "2020") (hcl "#fffffd") (byr "1937") (iyr "2017") (cid "147") (hgt "183cm")) ((iyr "2013") (ecl "amb") (cid "350") (eyr "2023") (pid "028048884") (hcl "#cfa07d") (byr "1929")) ((hcl "#ae17e1") (iyr "2013") (eyr "2024") (ecl "brn") (pid "760753108") (byr "1931") (hgt "179cm")) ((hcl "#cfa07d") (eyr "2025") (pid "166559648") (iyr "2011") (ecl "brn") (hgt "59in"))) (mapcar #'parse-passport *passports*)))) (test simple-validate-passports (is (equal '(t nil t nil) (mapcar (compose #'simple-validate-passport #'parse-passport) *passports*)))) (test passport-field-validators (flet ((check (pair) (destructuring-bind (field value) pair (funcall (getf *required-field-tests* field) value)))) (is-true (check '(byr "2002"))) (is-false (check '(byr "2003"))) (is-true (check '(hgt "60in"))) (is-true (check '(hgt "190cm"))) (is-false (check '(hgt "190in"))) (is-false (check '(hgt "190"))) (is-true (check '(hcl "#123abc"))) (is-false (check '(hcl "#123abz"))) (is-false (check '(hcl "123abc"))) (is-true (check '(ecl "brn"))) (is-false (check '(ecl "wat"))) (is-true (check '(pid "000000001"))) (is-false (check '(pid "0123456789"))))) (defvar *valid-passports* '(((pid "087499704") (hgt "74in") (ecl "grn") (iyr "2012") (eyr "2030") (byr "1980") (hcl "#623a2f")) ((eyr "2029") (ecl "blu") (cid "129") (byr "1989") (iyr "2014") (pid "896056539") (hcl "#a97842") (hgt "165cm")) ((hcl "#888785") (hgt "164cm") (byr "2001") (iyr "2015") (cid "88") (pid "545766238") (ecl "hzl") (eyr "2022")) ((iyr "2010") (hgt "158cm") (hcl "#b6652a") (ecl "blu") (byr "1944") (eyr "2021") (pid "093154719")))) (defvar *invalid-passports* '(((eyr "1972") (cid "100") (hcl "#18171d") (ecl "amb") (hgt "170") (pid "186cm") (iyr "2018") (byr "1926")) ((iyr "2019") (hcl "#602927") (eyr "1967") (hgt "170cm") (ecl "grn") (pid "012533040") (byr "1946")) ((hcl "dab227") (iyr "2012") (ecl "brn") (hgt "182cm") (pid "021572410") (eyr "2020") (byr "1992") (cid "277")) ((hgt "59cm") (ecl "zzz") (eyr "2038") (hcl "74454a") (iyr "2023") (pid "3556412378") (byr "2007")))) (test complex-validate-passports (loop for passport in *valid-passports* do (is-true (complex-validate-passport passport))) (loop for passport in *invalid-passports* do (is-false (complex-validate-passport passport)))) (run! 'day04)