summaryrefslogtreecommitdiffstats
path: root/16/solution.py
blob: b7ebc5c73a5ce3df2ed424af64859ee20bf867fa (plain)
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))