Skip to content

Instantly share code, notes, and snippets.

@seblin
Created December 4, 2020 10:59
Show Gist options
  • Save seblin/154714ccb95fc654855a4995a9b3e710 to your computer and use it in GitHub Desktop.
Save seblin/154714ccb95fc654855a4995a9b3e710 to your computer and use it in GitHub Desktop.
# https://adventofcode.com/2020/day/4
from operator import methodcaller
from pathlib import Path
INPUT_FILE = Path(__file__).parent / "input.txt"
class Document:
PASSPORT_KEYS = {
"byr", # (Birth Year)
"iyr", # (Issue Year)
"eyr", # (Expiration Year)
"hgt", # (Height)
"hcl", # (Hair Color)
"ecl", # (Eye Color)
"pid", # (Passport ID)
"cid", # (Country ID)
}
NORTHPOLE_CREDENTIALS = PASSPORT_KEYS - {"cid"}
def __init__(self, mapping):
self.mapping = mapping
def is_valid(self):
validators = [
getattr(self, name) for name in dir(self)
if name.startswith("has_valid")
]
return (
self.has_valid_keys()
and all(valid() for valid in validators)
)
def has_valid_keys(self):
return (
self.mapping.keys() == self.PASSPORT_KEYS
or self.mapping.keys() == self.NORTHPOLE_CREDENTIALS
)
def has_valid_birth_year(self):
return 1920 <= int(self.mapping["byr"]) <= 2002
def has_valid_issue_year(self):
return 2010 <= int(self.mapping["iyr"]) <= 2020
def has_valid_expiration_year(self):
return 2020 <= int(self.mapping["eyr"]) <= 2030
def has_valid_height(self):
height = self.mapping["hgt"][:-2]
unit = self.mapping["hgt"][-2:]
return (
unit == "cm" and 150 <= int(height) <= 193
or unit == "in" and 59 <= int(height) <= 76
)
def has_valid_hair_color(self):
prefix, *code = self.mapping["hcl"]
allowed = set("0123456789abcdef")
return (
prefix == "#" and len(code) == 6
and set(code) <= allowed
)
def has_valid_eye_color(self):
valid_colors = [
"amb", "blu", "brn", "gry", "grn", "hzl", "oth"
]
return self.mapping["ecl"] in valid_colors
def has_valid_passport_id(self):
pid = self.mapping["pid"]
return pid.isdigit() and len(pid) == 9
@classmethod
def from_stream(cls, stream):
mapping = {}
for line in stream:
if not line.strip():
break
for part in line.split():
key, value = part.split(":")
mapping[key] = value
return cls(mapping)
def get_documents(stream):
while True:
document = Document.from_stream(stream)
if not document.mapping:
break
yield document
def main():
with INPUT_FILE.open() as stream:
documents = list(get_documents(stream))
for validator in ("has_valid_keys", "is_valid"):
num_valid = sum(map(methodcaller(validator), documents))
print(validator, num_valid)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment