Source code for day07.day7

"""day7 solution."""
from collections import defaultdict
from dataclasses import dataclass, field
from functools import total_ordering
from typing import Any, ClassVar, Self

INPUT = "day07/input.txt"
INPUT_SMALL = "day07/input-small.txt"


[docs] @total_ordering @dataclass(eq=False) class Hand: """Simple hand class, uses cards_inted and of_a_kind for sorting.""" cards: str bet: int cards_inted: list[int] = field(init=False, repr=False) of_a_kind: list[int] = field(init=False) CARD_MAPPING: ClassVar[str] = "23456789TJQKA" def __post_init__(self) -> None: """Convert cards to ints.""" self.cards_inted = [self.CARD_MAPPING.index(card) for card in self.cards] self.bet = int(self.bet) self.of_a_kind = self.calculate_of_a_kind()
[docs] def calculate_of_a_kind(self) -> list[int]: """Figure out card sets.""" card_sets: dict[str, int] = defaultdict(int) for card in self.cards: card_sets[card] += 1 return sorted(card_sets.values(), reverse=True)
def __lt__(self, other: Self) -> Any: """Less than comparator function.""" if not isinstance(other, Hand): raise ValueError("using __lt__ on non identical class") # compare our sets for ours, theirs in zip(self.of_a_kind, other.of_a_kind): if ours != theirs: return ours < theirs # compare our individual cards return self.cards_inted < other.cards_inted # int lists easy to compare def __eq__(self, other: object) -> bool: """Compares if two hands are equal by comparing cards in same order and value.""" if not isinstance(other, Hand): raise ValueError("using __lt__ on non identical class") return self.cards_inted == other.cards_inted
[docs] class HandPart2(Hand): """Part two; implements joker rule.""" CARD_MAPPING = "J23456789TQKA" # new card ordering # override
[docs] def calculate_of_a_kind(self) -> list[int]: """Figure out card sets; jokers will be added to the biggest card set.""" card_sets: dict[str, int] = defaultdict(int) for card in self.cards: card_sets[card] += 1 jokers = card_sets.pop("J", 0) if len(card_sets) == 0: return [jokers] of_a_kind = sorted(card_sets.values(), reverse=True) of_a_kind[0] += jokers return of_a_kind
[docs] def parse_lines(cls: type, path: str) -> list[Hand]: """Open input file and parse into hand structures.""" with open(path, "r", encoding="utf8") as file: # wow super cool list comprehension thingo i'm so cool results = [cls(*line.split()) for line in file] return results
[docs] def calculate_hands(cls: type, input_path: str) -> int: """Generates class `cls` then calculates points.""" hands = sorted(parse_lines(cls, input_path)) score = 0 for rank, hand in enumerate(hands): score += (rank + 1) * hand.bet return score
[docs] def main() -> None: """Main func.""" # Q1 print(calculate_hands(Hand, INPUT)) # Q2 print(calculate_hands(HandPart2, INPUT))
if __name__ == "__main__": main()