# --- Day 4: Passport Processing --- # You arrive at the airport only to realize that you grabbed your North Pole # Credentials instead of your passport. While these documents are extremely # similar, North Pole Credentials aren't issued by a country and therefore # aren't actually valid documentation for travel in most of the world. # It seems like you're not the only one having problems, though; a very long # line has formed for the automatic passport scanners, and the delay could # upset your travel itinerary. # Due to some questionable network security, you realize you might be able to # solve both of these problems at the same time. # The automatic passport scanners are slow because they're having trouble # detecting which passports have all required fields. The expected fields are # as follows: # byr (Birth Year) # iyr (Issue Year) # eyr (Expiration Year) # hgt (Height) # hcl (Hair Color) # ecl (Eye Color) # pid (Passport ID) # cid (Country ID) # Passport data is validated in batch files (your puzzle input). Each passport # is represented as a sequence of key:value pairs separated by spaces or # newlines. Passports are separated by blank lines. # Here is an example batch file containing four 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 # The first passport is valid - all eight fields are present. The second # passport is invalid - it is missing hgt (the Height field). # The third passport is interesting; the only missing field is cid, so it looks # like data from North Pole Credentials, not a passport at all! Surely, nobody # would mind if you made the system temporarily ignore missing cid fields. # Treat this "passport" as valid. # The fourth passport is missing two fields, cid and byr. Missing cid is fine, # but missing any other field is not, so this passport is invalid. # According to the above rules, your improved system would report 2 valid # passports. # Count the number of valid passports - those that have all required fields. # Treat cid as optional. In your batch file, how many passports are valid? with open("files/P4.txt", "r") as f: passports = [line.split() for line in f.read().strip().split("\n\n")] def part_1() -> None: valid_passports = 0 for passport in passports: if len(passport) > 7: valid_passports += 1 if len(passport) == 7: valid_fields = 0 for field in passport: if "cid" not in field: valid_fields += 1 if valid_fields == 7: valid_passports += 1 print(f"There are {valid_passports} valid passports") # --- Part Two --- # The line is moving more quickly now, but you overhear airport security # talking about how passports with invalid data are getting through. Better add # some data validation, quick! # You can continue to ignore the cid field, but each other field has strict # rules about what values are valid for automatic validation: # byr (Birth Year) - four digits; at least 1920 and at most 2002. # iyr (Issue Year) - four digits; at least 2010 and at most 2020. # eyr (Expiration Year) - four digits; at least 2020 and at most 2030. # hgt (Height) - a number followed by either cm or in: # If cm, the number must be at least 150 and at most 193. # If in, the number must be at least 59 and at most 76. # hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f. # ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth. # pid (Passport ID) - a nine-digit number, including leading zeroes. # cid (Country ID) - ignored, missing or not. # Your job is to count the passports where all required fields are both present # and valid according to the above rules. Here are some example values: # byr valid: 2002 # byr invalid: 2003 # hgt valid: 60in # hgt valid: 190cm # hgt invalid: 190in # hgt invalid: 190 # hcl valid: #123abc # hcl invalid: #123abz # hcl invalid: 123abc # ecl valid: brn # ecl invalid: wat # pid valid: 000000001 # pid invalid: 0123456789 # Here are some 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 # Here are some 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 # Count the number of valid passports - those that have all required fields and # valid values. Continue to treat cid as optional. In your batch file, how many # passports are valid? def is_valid(passport: dict[str, str]) -> bool: keys = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"] for key in keys: if key not in passport: return False if not any([passport[key] != 4 for key in ["byr", "iyr", "eyr"]]): return False if passport["byr"] < "1920" or passport["byr"] > "2002": return False if passport["iyr"] < "2010" or passport["iyr"] > "2020": return False if passport["eyr"] < "2020" or passport["eyr"] > "2030": return False if not any(["cm" in passport["hgt"], "in" in passport["hgt"]]): return False _hgt = int(passport["hgt"][:-2]) if "cm" in passport["hgt"]: if _hgt < 150 or _hgt > 193: return False if "in" in passport["hgt"]: if _hgt < 59 or _hgt > 76: return False if "#" not in passport["hcl"]: return False _hcl = passport["hcl"].split("#")[-1] if len(_hcl) != 6: return False try: int(_hcl, 16) except ValueError: return False if not any( [ passport["ecl"] in [ "amb", "blu", "brn", "gry", "grn", "hzl", "oth", ] ] ): return False if len(passport["pid"]) != 9: return False return True def part_2() -> None: valid_passports = 0 for passport in passports: _items: list[list[str]] = [ item.split(":") for item in passport if item ] passport_dict: dict[str, str] = dict(_items) if is_valid(passport_dict): valid_passports += 1 print(f"There are {valid_passports} valid passports") if __name__ == "__main__": part_1() part_2()