175 lines
4.0 KiB
Python
Executable File
175 lines
4.0 KiB
Python
Executable File
#! /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()
|