def __init__(self, name: str) -> None: """Initiates Player with a name and an empty hand. Args: name: Name of player. """ self.hand = Deck(empty=True) self.name = name
def test_basic_game_4(capsys, monkeypatch): monkeypatch.setattr("builtins.input", lambda x: "") rand = Random() rand.seed(1) deck = Deck(random_instance=rand) deck.shuffle() game = Game(PLAYERS_2, deck=deck, message=MESSAGE) game.play() captured = capsys.readouterr() assert captured.out == GAME_4_RESULTS
def test_basic_deck_shuffle_seed(): rand = Random() rand.seed(1) deck = Deck(random_instance=rand) deck.shuffle() assert len(deck) == 52 assert deck == SHUFFLED_DECK_1 assert deck.top_card == SHUFFLED_DECK_1[-1] card = deck.get_card() assert len(deck) == 51 assert card == SHUFFLED_DECK_1[-1] assert deck == SHUFFLED_DECK_1[:-1] assert deck.top_card == SHUFFLED_DECK_1[-2] for i in range(50, -1, -1): card = deck.get_card() assert card == SHUFFLED_DECK_1[i] assert len(deck) == 0 assert deck == [] assert deck.top_card is None deck.new_deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK assert deck.top_card == UNSHUFFLED_DECK[-1]
def test_basic_deck_draw(): deck = Deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK assert deck.top_card == UNSHUFFLED_DECK[-1] card = deck.get_card() assert len(deck) == 51 assert card == UNSHUFFLED_DECK[-1] assert deck == UNSHUFFLED_DECK[:-1] assert deck.top_card == UNSHUFFLED_DECK[-2] for i in range(50, -1, -1): card = deck.get_card() assert card == UNSHUFFLED_DECK[i] assert len(deck) == 0 assert deck == [] assert deck.top_card is None with pytest.raises(DeckEmptyError): card = deck.get_card() with pytest.raises(DeckEmptyError): card = deck.get_card() assert len(deck) == 0 assert deck == [] deck.new_deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK assert deck.top_card == UNSHUFFLED_DECK[-1]
def __init__( self, names: List[str], deck: Optional[Deck] = None, max_rounds: int = 3, message: Optional[Dict[str, List[str]]] = None ) -> None: """Initiates a Game by creating Players from names. Creates a Deck and shuffles if none is supplied. Generates internal message dictionary from messages.conf if none is supplied. Sets max rounds to 3 if none is supplied. Sets Game to active. Args: name: Names of players. message: Optional; Message dictionary used for game verbiage. deck: Optional; Deck to use during the game. max_rounds: Optional; Max number of rounds to play. """ if max_rounds < 1: raise GameError("Invalid max number of rounds.") if len(names) == 0: raise GameError("Invalid number of players.") if message: self._message = message else: conf_reader = ConfigParser() try: message_path = join(dirname(abspath(__file__)), "messages.conf") with open(message_path) as file: conf_reader.read_file(file) except IOError: raise GameError( "Could not open messages config file.") from None self._message = {k: list(v.values()) for k, v in dict(conf_reader).items()} if deck: self._deck = deck else: self._deck = Deck() self._deck.shuffle() self.players = Players(names) self._current_round = 1 self._is_active = True self._max_rounds = max_rounds self._rand = Random()
def test_basic_game_2(capsys, monkeypatch): monkeypatch.setattr("builtins.input", lambda x: "") rand = Random() rand.seed(1) deck = Deck(random_instance=rand) deck.shuffle() game = Game(PLAYERS_1, deck=deck, message=MESSAGE, max_rounds=1) assert game game.play() assert not game captured = capsys.readouterr() assert captured.out == GAME_1_RESULTS game.new_game() assert game game.play() assert not game captured = capsys.readouterr() assert captured.out == GAME_2_RESULTS
def test_basic_game_5(capsys, monkeypatch): monkeypatch.setattr("builtins.input", lambda x: "") cards = [ Card("Clubs", "6"), Card("Spades", "8"), Card("Diamonds", "3"), Card("Clubs", "9"), Card("Clubs", "Ace"), Card("Spades", "King"), Card("Diamonds", "5"), Card("Diamonds", "3"), Card("Spades", "10"), ] deck = Deck(empty=True) for card in cards: deck.add_card(card) game = Game(PLAYERS_2, deck=deck, message=MESSAGE) game.play() captured = capsys.readouterr() assert captured.out == GAME_5_RESULTS
def test__basic_game_3(capsys, monkeypatch): monkeypatch.setattr("builtins.input", lambda x: "") rand = Random() rand.seed(1) deck = Deck(empty=True, random_instance=rand) deck.add_card(Card("Clubs", "9")) deck.shuffle() game = Game(PLAYERS_1, deck=deck, message=MESSAGE, max_rounds=2) game.play() captured = capsys.readouterr() assert captured.out == GAME_3_RESULTS
def test_basic_empty_deck(): deck = Deck(empty=True) assert len(deck) == 0 assert deck == [] assert deck.top_card is None card_1 = Card("Clubs", "9") deck.add_card(card_1) assert len(deck) == 1 assert deck == [card_1] assert deck.top_card == card_1 card_2 = Card("Spades", "King") deck.add_card(card_2) assert len(deck) == 2 assert deck == [card_1, card_2] assert deck.top_card == card_2 deck.new_deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK assert deck.top_card == UNSHUFFLED_DECK[-1]
def test_basic_deck_iter(): deck = Deck(empty=True) with pytest.raises(StopIteration): card = next(deck) deck = Deck() for i in range(0, 52): card = next(deck) assert card == UNSHUFFLED_DECK[i] with pytest.raises(StopIteration): card = next(deck) deck.new_deck() for i in range(0, 52): card = next(deck) assert card == UNSHUFFLED_DECK[i] with pytest.raises(StopIteration): card = next(deck) deck.new_deck() for i in range(0, 52): assert deck[i] == UNSHUFFLED_DECK[i]
def clear_hand(self) -> None: """Empty the Player's hand.""" self.hand = Deck(empty=True)
class Player: """The Player class is used to manage a person's hand and score. Player objects are initiated by passing a person's name. Their hand starts out as an empty Deck and is expanded by drawing Cards. Player total score or score after a specific round can be received. A Player can be compared to another Player based on score (< or >) or based on name and hand (==). Attributes: hand: The Player's hand of playing cards they currently hold. name: The Player's name. """ def __init__(self, name: str) -> None: """Initiates Player with a name and an empty hand. Args: name: Name of player. """ self.hand = Deck(empty=True) self.name = name def clear_hand(self) -> None: """Empty the Player's hand.""" self.hand = Deck(empty=True) def draw_card(self, card: Card) -> None: """Have the Player draw a card. Args: card: The Card to add to the Player's hand. """ self.hand.add_card(card) def score(self, round_number: int = 0) -> int: # We are assuming 1-indexed round numbers """Calculate and return the Player's total or round score. Args: card: Optional; The Card to add to the Player's hand. """ if round_number: if round_number > len(self.hand): return 0 return self.hand[round_number - 1].score return sum([card.score for card in self.hand]) def __str__(self): return "{name} holds {hand} totalling {points} points".format( name=self.name, hand=self.hand, points=self.score()) def __eq__(self, other): if not isinstance(other, Player): return False return self.hand == other.hand and self.name == other.name def __gt__(self, other): if not isinstance(other, Player): return False return self.score() > other.score() def __lt__(self, other): if not isinstance(other, Player): return False return self.score() < other.score()
def test_basic_deck_string(): rand = Random() rand.seed(1) deck = Deck() assert str(deck) == str(UNSHUFFLED_DECK_TUPLES) deck.get_card() assert str(deck) == str(UNSHUFFLED_DECK_TUPLES[:-1]) deck = Deck(random_instance=rand) deck.shuffle() assert str(deck) == str(SHUFFLED_DECK_1_TUPLES) deck.get_card() assert str(deck) == str(SHUFFLED_DECK_1_TUPLES[:-1]) deck = Deck() sorted_deck = deck.sort_cards(SORT_ORDER) assert str(sorted_deck) == str(SORTED_DECK_TUPLES) sorted_deck.get_card() assert str(sorted_deck) == str(SORTED_DECK_TUPLES[:-1]) for i in range(0, 51): sorted_deck.get_card() assert str(sorted_deck) == str([])
def test_basic_deck_shuffle(): deck = Deck() deck.shuffle() assert len(deck) == 52 assert deck != UNSHUFFLED_DECK assert set(deck) == set(UNSHUFFLED_DECK) deck.get_card() assert len(deck) == 51 assert deck != UNSHUFFLED_DECK[:-1] assert set(deck).issubset(UNSHUFFLED_DECK) for i in range(0, 50): deck.get_card() deck.shuffle() assert len(deck) == 1 assert set(deck).issubset(UNSHUFFLED_DECK) deck.get_card() deck.shuffle() assert len(deck) == 0 assert deck == [] with pytest.raises(DeckEmptyError): card = deck.get_card() assert deck.top_card is None deck.shuffle() assert len(deck) == 0 assert deck == [] deck.new_deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK assert deck.top_card == UNSHUFFLED_DECK[-1]
def test_basic_deck_create(): deck = Deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK
def test_basic_player_1(): with pytest.raises(TypeError): player_1 = Player() deck = Deck(empty=True) player_1 = Player("Berkelly") assert player_1.name == "Berkelly" assert player_1.hand == deck assert str(player_1.hand) == str(deck) assert player_1.score() == 0 assert not player_1.score(round_number=1) assert str(player_1) == STRING_1 card_1 = Card("Clubs", "King") deck.add_card(card_1) player_1.draw_card(card_1) assert player_1.hand == deck assert str(player_1.hand) == str(deck) assert player_1.score() == 52 assert player_1.score(round_number=1) == 52 assert str(player_1) == STRING_2 card_2 = Card("Hearts", "8") deck.add_card(card_2) player_1.draw_card(card_2) assert player_1.hand == deck assert str(player_1.hand) == str(deck) assert player_1.score() == 76 assert player_1.score(round_number=1) == 52 assert player_1.score(round_number=2) == 24 assert str(player_1) == STRING_3 assert player_1.hand == deck assert str(player_1.hand) == str(deck) assert player_1.score() == 76 assert player_1.score(round_number=1) == 52 assert player_1.score(round_number=2) == 24 assert str(player_1) == STRING_3 deck = Deck(empty=True) player_1.clear_hand() assert player_1.name == "Berkelly" assert player_1.hand == deck assert str(player_1.hand) == str(deck) assert player_1.score() == 0 assert not player_1.score(round_number=1) assert str(player_1) == STRING_1 player_1.draw_card(card_1) player_1.draw_card(card_2) player_2 = Player("Cez") assert player_1 > player_2 assert player_2 < player_1 card_1 = Card("Diamonds", "8") card_2 = Card("Clubs", "2") player_2.draw_card(card_1) player_2.draw_card(card_2) assert player_1 > player_2 assert player_2 < player_1 player_1 = Player("Cez") player_1.draw_card(card_1) player_1.draw_card(card_2) assert player_1 == player_2 player_1 = Player("Cez") player_1.draw_card(card_2) assert player_1 != player_2
def test_basic_deck_sort(): deck = Deck() sorted_deck = deck.sort_cards(SORT_ORDER) assert len(sorted_deck) == 52 assert sorted_deck == SORTED_DECK assert sorted_deck.top_card == SORTED_DECK[-1] card = sorted_deck.get_card() assert len(sorted_deck) == 51 assert card == SORTED_DECK[-1] assert sorted_deck == SORTED_DECK[:-1] assert sorted_deck.top_card == SORTED_DECK[-2] for i in range(50, -1, -1): card = sorted_deck.get_card() assert card == SORTED_DECK[i] assert len(sorted_deck) == 0 assert sorted_deck == [] assert sorted_deck.top_card is None sorted_deck = sorted_deck.sort_cards(SORT_ORDER) assert sorted_deck == [] deck = Deck() card_1 = deck.get_card() card_2 = deck.get_card() sorted_deck = deck.sort_cards(SORT_ORDER) assert sorted_deck == list( filter(lambda x: x not in [card_1, card_2], SORTED_DECK)) assert sorted_deck.top_card == SORTED_DECK[-1] deck = Deck() deck.shuffle() sorted_deck = deck.sort_cards(SORT_ORDER) assert sorted_deck == SORTED_DECK assert sorted_deck.top_card == SORTED_DECK[-1] deck.new_deck() assert len(deck) == 52 assert deck == UNSHUFFLED_DECK assert deck.top_card == UNSHUFFLED_DECK[-1]
class Game: """The Game class allows users to start new card games. Game objects are initiated with a list of the names of the players who wish to play. Optionally, a custom message dictionary can be passed for a change in game verbiage. A custom Deck can also be used. The max number of rounds is 3 by default. A Game can be started using .play() and new rounds will be played until the max number of rounds is reached. During each round, players take turns drawing cards by pressing enter. After the game is finished, a final scoreboard is printed. A new Game can be played after invoking .new_game(). Attributes: players: Game's Players object. """ def __init__( self, names: List[str], deck: Optional[Deck] = None, max_rounds: int = 3, message: Optional[Dict[str, List[str]]] = None ) -> None: """Initiates a Game by creating Players from names. Creates a Deck and shuffles if none is supplied. Generates internal message dictionary from messages.conf if none is supplied. Sets max rounds to 3 if none is supplied. Sets Game to active. Args: name: Names of players. message: Optional; Message dictionary used for game verbiage. deck: Optional; Deck to use during the game. max_rounds: Optional; Max number of rounds to play. """ if max_rounds < 1: raise GameError("Invalid max number of rounds.") if len(names) == 0: raise GameError("Invalid number of players.") if message: self._message = message else: conf_reader = ConfigParser() try: message_path = join(dirname(abspath(__file__)), "messages.conf") with open(message_path) as file: conf_reader.read_file(file) except IOError: raise GameError( "Could not open messages config file.") from None self._message = {k: list(v.values()) for k, v in dict(conf_reader).items()} if deck: self._deck = deck else: self._deck = Deck() self._deck.shuffle() self.players = Players(names) self._current_round = 1 self._is_active = True self._max_rounds = max_rounds self._rand = Random() def end_game(self) -> None: """Sets game to inactive thus ending the Game.""" self._is_active = False def new_game(self) -> None: """Resets the Game: Sets Game back to active, resets Players, creates new Deck and shuffles. """ self._is_active = True self._deck.new_deck() self._deck.shuffle() self._current_round = 1 self.players.reset() def play(self) -> None: """Launches game. Keeps initiating a new round until Game is no longer active (max round limit reached). Prints leaderboard at the end. """ self._display_message("WELCOME") while self._is_active: self._display_message( "ROUND START", current_round=self._current_round) self._next_round() last_round = self._current_round - 1 if self.players.first(last_round).tie: self._display_message("ROUND END TIE") else: self._display_message( "ROUND END", round_winner=self.players.first(last_round).name) if self.players.first().tie: self._display_message("GAME OVER TIE") else: self._display_message( "GAME OVER", game_winner=self.players.first().name) print(self.players) def _next_round(self) -> None: """Starts the next round. Iterates through Players and asks them to draw a Card by hitting enter. If the Deck runs out of Cards will display a notification and generate a new Deck to draw from. """ for player in self.players: self._display_message( "TURN START", current_player_name=player.name) input("Hit enter to draw a card.") try: card = self._deck.get_card() except DeckEmptyError: self._display_message("EMPTY DECK") self._deck.new_deck() self._deck.shuffle() card = self._deck.get_card() player.draw_card(card) pos_result = player in self.players.first().players self._display_message( "{result} TURN END".format(result=str(pos_result).upper()), current_player_name=player.name, current_card=card, current_player_score=player.score()) if self._current_round >= self._max_rounds: self.end_game() self._current_round += 1 def _display_message(self, category: str, **kwargs) -> None: """Takes a category of message and randomly selects a corresponding message template from the instance's message dictionary, subsititutes any given args, and prints message. Args: category: Message category. **kwargs: Any arguments to be subsituted in the message template """ message = self._rand.choice(self._message[category]) message_template = Template(message) print(message_template.substitute(kwargs)) def __bool__(self): return self._is_active