Skip to content

Instantly share code, notes, and snippets.

@unbibium
Created December 22, 2020 22:51
Show Gist options
  • Save unbibium/bfc800d809a9a4fe9bbfad7de77e937a to your computer and use it in GitHub Desktop.
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
#!/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