def __init__(self, players): pf = Profiler() pf.printer.indent() pf.printer.silent = not PERFORMANCE_LOGGING self.players = players # Set up the deck deck = INITIAL_DECK[:] pf.measure("Set up deck") shuffle(deck) pf.measure("Shuffle deck") self.deck = deck """ Current draw deck. Excludes Snitches. """ self.discard_deck = [] """ Deck discarded (played) cards. Excludes Snitches. """ pf.measure("Other set up") # Give each player the initial number of Snitches and deal the rest. for player in self.players: number_of_character_cards_to_deal = INITIAL_CARDS_PER_PLAYER - INITIAL_SNITCHES_PER_PLAYER character_hand = self.deck[:number_of_character_cards_to_deal] del self.deck[:number_of_character_cards_to_deal] player.set_up( hand=INITIAL_SNITCH_HAND + character_hand, coins=INITIAL_COINS_PER_PLAYER) player.prepare() pf.measure("Give players cards")
def play(self, silent=False): p = Printer(silent=silent) pf = Profiler() pf.printer.indent() pf.printer.silent = not PERFORMANCE_LOGGING round_info = RoundInfo() for round_number in range(GAME_ROUNDS): p.print(f"-- Round {round_number} --") p.indent() pf.reset() # Select game leader leader_player = self.players[round_number % len(self.players)] # Leader player selects the number of players. heist_difficulty = leader_player.select_heist_difficulty(len(self.players)) assert(heist_difficulty >= 1 and heist_difficulty <= len(self.players)) p.print(f"{leader_player.short_name} selects difficulty of {heist_difficulty}") # Draw this many cards to form the contract contract_cards = self.drawSafe(how_many=heist_difficulty) p.print(f"Contract cards: {contract_cards}") # Set up round info, to pass to each player instance. round_info.number = round_number round_info.contract_cards = contract_cards # Ask each player to play a card player_cards = [(player, player.play_card(round_info)) for player in self.players] played_cards = [] for (player, card) in player_cards: p.print(f"{player.short_name} plays {card}") played_cards.append(card) pf.measure("Draw cards") # Evaluate game heist_success = self.evaluate_contract(contract_cards, played_cards) if heist_success: p.print("Heist SUCCESS 🙌!") # All non-snitching players get reward for (player, card) in player_cards: if card in CHARACTER_CARDS: p.print(f"{player.short_name} contributed and wins {heist_difficulty}💰. Yay 😊") player.coins += heist_difficulty else: p.print(f"{player.short_name} snitched and gets nothing. 🤥 ") else: p.print("Heist FAILED ❌!") if not Card.SNITCH in played_cards: # There were no snitches. The heist simply failed due to lack of cards. p.print("No one played a Snitch. All players retain their coins.") elif len([card for card in played_cards if card == Card.SNITCH]) == len(self.players): # Everyone played Snitch. p.print("Everyone played a Snitch. All players retain their coins.") else: # All snitched-on players lose -1 pot = 0 snitch_players = [] for (player, card) in player_cards: if card in CHARACTER_CARDS: if player.coins > 0: player.coins -= 1 pot += 1 p.print(f"{player.short_name} got betrayed and loses 1💰. Grr 😤") else: p.print(f"{player.short_name} got betrayed but has no money.") else: snitch_players.append(player) # All snitching players (if any) split the reward, to a minimum of 1 reward = max(1, ceil(pot / len(snitch_players))) for player in snitch_players: player.coins += reward p.print(f"{player.short_name} snitched and gets {reward}💰. Ha! 😆") pf.measure("Evaluate") # Discard all cards self.discard_deck.extend(contract_cards) for (_, card) in player_cards: self.discard_deck.append(card) p.deindent() p.print("*** Game Summary ***") p.indent() # Sort by money. players = list(self.players) players.sort(key=lambda player: player.coins, reverse=True) # Determine winners winners = [player for player in players if player.coins == players[0].coins] if len(winners) == 1: p.print(f"{winners[0].short_name} wins with ${winners[0].coins}") elif len(winners) == len(players): p.print(f"All players draw.") winners = [] else: winner_names = ", ".join(p.short_name for p in winners) p.print(f"{winner_names} drew with ${winners[0].coins}") p.deindent() # Return result and stats return { "winners": winners }
shared_wins = 0 # i.e. where multiple but not all players won draws = 0 # i.e. all players drew. wins_per_player = {player: 0 for player in players} pf = Profiler() for _ in range(GAMES): game = Game(players) result = game.play(silent=True) # Record stats about the game winners = result["winners"] if len(winners) == 0: draws += 1 else: for winner in winners: wins_per_player[winner] += 1 if len(winners) == 1: pure_wins += 1 else: shared_wins += 1 pf.measure("Time taken") # Print stats print(f"Player {GAMES} games with {NUMBER_OF_PLAYERS} players") print(f"There were:") print(f"\t{pure_wins}\tPure wins") print(f"\t{shared_wins}\tShared wins") print(f"\t{draws}\tDraws") print(f"Wins per player:") for player, wins in wins_per_player.items(): print(f"\t{wins}\t{player.short_name}")