diff options
author | Cody Hiar <cody@hiar.ca> | 2021-01-25 12:34:50 -0700 |
---|---|---|
committer | Cody Hiar <cody@hiar.ca> | 2021-01-25 12:34:50 -0700 |
commit | 34f71edd66e22b7d1e1b262d92d80e1bf335aa57 (patch) | |
tree | ab37eb9345f859b1de50aba7869ea39e35b700a1 /day4/day4.py |
Initial commit
Diffstat (limited to 'day4/day4.py')
-rw-r--r-- | day4/day4.py | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/day4/day4.py b/day4/day4.py new file mode 100644 index 0000000..f64356c --- /dev/null +++ b/day4/day4.py @@ -0,0 +1,104 @@ +"""Day 4 code.""" +import re +from itertools import chain, groupby + +REQUIRED_FIELDS = set(["byr", "ecl", "eyr", "hcl", "hgt", "iyr", "pid"]) +EYE_COLORS = ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"] +HCL_PATTERN = r"^#([a-fA-F0-9]{6})$" +PID_PATTERN = "^([0-9]{9})$" + + +def passport_str_to_list(passport_str): + """Convert string of passport data into list of key, values. + + The reqason we use tuple is so that when we eventually get a full list that + presents all of key values of passport we can convert it into a dict easier. + """ + pairs = passport_str.split(" ") + return [tuple(x.split(":")) for x in pairs] + + +def passport_parser(itr): + """Parse passport data into individual passports. + + Groupby allows us to generate a new group every time we encounter an empty + line which signifies the boundary of each passport's data. + """ + for k, groups in groupby(data, lambda x: x == ""): + if k is False: + yield chain.from_iterable(passport_str_to_list(x) for x in groups) + + +def passport_has_required_fields(passport): + """Check if passport is valid.""" + return REQUIRED_FIELDS.issubset(set(passport.keys())) + + +data = map(str.strip, open("input")) +passports = [dict(x) for x in passport_parser(data)] + +print("Part 1:", sum(map(passport_has_required_fields, passports))) + + +def byr_is_valid(passport): + """Check birthyear is valid.""" + return 1920 <= int(passport["byr"]) <= 2002 + + +def iyr_is_valid(passport): + """Check issue year is valid.""" + return 2010 <= int(passport["iyr"]) <= 2020 + + +def eyr_is_valid(passport): + """Check expiration year is valid.""" + return 2020 <= int(passport["eyr"]) <= 2030 + + +def hgt_is_valid(passport): + """Check the hieght is valid.""" + hgt = passport["hgt"] + if "cm" in hgt: + val = int(hgt[: hgt.find("cm")]) + if 150 <= val <= 193: + return True + elif "in" in hgt: + val = int(hgt[: hgt.find("in")]) + if 59 <= val <= 76: + return True + return False + + +def hcl_is_valid(passport): + """Check hair color is valid.""" + return re.match(HCL_PATTERN, passport["hcl"]) + + +def ecl_is_valid(passport): + """Check if eye color is valid.""" + return passport["ecl"] in EYE_COLORS + + +def pid_is_valid(passport): + """Check passport id is valid.""" + return re.match(PID_PATTERN, passport["pid"]) + + +VALIDATION_TESTS = [ + byr_is_valid, + iyr_is_valid, + eyr_is_valid, + hgt_is_valid, + hcl_is_valid, + ecl_is_valid, + pid_is_valid, +] + + +def passport_is_valid(passport): + """Check if a passport is valid.""" + return all(func(passport) for func in VALIDATION_TESTS) + + +passports_with_required_fields = filter(passport_has_required_fields, passports) +print("Answer 2", sum(map(passport_is_valid, passports_with_required_fields))) |