def test_shuffle(self): def is_deck_shuffled(list1, list2): is_different = False for i in range(len(list1)): if list1[i] != list2[i]: is_different = True return is_different deck = Deck() deck.initialize() list_of_cards1 = list(deck.cards) deck.shuffle() list_of_cards2 = list(deck.cards) self.assertTrue(is_deck_shuffled(list_of_cards1, list_of_cards2)) deck.shuffle() list_of_cards3 = list(deck.cards) self.assertTrue(is_deck_shuffled(list_of_cards3, list_of_cards2))
def test_shuffle_no_seed(self, mock_seed, mock_shuffle): # call it twice, ensure it's the same order? deck = Deck() deck.shuffle() assert not mock_seed.called, 'seed was set and should not have been' mock_shuffle.assert_called_once_with(deck.card_list)
def test_shuffle_seed(self, mock_seed, mock_shuffle): # Assert the seed is used before shuffle is called deck = Deck() deck.shuffle(seed=1234) mock_seed.assert_called_once_with(1234) mock_shuffle.assert_called_once_with(deck.card_list)
class GameController(object): """Takes a list containing each player, as well as a seed for the deck.""" def __init__(self, players, deck_seed=False): num_players = len(players) if not 2 <= num_players <= 5: raise ValueError( "There must be between 2 and 5 players to play Hanabi.") for player in players: if not isinstance(player, Player): raise ValueError( "All players must inherit from the Player class.") self.colors = ('r', 'y', 'g', 'w', 'b') # TODO: rainbow/mixed/wilds self.numbers = (1, 1, 1, 2, 2, 3, 3, 4, 4, 5) self.players = players self.deck = Deck(colors=self.colors, numbers=self.numbers) self.deck.shuffle(seed=None) self.player_hands = [[] for _ in range(len(self.players))] self.master_game_state = GameState(Board(self.deck), self.player_hands) def deal_initial_hand(self): cards_to_deal = 5 if len(self.players) > 3: cards_to_deal = 4 for rounds in range(cards_to_deal): for player in range(len(self.players)): self.player_hands[player].append(self.deck.draw_card()) self.master_game_state.player_hands = self.player_hands def game_over(self, current_player, game_state=None): if game_state is None: game_state = self.master_game_state # Check if we blew up if game_state.board.fuse_tokens < 1: # print("no more fuses") return True # Check if we've completed all the stacks MAX_STACK_SCORE = 5 # Todo: figure out a better place for a global var like this if game_state.board.compute_score() == (len(self.colors) * MAX_STACK_SCORE): # print("You win!") return True # Check if the deck is done and everyone played their final turn: if len(self.deck ) == 0 and current_player == game_state.board.game_almost_over: # print("game almost over") #print(player_id) return True # Otherwise, the engine is not over return False # Turn order: move, check end, draw card, initiate final round, next player def play_game(self): self.deal_initial_hand() self.master_game_state.board.deck_size = len(self.deck) for player_id in cycle(range(len(self.players))): player_game_state = PlayerGameState(self.master_game_state, player_id) new_move = self.players[player_id].make_move(player_game_state) assert isinstance(new_move, Move) try: self.master_game_state = new_move.apply(self.master_game_state) except AssertionError, e: raise Exception( "{p} submitted unplayable move: {m}".format( p=str(self.players[player_id]), m=str(new_move)), e) if self.game_over(player_id): game_score = self.master_game_state.board.compute_score() #print("Game over: Score {score}".format(score=game_score)) return game_score if len(self.deck) > 0: self.player_hands[player_id].append(self.deck.draw_card()) self.master_game_state.player_hands = self.player_hands self.master_game_state.board.deck_size = len(self.deck) # This triggers when the last card is drawn, every player including this one takes one more turn. if len( self.deck ) == 0 and self.master_game_state.board.game_almost_over is None: self.master_game_state.board.game_almost_over = player_id
class Dealer: def __init__(self, deck, seating, random_seed_for_shuffling=None): self.community_cards = [] self.deck = deck self.seating = seating self.pot = None self.big_blind_size = None self.random_seed_for_shuffling = random_seed_for_shuffling def deal_cards_to_players(self): for player in self.seating.players: player.receive_cards(self.deck.draw(2)) def move_button(self): self.seating.move_button() def collect_blinds(self, small_blind_size): for player in self.seating.players: player.money_in_pot = 0 sb_player = self.seating.small_blind_player() bb_player = self.seating.big_blind_player() available_size_of_small_blind = min(small_blind_size, sb_player.stack) self.pot.player_calls(sb_player, available_size_of_small_blind) available_size_of_big_blind = min(small_blind_size * 2, bb_player.stack) self.pot.player_calls(bb_player, available_size_of_big_blind) def setup_deck(self): self.deck = Deck() self.deck.initialize() self.deck.shuffle(self.random_seed_for_shuffling) def add_community_cards(self, card_count): self.community_cards += self.deck.draw(card_count) for player in self.seating.players: player.see_community_cards(self.community_cards) print("Community cards: " + ", ".join([str(card) for card in self.community_cards])) def play_preflop(self, small_blind_size): self.big_blind_size = small_blind_size * 2 self.pot = Pot() self.collect_blinds(small_blind_size) self.deal_cards_to_players() bb_player = self.seating.big_blind_player() amount_to_match = small_blind_size * 2 return self.ask_players_for_actions(bb_player, amount_to_match, True) def play_flop(self): self.add_community_cards(3) last_player_to_go = self.seating.button_player() return self.ask_players_for_actions(last_player_to_go, last_player_to_go.money_in_pot, True) def play_turn(self): self.add_community_cards(1) last_player_to_go = self.seating.button_player() return self.ask_players_for_actions(last_player_to_go, last_player_to_go.money_in_pot, True) def play_river(self): self.add_community_cards(1) last_player_to_go = self.seating.button_player() winner = self.ask_players_for_actions(last_player_to_go, last_player_to_go.money_in_pot, True) if winner: return True else: for pot in self.pot.get_all_pots(): winners = card_showdown.find_winners(pot.chips_per_player, self.community_cards) players_share = int(pot.pot_size() / len(winners)) for player in winners: player.stack += players_share def ask_players_for_actions(self, player_who_raised, new_raised_amount, include_last_player): next_player = self.seating.next_player_after_player(player_who_raised) amount_of_calls_to_make = len(self.seating.players) if not include_last_player: amount_of_calls_to_make -= 1 for i in range(amount_of_calls_to_make): if next_player.cards and next_player.stack > 0: p_action, p_amount = next_player.act(new_raised_amount) if p_action == Action.ACTION_FOLD: print(str(next_player) + " folds") self.player_folds(next_player) elif p_action == Action.ACTION_CALL: print(str(next_player) + " calls " + str(p_amount)) self.pot.player_calls(next_player, p_amount) elif p_action == Action.ACTION_RAISE: print(str(next_player) + " raises to " + str(p_amount)) self.pot.player_calls(next_player, p_amount) return self.ask_players_for_actions( next_player, p_amount, False) if self.is_winner_determined(): winner = list(self.pot.chips_per_player.keys())[0] winner.stack += self.pot.pot_size() return True next_player = self.seating.next_player_after_player(next_player) return False def is_winner_determined(self): return len(self.pot.chips_per_player) == 1 def player_folds(self, player): player.money_in_pot = 0 player.release_cards() if player in self.pot.chips_per_player: self.pot.size += self.pot.chips_per_player[player] self.pot.chips_per_player.pop(player, None)