#! /usr/bin/env python3 from copy import copy from enum import Enum from typing import Set, Tuple, List, Optional class Player(Enum): UNKNOWN = 0 ONE = 1 TWO = 2 def read_decks(filename: str): deck1, deck2 = None, None cur_deck: List[int] = [] with open(filename) as f: for line in f: line = line.strip() if line.startswith("Player 1"): continue if line == "": continue if line.startswith("Player 2"): deck1 = cur_deck cur_deck = [] continue cur_deck.append(int(line)) deck2 = cur_deck return deck1, deck2 def play_round(deck1, deck2): c1 = deck1.pop(0) c2 = deck2.pop(0) print(f"{c1} vs {c2} ", end="", flush=True) if c1 > c2: print("Player 1 wins!") deck1 += [c1, c2] elif c2 > c1: print("Player 2 wins!") deck2 += [c2, c1] else: print("Nobody wins?") # raise ValueError("Nobody wins") RoundHistory = Set[Tuple[Tuple[int, ...], Tuple[int, ...]]] def recursive_combat( deck1: List[int], deck2: List[int], game: int = 1, past_games: Optional[RoundHistory] = None ) -> Player: if past_games is None: past_games = set() print(f"{game}: Starting p2 {deck1} p2 {deck2}") # Play until there is an empty deck game_round = 0 winner = Player.UNKNOWN while deck1 and deck2: game_round += 1 # Check for looping... this_round = (tuple(deck1), tuple(deck2)) if this_round in past_games: print( f"{game}:{game_round} Loop detected. Player 1 wins the game!", ) return Player.ONE past_games.add(this_round) print(f"{game}:{game_round} p1 {deck1} p2 {deck2} ") # Draw c1 = deck1.pop(0) c2 = deck2.pop(0) print(f"{game}:{game_round} {c1} vs {c2} ", end="", flush=True) winner = Player.UNKNOWN if c1 <= len(deck1) and c2 <= len(deck2): print("RECURSIVE COMBAT!!!!") winner = recursive_combat( copy(deck1[:c1]), copy(deck2[:c2]), game=game+1, past_games=past_games.copy(), ) else: if c1 > c2: winner = Player.ONE elif c2 > c1: winner = Player.TWO else: raise ValueError("cards are equal? No way!") if winner == Player.ONE: print("Player 1 wins!") deck1 += [c1, c2] elif winner == Player.TWO: print("Player 2 wins!") deck2 += [c2, c1] else: print("Nobody wins?") raise ValueError("Nobody wins") print(f"{game}: Game finished p1 {deck1} p2 {deck2}") # Deck is empty! if deck1: winner = Player.ONE elif deck2: winner = Player.TWO else: raise ValueError("unknown winner") return winner def part1(): deck1, deck2 = read_decks("input.txt") print("Initial decks") print("Player 1", deck1) print("Player 2", deck2) while deck1 and deck2: play_round(deck1, deck2) winning_deck: List[int] = None if deck1: winning_deck = deck1 print("Winner! Player 1!") elif deck2: winning_deck = deck2 print("Winner! Player 2!") score = sum((i*v for i, v in enumerate(reversed(winning_deck), 1))) print("Score:", score) def part2(): deck1, deck2 = read_decks("input.txt") print("Initial decks") print("Player 1", deck1) print("Player 2", deck2) _ = recursive_combat(deck1, deck2) winning_deck: List[int] = None if deck1: winning_deck = deck1 print("Winner! Player 1!") elif deck2: winning_deck = deck2 print("Winner! Player 2!") score = sum((i*v for i, v in enumerate(reversed(winning_deck), 1))) print("Score:", score) if __name__ == "__main__": part1() part2()