1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
"""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)))
|