aoc-2020/d22/main.py

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