You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
2.9 KiB

import sys
import typing
def main() -> int:
checked = []
for passport in _read_passports(sys.stdin):
if passport is None:
checked.append(False)
continue
checked.append(passport.is_valid())
print(f"total={len(checked)} valid={checked.count(True)}")
return 0
NoneString = typing.Optional[str]
VALID_EYE_COLORS = ("amb", "blu", "brn", "gry", "grn", "hzl", "oth")
class Passport:
byr: NoneString = None
cid: NoneString = None
ecl: NoneString = None
eyr: NoneString = None
hcl: NoneString = None
hgt: NoneString = None
iyr: NoneString = None
pid: NoneString = None
def is_valid(self) -> bool:
return (
self._is_year_in_range(self.byr, range(1920, 2003))
and self._is_year_in_range(self.iyr, range(2010, 2021))
and self._is_year_in_range(self.eyr, range(2020, 2031))
and self._has_valid_height()
and self._has_valid_hair_color()
and self.ecl in VALID_EYE_COLORS
and self._has_valid_passport_id()
)
def _has_valid_height(self) -> bool:
if self.hgt is None:
return False
height_value = 0
height_value_string = self.hgt.replace("cm", "").replace("in", "")
if not height_value_string.isdigit():
return False
height = int(height_value_string)
if self.hgt.endswith("cm") and height in range(150, 194):
return True
elif self.hgt.endswith("in") and height in range(59, 77):
return True
return False
def _has_valid_hair_color(self) -> bool:
if self.hcl is None:
return False
if not self.hcl.startswith("#"):
return False
hair_value = self.hcl.replace("#", "")
if len(hair_value) != 6:
return False
try:
_ = int(hair_value, 16)
return True
except ValueError:
return False
def _has_valid_passport_id(self) -> bool:
return (
self.pid is not None
and len(self.pid) == 9
and all([s.isdigit() for s in list(self.pid)])
)
def _is_year_in_range(self, value: NoneString, yr_range: range) -> bool:
if value is None:
return False
if len(value) != 4:
return False
if not value.isdigit():
return False
return int(value) in yr_range
def _read_passports(
instream: typing.TextIO,
) -> typing.Generator[typing.Optional[Passport], None, None]:
cur = Passport()
for i, line in enumerate(instream):
line = line.strip()
if line == "":
yield cur
cur = Passport()
for pair in line.split():
attr, value = pair.split(":", 1)
setattr(cur, attr, value)
yield cur
if __name__ == "__main__":
sys.exit(main())