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
|
from dataclasses import dataclass
@dataclass
class SueQuality:
quality: int | None = None
def __eq__(self, other):
return self.quality is None or other.quality is None or self.quality == other.quality
def __gt__(self, other):
return self.quality is None or other.quality is None or self.quality > other.quality
def __lt__(self, other):
return self.quality is None or other.quality is None or self.quality < other.quality
@dataclass
class Sue:
children: SueQuality = SueQuality()
cats: SueQuality = SueQuality()
samoyeds: SueQuality = SueQuality()
pomeranians: SueQuality = SueQuality()
akitas: SueQuality = SueQuality()
vizslas: SueQuality = SueQuality()
goldfish: SueQuality = SueQuality()
trees: SueQuality = SueQuality()
cars: SueQuality = SueQuality()
perfumes: SueQuality = SueQuality()
def cmp(self, other: 'Sue') -> bool:
for attr in self.__dict__:
if getattr(self, attr) != getattr(other, attr):
return False
return True
def cmp2(self, other: 'Sue') -> bool:
return (
self.children == other.children and
self.cats > other.cats and
self.samoyeds == other.samoyeds and
self.pomeranians < other.pomeranians and
self.akitas == other.akitas and
self.vizslas == other.vizslas and
self.goldfish < other.goldfish and
self.trees > other.trees and
self.cars == other.cars and
self.perfumes == other.perfumes
)
@staticmethod
def from_string(s: str) -> tuple[int, 'Sue']:
number, rest = s.split(': ', 1)
number = int(number.split(' ')[1])
properties = {p[0]: SueQuality(int(p[1])) for p in (prop.split(': ') for prop in rest.split(', '))}
return number, Sue(**properties)
def part1(sues: list[Sue]) -> int:
desired = Sue(
children=SueQuality(3), cats=SueQuality(7), samoyeds=SueQuality(2),
pomeranians=SueQuality(3), akitas=SueQuality(0), vizslas=SueQuality(0),
goldfish=SueQuality(5), trees=SueQuality(3), cars=SueQuality(2),
perfumes=SueQuality(1)
)
return next(number for number, sue in sues.items() if sue.cmp(desired))
def part2(sues: list[Sue]) -> int:
desired = Sue(
children=SueQuality(3), cats=SueQuality(7), samoyeds=SueQuality(2),
pomeranians=SueQuality(3), akitas=SueQuality(0), vizslas=SueQuality(0),
goldfish=SueQuality(5), trees=SueQuality(3), cars=SueQuality(2),
perfumes=SueQuality(1)
)
return next(number for number, sue in sues.items() if sue.cmp2(desired))
if __name__ == '__main__':
with open('input') as f:
sues = {number: sue for number, sue in (Sue.from_string(line) for line in f)}
print(part1(sues))
print(part2(sues))
|