Created
December 22, 2020 22:51
-
-
Save unbibium/bfc800d809a9a4fe9bbfad7de77e937a to your computer and use it in GitHub Desktop.
AoC 2020 day 22 part 2 solution that still works if you add a third player
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
print("22: recursive combat") | |
# I tried to program this without admitting that there | |
# are only two players. | |
import sys, os,math | |
from collections import deque, defaultdict | |
def score(cards): | |
score=0 | |
for i,value in enumerate(cards): | |
score += (len(cards)-i) * value | |
return score | |
class Game(): | |
def __init__(self, decks=None, filename=None, turns=0): | |
if decks: | |
self.decks = decks | |
print("decks=",decks) | |
else: | |
self.decks = dict() | |
self.history = [] | |
if filename: | |
with open(filename) as f: | |
myDeck = None | |
for line in f.readlines(): | |
line = line.strip() | |
if not line: | |
continue | |
elif line[-1] == ':': | |
myDeck = deque() | |
self.decks[line[:-1]] = myDeck | |
else: | |
myDeck.append(int(line)) | |
self.turns = turns | |
def __hash__(self): | |
return hash(str(self.decks)) | |
def play_turn(self): | |
self.turns += 1 | |
print("--- Round",self.turns,"---") | |
# check current state against history | |
if hash(self) in self.history: | |
raise InfiniteGameException("INFINITE LOOP FOUND in turn", self.history.index(hash(self))) | |
# add current state to history | |
self.history.append(hash(self)) | |
for player in self.decks: | |
print(player,"deck:", list(self.decks[player])) | |
turn = {player: self.decks[player].popleft() for player in self.decks if self.decks[player]} | |
#turn = dict({player: self.decks[player].popleft() for player in self.decks}) | |
# if both players have at least as many cards in their own decks as the number on the card they just dealt | |
if all(len(self.decks[player]) >= turn[player] for player in turn): | |
print("RECURSIVE COMBAT") | |
subgame = Game(decks=self.copy_of_decks(turn), turns=complex(0, self.turns.imag+1) ) | |
winner, theirScore = subgame.play() | |
else: # normal game | |
for player in turn: | |
print(player,"plays:", turn[player]) | |
winner = max(turn, key=turn.__getitem__) | |
print(winner, "wins the round") | |
#self.decks[winner].extend(cards) | |
self.decks[winner].append(turn[winner]) | |
for player in turn: | |
if player != winner: | |
self.decks[winner].append(turn[player]) | |
def play(self): | |
try: | |
while len(list(filter(bool,self.decks.values()))) > 1: | |
self.play_turn() | |
except InfiniteGameException: | |
print("caught") | |
for player in self.decks: | |
if len(self.decks[player]) > 0: | |
return (player, score(self.decks[player])) | |
def copy_of_decks(self, turn): | |
# return a deep copy | |
return {player: deque(list(self.decks[player])[:turn[player]]) for player in self.decks} | |
class InfiniteGameException(Exception): | |
pass | |
if __name__ == '__main__': | |
if len(sys.argv)<2: | |
print("Usage",sys.argv[0],"filename") | |
sys.exit(1) | |
game = defaultdict(deque) | |
game = Game(filename=sys.argv[1]) | |
print("display result") | |
print(game.play()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment