import re from dataclasses import dataclass from typing import Generator, List from aoc import BaseAssignment hgt_matcher = re.compile('(?P\d+)(?P(in|cm))') valid_units = { 'cm': {'min': 150, 'max': 193}, 'in': {'min': 59, 'max': 76} } hcl_matcher = re.compile('#[0-9a-f]{6}') @dataclass class Passport: byr: int = None iyr: int = None eyr: int = None hgt: str = None hcl: str = None ecl: str = None pid: str = None cid: str = None def __post_init__(self): self.byr = self.byr and int(self.byr) self.iyr = self.iyr and int(self.iyr) self.eyr = self.eyr and int(self.eyr) class Assignment(BaseAssignment): def create_passport(self, items: List[List]) -> Passport: return Passport(**dict(items)) def read_input(self, example=False) -> Generator: passport = [] for item in super().read_input(example): if item == '': yield self.create_passport(passport) passport = [] continue for field in item.split(' '): passport.append(field.split(':')) yield self.create_passport(passport) def valid_passport(self, passport: Passport): raise NotImplementedError('Please implement valid_passport') def run(self, input: Generator): valid_passports = 0 for passport in input: if self.valid_passport(passport): valid_passports += 1 return valid_passports class AssignmentOne(Assignment): def valid_passport(self, passport: Passport) -> bool: return all([ val for key, val in vars(passport).items() if key != 'cid' ]) class AssignmentTwo(Assignment): def valid_byr(self, byr: int): return byr and 1920 <= byr <= 2002 def valid_iyr(self, iyr: int): return iyr and 2010 <= iyr <= 2020 def valid_eyr(self, eyr: int): return eyr and 2020 <= eyr <= 2030 def valid_hgt(self, hgt: str): try: match = hgt_matcher.match(hgt).groupdict() unit = valid_units[match['unit']] return unit['min'] <= int(match['value']) <= unit['max'] except (TypeError, AttributeError): return False def valid_hcl(self, hcl: str): return hcl and hcl_matcher.match(hcl) def valid_ecl(self, ecl: str): return ecl and ecl in ( 'amb', 'blu', 'brn', 'gry', 'grn', 'hzl', 'oth' ) def valid_pid(self, pid: str): return pid and len(pid) == 9 def valid_passport(self, passport: Passport): return self.valid_byr(passport.byr) \ and self.valid_iyr(passport.iyr) \ and self.valid_eyr(passport.eyr) \ and self.valid_hgt(passport.hgt) \ and self.valid_hcl(passport.hcl) \ and self.valid_ecl(passport.ecl) \ and self.valid_pid(passport.pid)