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))