def test_draw_card_fresh_deck(self): deck = Deck(colors=('red', 'blue'), numbers=(1, 2, 3)) len_before = len(deck) card = deck.draw_card() len_after = len(deck) assert len_before == len_after + 1 assert card not in deck.card_list
def test_init_deck(self): deck = Deck() deck.initialize() unique_cards = set(deck.cards) self.assertEqual(self.deck_size, len(unique_cards)) for card in unique_cards: self.assertTrue(isinstance(card, Card))
def test_draw_card_empty(self): deck = Deck(colors=('red', ), numbers=(1, 2)) card1 = deck.draw_card() card2 = deck.draw_card() assert len(deck) == 0 assert card1 not in deck.card_list assert card2 not in deck.card_list with pytest.raises(IndexError) as excinfo: card3 = deck.draw_card() assert str(excinfo.value) == "Cannot draw card from an empty deck."
def test_draw_card_multiple_draws(self): deck = Deck(colors=('red', 'blue'), numbers=(1, 2, 3)) len_before = len(deck) card1 = deck.draw_card() card2 = deck.draw_card() card3 = deck.draw_card() len_after = len(deck) assert len_before == len_after + 3 assert card1 not in deck.card_list assert card2 not in deck.card_list assert card3 not in deck.card_list
def test_deck_correctness(self): deck = Deck(shuffle=True, is_small=False) cards = [deck.pull_out() for _ in range(52)] with self.subTest(): with self.assertRaises(EmptyDeckException): deck.pull_out() with self.subTest(): sorted_cards = [] for suit in SUIT: for value in VALUE: sorted_cards.append(Card(suit, value)) self.assertNotEqual(cards, sorted_cards)
def test_deal(self): player1 = Player("player1", 1000) player2 = Player("player2", 1000) seating = Seating([player1, player2]) deck = Deck() deck.initialize() dealer = Dealer(deck, seating) dealer.deal_cards_to_players() hand_size = 2 cards_dealt = len(seating.players) * hand_size self.assertEqual(cards_dealt, len(set(player1.cards + player2.cards))) expected_remaining_card_count = TestDeck.deck_size - cards_dealt self.assertEqual(expected_remaining_card_count, len(deck.cards))
def __init__(self, players, deck_seed=False): num_players = len(players) if not 2 <= num_players <= 5: raise Exception("There must be between 2 and 5 players") for player in players: if not isinstance(player, Player): raise Exception("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, seed=deck_seed) self.player_hands = [[] for _ in range(len(self.players))] self.master_game_state = GameState(Board(self.deck), self.player_hands) self.game_almost_over = None
def __init__(self): # sub-objects self.players = dict() self.transitions = None self.deck = Deck() # shared game state self.pot = 0 self.stack = 8 self.kobayakawa = None self.discarded = [] # transient state variables (used during betting round) self._contenders = [] self._turn_winner = None
def test_collect_blinds(self): initial_stack = 1000 small_blind_size = 5 big_blind_size = small_blind_size * 2 button_player = self.setup_new_player("Button", initial_stack) player2 = self.setup_new_player("SmallBlind", initial_stack) player3 = self.setup_new_player("BigBlind", initial_stack) seating = Seating([button_player, player2, player3]) deck = Deck() dealer = Dealer(deck, seating) dealer.pot = Pot() dealer.collect_blinds(small_blind_size) self.assertEqual(initial_stack, button_player.stack) self.assertEqual(initial_stack - small_blind_size, player2.stack) self.assertEqual(initial_stack - big_blind_size, player3.stack) self.assertEqual(big_blind_size + small_blind_size, dealer.pot.total_count()) dealer.move_button() dealer.pot = Pot() dealer.collect_blinds(small_blind_size) self.assertEqual(initial_stack - big_blind_size, button_player.stack) self.assertEqual(initial_stack - small_blind_size, player2.stack) self.assertEqual(initial_stack - big_blind_size - small_blind_size, player3.stack) self.assertEqual(big_blind_size + small_blind_size, dealer.pot.total_count()) self.assertTrue(button_player in dealer.pot.chips_per_player) self.assertTrue(player3 in dealer.pot.chips_per_player)
def test_move_button__when5players(self): player1 = self.setup_new_player("Player1", 100) player2 = self.setup_new_player("Player2", 100) initial_button_player = self.setup_new_player("Button", 100) initial_sb_player = self.setup_new_player("SmallBlind", 100) initial_bb_player = self.setup_new_player("BigBlind", 100) seating = Seating([ player1, player2, initial_button_player, initial_sb_player, initial_bb_player ]) deck = Deck() dealer = Dealer(deck, seating) dealer.move_button() seating.button_position = 0 dealer.move_button() seating.button_position = 1 dealer.move_button() seating.button_position = 2 dealer.move_button() seating.button_position = 3 dealer.move_button() seating.button_position = 4 dealer.move_button() seating.button_position = 5 dealer.move_button() seating.button_position = 0
def setUp(self): self.controller_1 = ManualController(0) self.controller_2 = ManualController(1) self.user_1 = User(0, self.controller_1) self.user_2 = User(1, self.controller_2) self.engine = Engine() self.engine.add_user(self.user_1) self.engine.add_user(self.user_2) self.engine.deck = Deck(shuffle=False, is_small=False)
def __init__(self): self.card_images = [] deck = Deck().all_cards for card in deck: photo = Image.open('graphics/images/c{}.jpg'.format(card)) self.card_images.append(ImageTk.PhotoImage(photo)) photo = Image.open('graphics/images/cardback.jpg') self.card_back = ImageTk.PhotoImage(photo)
def start_game_awaiting(self): resp = requests.post(server_url + '/start', json={'id': client_id}) data = resp.json() while self.players_info: self.menu.rem_player() for player in data['players']: self.menu.add_player(username=player[0], ai=False) if data['status'] == 'OK' and data['ready']: self.start_game(Deck(str_deck=data['deck'])) else: self.after(100, self.start_game_awaiting)
def test_move_button__when2players(self): initial_button_player = self.setup_new_player("Button", 100) initial_bb_player = self.setup_new_player("BigBlind", 100) seating = Seating([initial_button_player, initial_bb_player]) deck = Deck() dealer = Dealer(deck, seating) dealer.move_button() seating.button_position = 0 dealer.move_button() seating.button_position = 1 dealer.move_button() seating.button_position = 0
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 start_game(self, deck=None): self.master.geometry('1100x250') deck = Deck() if deck is None else deck self.engine = Engine(deck) for user in self.users: self.engine.add_user(user) self.engine.init_game() self.engine.update_users() for user in self.engine.get_active_users(): self.active_users_queue.put(user) user = self.active_users_queue.get() self.engine.process_turn( user, user.make_turn(*self.engine.get_game_table_info())) if self.engine.is_ended(): self.engine.outcomes_notify(self.engine.generate_outcomes())
def test_collect_blinds__when_player_dont_have_enough_chips(self): initial_stack = 100 small_blind_size = 5 big_blind_size = small_blind_size * 2 button_player = self.setup_new_player("Button", 100) player2 = self.setup_new_player("SmallBlind", small_blind_size - 1) player3 = self.setup_new_player("BigBlind", big_blind_size - 1) seating = Seating([button_player, player2, player3]) deck = Deck() dealer = Dealer(deck, seating) dealer.pot = Pot() dealer.collect_blinds(small_blind_size) self.assertEqual(initial_stack, button_player.stack) self.assertEqual(0, player2.stack) self.assertEqual(0, player3.stack) self.assertEqual(13, dealer.pot.total_count()) self.assertTrue(player2 in dealer.pot.chips_per_player) self.assertTrue(player3 in dealer.pot.chips_per_player)
def construct_inferred_deck(self, game_state): new_deck = Deck(colors=game_state.board.deck_colors, numbers=game_state.board.deck_numbers, seed=42) # remove cards that have been discarded for card in game_state.board.discard: new_deck.card_list.remove(card) # remove cards in other player's hands hand_indexes = range(len(game_state.player_hands)) hand_indexes.remove(game_state.player_id) for hand_id in hand_indexes: for card in game_state.player_hands[hand_id]: new_deck.card_list.remove(card) # remove cards that have already scored points for color, stacked_cards in game_state.board.card_stacks.iteritems(): for card in stacked_cards.cards_played: new_deck.card_list.remove(card) return new_deck
def test_init_defaults(self): deck = Deck() # Ensure card count == whatever defaults should predict assert len(deck) == len(deck.card_colors) * len(deck.card_numbers)
from flask import Flask, jsonify, request from engine.deck import Deck app = Flask(__name__) players_count = 2 users = {} deck = Deck() whose_turn, turn_result = 0, None next_turn, step_number = None, 0 accept_turn = set() @app.route("/connect", methods=['POST']) def connect(): data = request.get_json() if data['name'] and data['name'] not in map(lambda v: v['name'], users.values()): if players_count > len(users): new_id = len(users) users[new_id] = { 'id': new_id, 'name': data['name'], 'ready': False } return jsonify(status='OK', id=new_id) return jsonify(status='FAIL', error_msg='Server is full.') return jsonify(status='FAIL', error_msg='The nickname is already taken') @app.route("/start", methods=['POST'])
def __init__(self, deck=None): self.users = [] self.deck = Deck(shuffle=True, is_small=False) if deck is None else deck self.bj_gametable = None self.is_inited = False
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 clear(): global users, deck, whose_turn, turn_result users = {} deck = Deck() whose_turn, turn_result = 0, None return jsonify(status='OK')
def test_init_empty_numbers(self): with pytest.raises(ValueError) as excinfo: numbers = () deck = Deck(numbers=numbers) assert str( excinfo.value) == "Decks must have at least one card number."
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)
def test_init_empty_colors(self): with pytest.raises(ValueError) as excinfo: colors = () deck = Deck(colors=colors) assert str(excinfo.value) == "Decks must have at least one card color."
def test_init_custom_everything(self): numbers = (1, 2, 3) colors = ('red', 'blue') deck = Deck(colors=colors, numbers=numbers) assert len(deck) == 6
def test_init_custom_numbers(self): numbers = (1, 2, 3) deck = Deck(numbers=numbers) assert len(deck) == len(deck.card_colors) * 3
def test_init_custom_colors(self): colors = ('red', 'blue') deck = Deck(colors=colors) assert len(deck) == 2 * len(deck.card_numbers)
class GameEngine: """ Administrates and monitors all aspect of a game of Kobayakawa Maintaining the progression indicator is delegated to GameTransitionIterator Dealing with players actions is delegated to functions in engine.actions """ def __init__(self): # sub-objects self.players = dict() self.transitions = None self.deck = Deck() # shared game state self.pot = 0 self.stack = 8 self.kobayakawa = None self.discarded = [] # transient state variables (used during betting round) self._contenders = [] self._turn_winner = None """ External API """ def attach_player(self, player): self.players[player.name] = player GameEvent(Events.PLAYER_JOINED, player.name) def start_game(self, starting_player_name): assert len(self.players) > 0 assert starting_player_name in self.players_by_name self.transitions = GameStateIterator(self, starting_player_name) self.transitions.start_of_phase = self._resolve_start_of_phase self.transitions.end_of_phase = self._resolve_end_of_phase self.transitions.end_of_turn = self._resolve_end_of_turn self.transitions.end_of_game = self._resolve_end_of_game self.pot = 0 for transition in self.transitions: player_name = transition.active_player player = self.players[player_name] move = player.do(self.transitions.current_phase) move(player, self) def terminate(self): # detach all players from positions players = list(self.players.keys()) for player in players: self.players[player] = None @property def players_by_name(self): return list(self.players.keys()) def place_forced_bet(self): minimum_bet = 1 if self.transitions.turn_number == 7: minimum_bet = 2 self.pot, self.stack = self.pot + minimum_bet, self.stack - minimum_bet @property def contenders(self): return self._contenders[:] # return a COPY of that list def add_contender(self, player_name): assert player_name in self.players_by_name assert player_name not in self._contenders self._contenders.append(player_name) """ API for player agents """ def sees(self, player_name): # can see all discarded cards # can see kobayakawa and previously covered kobayakawa # can see stack from players other than itself # can see current bets # can see past actions of all players in the current turn (redundant?) assert player_name in self.players_by_name players = self.players.values() discarded = set(self.discarded) for p in players: discarded = set(chain(discarded, p.discarded)) players_stack = {p.name: p.stack for p in players} players_bet = {p.name: p.has_bet for p in players} from collections import namedtuple GameData = namedtuple('GameData', 'players discarded kobayakawa stacks bets') return GameData(self.players_by_name, discarded, self.kobayakawa, players_stack, players_bet) """ State machine internal transition methods """ def _resolve_start_of_phase(self): self.transitions.current_phase.on_start() def _resolve_end_of_phase(self): GameEvent(Events.END_OF_PHASE, self.transitions.turn_number, self.transitions.current_phase.name) self.transitions.current_phase.on_end() def _reset(self): """ self called in between turns Note that self.stack is not reset """ self.pot = 0 self.kobayakawa = None self.discarded = [] # get all cards into random.deck and shuffles for player in self.players.values(): player.reset() self.deck.reset() def _ascertain_winner(self): """ I tell who the winner is at the end of turn """ if len(self._contenders) == 0: # this means that nobody has bet, can only happen by chance when all players positions # are random agents. the winner should be the last player to have played winner = self.transitions.active_player elif len(self._contenders) == 1: # automatic winner, no showdown winner = self._contenders[0] else: """ SHOWDOWN!!!!! """ for player_name in self._contenders: card = self.players[player_name].has_card GameEvent(Events.PLAYER_SHOWDOWN, player_name, card) player_cards = { self.players[name].has_card: name for name in self._contenders } min_card = min(player_cards.keys()) max_card = max(player_cards.keys()) if min_card + self.kobayakawa > max_card: winner = player_cards[min_card] GameEvent(Events.WINNING_FIGURE_MIN, min_card, self.kobayakawa, max_card) elif max_card > min_card + self.kobayakawa: winner = player_cards[max_card] GameEvent(Events.WINNING_FIGURE_MAX, max_card, min_card, self.kobayakawa) else: # it's a draw GameEvent(Events.DRAW, max_card, min_card, self.kobayakawa) player_a = player_cards[min_card] player_b = player_cards[max_card] winner = self.transitions.closest_from_first_player( player_a, player_b) self._contenders = [] return winner def _check_eliminated_players(self): """ part of end of turn resolution I check whether players have a zero stack at the end of the turn so they can be eliminated from the game :return: the list of remaining players in the game (by name """ eliminated = [] kept_players = [] for (player_name, player) in self.players.items(): if player.stack == 0: eliminated.append(player_name) else: kept_players.append(player_name) if len(eliminated): for player_name in eliminated: GameEvent(Events.PLAYER_OFF, player_name) del self.players[player_name] return self.players_by_name def _resolve_end_of_turn(self): # who has won? self._turn_winner = self._ascertain_winner() # update game state and give the winner its reward gain = self.pot self.pot = 0 self.players[self._turn_winner].stack += gain GameEvent(Events.TURN_WINNER, self._turn_winner, gain) # clean up broke players kept_players = self._check_eliminated_players() self.transitions.update_players_list(self._turn_winner, kept_players) # reset state for a new turn self._reset() def _resolve_end_of_game(self): players_stacks = [(player.stack, player.name) for player in self.players.values()] players_stacks = sorted(players_stacks, key=lambda t: t[0], reverse=True) gain, winner = players_stacks[0] winners = [winner] for (stack, player) in players_stacks[1:]: if stack == gain: winners.append(player) if len(winners) > 1: s = ', '.join(winners[:-1]) + ' and ' + winners[-1] GameEvent(Events.GAME_END_MULTIPLE_WINNERS, s, gain) else: GameEvent(Events.GAME_END_SINGLE_WINNER, winner, gain)