from tools.aoc import AOCDay from typing import Any F_ORDER = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"] class CardSet: def __init__(self, card_string: str, bid: str, part2: bool = False): self.bid = int(bid) self.cards = [F_ORDER.index(x) + 1 for x in card_string] if part2: for i, c in enumerate(self.cards): if c == 11: self.cards[i] = 1 if not part2: self.strength = self.type_rank() else: self.strength = self.joker_type_rank() def type_rank(self) -> int: card_set = set(self.cards) if len(card_set) == 5: # high card return 0 elif len(card_set) == 4: # one pair return 1 elif len(card_set) == 3: # two pair or three of a kind for c in self.cards: if self.cards.count(c) == 3: return 3 return 2 elif len(card_set) == 2: # full house or four of a kind for c in self.cards: if self.cards.count(c) == 4: return 5 return 4 else: return 6 def joker_type_rank(self) -> int: if 1 not in self.cards: return self.type_rank() orig_cards = self.cards.copy() max_strength = 0 for i in range(2, 15): self.cards = [x if x != 1 else i for x in self.cards] max_strength = max(max_strength, self.type_rank()) self.cards = orig_cards.copy() return max_strength def __lt__(self, other): if self.strength < other.strength: return True elif self.strength > other.strength: return False else: for i, c in enumerate(self.cards): if c < other.cards[i]: return True elif c > other.cards[i]: return False class Day(AOCDay): inputs = [ [ (6440, "input7_test"), (249483956, "input7_dennis"), (247815719, "input7"), ], [ (5905, "input7_test"), (252137472, "input7_dennis"), (248747492, "input7"), ], ] def parse_input(self, part2: bool = False) -> list: return [CardSet(*line.split(), part2) for line in self.getInput()] def part1(self) -> Any: return sum(c.bid * (i + 1) for i, c in enumerate(sorted(self.parse_input()))) def part2(self) -> Any: return sum(c.bid * (i + 1) for i, c in enumerate(sorted(self.parse_input(True)))) if __name__ == "__main__": day = Day(2023, 7) day.run(verbose=True)