"""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)))