Skip to content

Instantly share code, notes, and snippets.

@shayelkin
Created December 5, 2020 04:55
Show Gist options
  • Save shayelkin/06437d1da05787ec1db44e1897d50c39 to your computer and use it in GitHub Desktop.
Save shayelkin/06437d1da05787ec1db44e1897d50c39 to your computer and use it in GitHub Desktop.
AoC 2020 Day 4. Solve by generating a huge regex
;; AoC 2020 Day 4. Solve by generating a huge regex.
(require '[clojure.string :as s])
(def sample-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")
(def mandatory-fields
{"byr" "19[2-9]\\d|200[0-2]"
"iyr" "20(?:1\\d|20)"
"eyr" "20(?:2\\d|30)"
"hgt" "(?:59|6\\d|7[0-6])in|(?:1(?:[5-8]\\d|9[0-3]))cm"
"hcl" "#[\\da-f]{6}"
"ecl" "amb|blu|brn|gry|grn|hzl|oth"
"pid" "\\d{9}"})
(def re-valid-passport
(letfn [(make-inner [xs start-idx]
(s/join
["(?:" "\\b(?:"
;; trick: to make sure we don't catch the same key twice, add an
;; empty capturing group after each field, then re-match on them all.
;; if we didn't match on all the fields, we'd be lacking groups to
;; match on, and fail.
;; note that we may have previous fields, so we need an index to start with
(s/join "()|" xs)
"()):\\S+(?:\\s+|$)" ;; assume field values are of length ≥ 1
"){" (count xs) "}"
(s/join
(map (partial str "\\")
(range start-idx (+ start-idx (count xs)))))]))]
(re-pattern
(str (make-inner (keys mandatory-fields) 1)
"|"
(make-inner (conj (keys mandatory-fields) "cid") (inc (count mandatory-fields)))
"(?:\\v|$)"))))
;; => #"(?:\b(?:byr()|iyr()|eyr()|hgt()|hcl()|ecl()|pid()):\S+(?:\s+|$)){7}\1\2\3\4\5\6\7|(?:\b(?:cid()|byr()|iyr()|eyr()|hgt()|hcl()|ecl()|pid()):\S+(?:\s+|$)){8}\8\9\10\11\12\13\14\15(?:\v|$)"
(defn count-passports [re passports]
(count (re-seq re passports)))
(assert (= 2 (count-passports re-valid-fields sample-passports)))
(def re-valid-passport-with-vals
(letfn [(make-inner [xs start-idx]
(s/join
["(?:" "\\b(?:"
(s/join
"()|"
(map (partial apply (partial format "%s:(?:%s)")) xs))
"())(?:\\s+|$)" ;; assume field values are of length ≥ 1
"){" (count xs) "}"
(s/join
(map (partial str "\\")
(range start-idx (+ start-idx (count xs)))))]))]
(re-pattern
(str (make-inner mandatory-fields 1)
"|"
(make-inner (conj mandatory-fields ["cid" ".+"]) (inc (count mandatory-fields)))
"(?:\\v|$)"))))
;; => #"(?:\b(?:byr:(?:19[2-9]\d|200[0-2])()|iyr:(?:20(?:1\d|20))()|eyr:(?:20(?:2\d|30))()|hgt:(?:(?:59|6\d|7[0-6])in|(?:1(?:[5-8]\d|9[0-3]))cm)()|hcl:(?:#[\da-f]{6})()|ecl:(?:amb|blu|brn|gry|grn|hzl|oth)()|pid:(?:\d{9})())(?:\s+|$)){7}\1\2\3\4\5\6\7|(?:\b(?:byr:(?:19[2-9]\d|200[0-2])()|iyr:(?:20(?:1\d|20))()|eyr:(?:20(?:2\d|30))()|hgt:(?:(?:59|6\d|7[0-6])in|(?:1(?:[5-8]\d|9[0-3]))cm)()|hcl:(?:#[\da-f]{6})()|ecl:(?:amb|blu|brn|gry|grn|hzl|oth)()|pid:(?:\d{9})()|cid:(?:.+)())(?:\s+|$)){8}\8\9\10\11\12\13\14\15(?:\v|$)"
(def sample-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")
(assert (= 0 (count-passports re-valid-passport-with-vals sample-invalid-passports)))
(def sample-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")
(assert (= 4 (count-passports re-valid-passport-with-vals sample-valid-passports)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment