Example #1
0
    def test_move_handmaid(self):
        """Deploy the handmaid and survive attack"""
        game = Game.new(4, 2)
        action = PlayerAction(Card.handmaid, 0, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]
        self.assertTrue(PlayerTools.is_playing(player))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertEqual(player.actions[0], action)
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))

        action_attack = PlayerAction(Card.guard, 0, Card.prince, Card.noCard)
        game, _ = game.move(action_attack)

        players = game.players()
        target = players[0]
        player = players[1]
        self.assertTrue(PlayerTools.is_playing(player))
        self.assertTrue(PlayerTools.is_playing(target))

        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertEqual(player.actions[0], action_attack)
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))

        for action in target.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #2
0
 def _move_princess(self, dealt_card, new_deck):
     """Handle a princess action into a new game state"""
     player = PlayerTools.force_discard(self.player(), dealt_card)
     player = PlayerTools.force_discard(player)
     current_players = Game._set_player(
         self._players, player, self.player_turn())
     return Game(new_deck, current_players, self._turn_index + 1)
Example #3
0
    def _move_prince(self, current_players, action, deck_new):
        """Handle a prince action into a new game state"""

        player_before_discard = current_players[action.player_target]
        action_updated = action._replace(player=self.player_turn(),
                                         force_discarded=player_before_discard.hand_card,
                                         force_discarder=action.player_target)
        # if there are no more cards, this has no effect
        if len(deck_new) - 1 < 1:
            return Game(deck_new, current_players, self._turn_index + 1,
                        [*self._action_log, action_updated])

        if player_before_discard.hand_card == Card.princess:
            player_post_discard = PlayerTools.force_discard(
                player_before_discard)
            deck_final = deck_new
        else:
            player_post_discard = PlayerTools.force_discard(
                player_before_discard, deck_new[0])
            deck_final = deck_new[1:]

        current_players = Game._set_player(
            current_players, player_post_discard, action.player_target)

        return Game(deck_final, current_players, self._turn_index + 1,
                    [*self._action_log, action_updated])
Example #4
0
 def _to_str_player(self, idx, player):
     is_playing = " " if PlayerTools.is_playing(player) else "☠️"
     is_turn = "⭐" if self.player_turn() == idx else " "
     draw_card = self.draw_card() if self.active(
     ) and self.player_turn() == idx else Card.noCard
     draw_card_render = Card.render_card_number(draw_card)
     header = "Player {} {} {}".format(idx, is_turn, is_playing)
     state = "   Current: {} {}".format(
         draw_card_render, PlayerTools.to_str(player))
     return [header, state]
Example #5
0
 def is_winner(self, idx):
     """True iff that player has won the game"""
     if self.active():
         return False
     player = self._players[idx]
     if not PlayerTools.is_playing(player):
         return False
     other_scores = [
         p.hand_card > player.hand_card for p in self._players if PlayerTools.is_playing(p)]
     return sum(other_scores) == 0
Example #6
0
    def is_action_valid(self, action):
        """Tests if an action is valid given the current game state"""
        player = self.player()

        # if player is out, only valid action is no action
        if player.hand_card == Card.noCard:
            return PlayerActionTools.is_blank(action)

        target_player = self._players[action.player_target]
        player_hand = [player.hand_card, self._deck[0]]

        # cannot discard a card not in the hand
        if action.discard not in player_hand:
            return False

        new_hand_card = Game.new_hand_card(action.discard, player_hand)

        # countess must be discarded if the other card is king/prince
        if new_hand_card == Card.countess and \
                (action.discard == Card.prince or action.discard == Card.king):
            return False

        # cannot target an invalid player
        if not self._is_valid_player_target(action.player_target):
            return False

        # cannot mis-target a card
        if self.player_turn() == action.player_target and action.discard in Card.only_other:
            # Check if self is the only valid target due to everybody else protected (or dead)
            other_players_invalid = [not PlayerTools.is_playing(p) or PlayerTools.is_defended(p)
                                     for p in self._players if p is not self.player()]

            if all(other_players_invalid):
                return True
            else:
                return False

        if self.player_turn() != action.player_target and action.discard in Card.only_self:
            return False

        if not PlayerTools.is_playing(target_player):
            return False

        # Check if target is defender (and not the current player)
        if PlayerTools.is_defended(target_player) and player != target_player:
            return False

        # Cannot guess guard or no card
        if action.discard == Card.guard and (
                action.guess == Card.guard or action.guess == Card.noCard):
            return False

        return True
Example #7
0
    def _move_king(self, current_players, action, deck_new):
        """Handle a king action into a new game state"""
        player = current_players[self.player_turn()]
        target = current_players[action.player_target]

        player_new = PlayerTools.set_hand(player, target.hand_card)
        target_new = PlayerTools.set_hand(target, player.hand_card)

        current_players = Game._set_player(
            current_players, player_new, self.player_turn())
        current_players = Game._set_player(
            current_players, target_new, action.player_target)

        return Game(deck_new, current_players, self._turn_index + 1)
Example #8
0
    def _move_guard(self, current_players, action, deck_new):
        """
        Handle a guard action into a new game state

        Player makes a guess to try and eliminate the opponent
        """
        if self._players[action.player_target].hand_card == action.guess and \
                not PlayerTools.is_defended(self._players[action.player_target]):
            # then target player is out
            player_target = PlayerTools.force_discard(
                self._players[action.player_target])
            current_players = Game._set_player(
                current_players, player_target, action.player_target)

        return Game(deck_new, current_players, self._turn_index + 1)
Example #9
0
    def test_move_guard_success(self):
        """Getting a guard move, with a right guess"""
        game = Game.new()
        action = PlayerAction(Card.guard, 3, Card.handmaid, 0)
        self.assertEqual(len(game.opponents()), 3)
        self.assertListEqual(game.opponent_turn(), [1, 2, 3])
        game, _ = game.move(action)

        self.assertEqual(game.round(), 0)
        self.assertEqual(game.player_turn(), 1)
        self.assertEqual(game.cards_left(), 10)
        self.assertTrue(game.active())
        self.assertFalse(game.over())

        players = game.players()
        player = players[0]
        target = players[3]
        recent_action = player.actions[0]
        self.assertListEqual(game.opponent_turn(), [0, 2])
        self.assertEqual(len(game.opponents()), 2)

        self.assertFalse(PlayerTools.is_playing(target))
        self.assertEqual(recent_action, action)
        self.assertEqual(player.hand_card, Card.handmaid)
        self.assertFalse(PlayerActionTools.is_blank(recent_action))
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #10
0
 def test_init(self):
     """Create a Player"""
     player = PlayerTools.blank(1)
     self.assertEqual(player.hand_card, 1)
     self.assertEqual(len(player.actions), 8)
     for action in player.actions:
         self.assertTrue(PlayerActionTools.is_blank(action))
Example #11
0
 def test_from_np(self):
     """Player from a numpy array"""
     player = Player(1,
                     [PlayerAction(1, 3, 5, 0),
                      PlayerAction(3, 0, 0, 2)])
     arr = np.array([1, 1, 3, 5, 0, 3, 0, 0, 2], dtype=np.uint8)
     player_res = PlayerTools.from_np(arr)
     self.assertEqual(player_res, player)
Example #12
0
    def __init__(self, deck, players, turn_index):
        self._deck = deck
        self._players = players
        self._turn_index = turn_index

        total_playing = sum(
            [1 for player in players if PlayerTools.is_playing(player)])
        self._game_active = total_playing > 1 and self.cards_left() > 0
Example #13
0
    def _move_priest(self, action, player_hand_new, deck_new):
        """
        Handle a priest action into a new game state

        Action gains knowledge of other player's card
        """
        player_targets_card = Card.noCard if \
            PlayerTools.is_defended(self._players[action.player_target]) \
            else self._players[action.player_target].hand_card
        action_updated = PlayerAction(
            action.discard, action.player_target, action.guess, player_targets_card)

        player = PlayerTools.move(
            self.player(), player_hand_new, action_updated)
        current_players = Game._set_player(
            self._players, player, self.player_turn())

        return Game(deck_new, current_players, self._turn_index + 1)
Example #14
0
 def test_to_np(self):
     """Player to a numpy array"""
     player = Player(1,
                     [PlayerAction(1, 3, 5, 0),
                      PlayerAction(3, 0, 0, 0)])
     arr = np.array([1, 1, 3, 5, 0, 3, 0, 0, 0], dtype=np.uint8)
     arr_res = PlayerTools.to_np(player)
     self.assertEqual(len(arr_res), len(arr))
     self.assertTrue((arr_res == arr).all())
Example #15
0
    def test_move_baron_failure(self):
        """Getting a baron move, with a failure"""
        game = Game.new(4, 48)
        action = PlayerAction(Card.baron, 1, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]
        target = players[1]

        self.assertFalse(PlayerTools.is_playing(player))
        self.assertTrue(PlayerTools.is_playing(target))

        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[1]))
        for action in player.actions[2:]:
            self.assertTrue(PlayerActionTools.is_blank(action))

        for action in target.actions:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #16
0
    def test_move_king(self):
        """Use king to swap hands with the target"""
        game = Game.new(4, 0)
        action = PlayerAction(Card.king, 1, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]
        target = players[1]

        self.assertTrue(PlayerTools.is_playing(player))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertEqual(player.actions[0], action)
        self.assertEqual(player.hand_card, Card.priest)
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))

        self.assertTrue(PlayerTools.is_playing(target))
        self.assertEqual(target.hand_card, Card.guard)
        for action in target.actions:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #17
0
    def _move(self, action, throw=False):
        """Current player makes an action.

        Returns (NewGame and Reward)<Game,int>"""


        if self.over() or not self.is_action_valid(action):
            return self._invalid_input(throw)

        # player is out, increment turn index
        if action.discard == Card.noCard:
            return Game(self.deck(), self.players(), self.turn_index() + 1,
                        self._action_log)

        player = self.player()
        player_hand = [player.hand_card, self._deck[0]]
        player_hand_new = Game.new_hand_card(action.discard, player_hand)
        deck_new = self._deck[1:]

        # choosing to discard the princess ... is valid
        if action.discard == Card.princess:
            return self._move_princess(self._deck[0], action, deck_new)

        # priest requires modification of action (knowledge)
        if action.discard == Card.priest:
            return self._move_priest(action, player_hand_new, deck_new)

        # updated players for the next turn
        player = PlayerTools.move(self.player(), player_hand_new, action)
        current_players = Game._set_player(
            self._players, player, self.player_turn())

        if action.discard == Card.baron:
            return self._move_baron(action, current_players, player_hand_new, deck_new)

        # No other logic for handmaids or countess
        if action.discard == Card.handmaid or \
                action.discard == Card.countess:
            action_updated = action._replace(player=self.player_turn())
            return Game(deck_new, current_players, self._turn_index + 1,
                        [*self._action_log, action_updated])

        if action.discard == Card.guard:
            return self._move_guard(current_players, action, deck_new)

        if action.discard == Card.prince:
            return self._move_prince(current_players, action, deck_new)

        if action.discard == Card.king:
            return self._move_king(current_players, action, deck_new)

        raise NotImplementedError("Missing game logic")
Example #18
0
    def test_move(self):
        """Player performs a move"""
        player = PlayerTools.blank(1)
        player_next = PlayerTools.move(player, 4, PlayerAction(3, 2, 0, 0))

        self.assertEqual(player.hand_card, 1)
        self.assertEqual(len(player.actions), 8)
        for action in player.actions:
            self.assertTrue(PlayerActionTools.is_blank(action))

        self.assertEqual(player_next.hand_card, 4)
        self.assertEqual(len(player_next.actions), 8)
        action = player_next.actions[0]

        self.assertEqual(action.discard, 3)
        self.assertEqual(action.player_target, 2)
        self.assertEqual(action.guess, 0)
        self.assertEqual(action.revealed_card, 0)
        self.assertEqual(PlayerActionTools.is_blank(action), False)

        for action in player_next.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #19
0
    def test_move_baron_success(self):
        """Getting a baron move, with a success"""
        game = Game.new(4, 48)
        action = PlayerAction(Card.baron, 3, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]
        target = players[3]
        recent_action = player.actions[0]

        self.assertTrue(PlayerTools.is_playing(player))
        self.assertFalse(PlayerTools.is_playing(target))
        self.assertEqual(recent_action, action)

        self.assertFalse(PlayerActionTools.is_blank(recent_action))
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))

        self.assertFalse(PlayerActionTools.is_blank(target.actions[0]))
        for action in target.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #20
0
    def test_move_prince_other(self):
        """Use prince to force another to discard"""
        game = Game.new(4, 2)
        action = PlayerAction(Card.prince, 1, Card.noCard, Card.noCard)
        action_target = PlayerAction(Card.guard, 0, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]
        target = players[1]

        self.assertTrue(PlayerTools.is_playing(player))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertEqual(player.actions[0], action)
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))

        self.assertTrue(PlayerTools.is_playing(target))
        self.assertFalse(PlayerActionTools.is_blank(target.actions[0]))
        self.assertEqual(target.actions[0], action_target)
        for action in target.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #21
0
    def _move_baron(self, action, current_players, player_hand_new, deck_new):
        """
        Handle a baron action into a new game state

        Player and target compare hand cards. Player with lower hand
        card is eliminated
        """
        card_target = self._players[action.player_target].hand_card
        if player_hand_new > card_target:
            if not PlayerTools.is_defended(self._players[action.player_target]):
                # target is eliminated
                player_target = PlayerTools.force_discard(
                    self._players[action.player_target])
                current_players = Game._set_player(
                    current_players, player_target, action.player_target)
                action_updated = action._replace(player=self.player_turn(),
                                                 force_discarded=card_target,
                                                 force_discarder=action.player_target)
            else:
                action_updated = action._replace(player=self.player_turn())

        elif player_hand_new == card_target:
            # Tie, nobody wins
            action_updated = action._replace(player=self.player_turn(),
                                             revealed_card=card_target)

        else:
            # player is eliminated
            player = PlayerTools.force_discard(self.player(), player_hand_new)
            player = PlayerTools.force_discard(player)
            current_players = Game._set_player(
                current_players, player, self.player_turn())
            action_updated = action._replace(player=self.player_turn(),
                                             force_discarded=player_hand_new,
                                             force_discarder=action.player)

        return Game(deck_new, current_players, self._turn_index + 1,
                    [*self._action_log, action_updated])
Example #22
0
    def _move_baron(self, action, current_players, player_hand_new, deck_new):
        """
        Handle a baron action into a new game state

        Player and target compare hand cards. Player with lower hand
        card is eliminated
        """
        card_target = self._players[action.player_target].hand_card
        if player_hand_new > card_target:
            if not PlayerTools.is_defended(self._players[action.player_target]):
                # target is eliminated
                player_target = PlayerTools.force_discard(
                    self._players[action.player_target])
                current_players = Game._set_player(
                    current_players, player_target, action.player_target)
        else:
            # player is eliminated
            player = PlayerTools.force_discard(self.player(), player_hand_new)
            player = PlayerTools.force_discard(player)
            current_players = Game._set_player(
                current_players, player, self.player_turn())

        return Game(deck_new, current_players, self._turn_index + 1)
Example #23
0
    def test_move_prince_self(self):
        """Use prince to force self discard"""
        game = Game.new(4, 2)
        action = PlayerAction(Card.prince, 0, Card.noCard, Card.noCard)
        action_other = PlayerAction(Card.handmaid, 0, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]
        self.assertTrue(PlayerTools.is_playing(player))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[1]))
        self.assertEqual(player.actions[0], action)
        self.assertEqual(player.actions[1], action_other)
        for action in player.actions[2:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #24
0
    def test_move_princess(self):
        """Commit suicide by discarding the princess"""
        game = Game.new(4, 11)
        action = PlayerAction(Card.princess, 0, Card.noCard, Card.noCard)
        game, _ = game.move(action)

        players = game.players()
        player = players[0]

        self.assertFalse(PlayerTools.is_playing(player))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[0]))
        self.assertFalse(PlayerActionTools.is_blank(player.actions[1]))
        self.assertEqual(player.actions[0],
                         PlayerAction(Card.baron, 0, Card.noCard, Card.noCard))
        self.assertEqual(player.actions[1], action)

        for action in player.actions[2:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #25
0
    def is_action_valid(self, action):
        """Tests if an action is valid given the current game state"""
        player = self.player()

        # if player is out, only valid action is no action
        if player.hand_card == Card.noCard:
            return PlayerActionTools.is_blank(action)

        target_player = self._players[action.player_target]
        player_hand = [player.hand_card, self._deck[0]]

        # cannot discard a card not in the hand
        if action.discard not in player_hand:
            return False

        new_hand_card = Game.new_hand_card(action.discard, player_hand)

        # countess must be discarded if the other card is king/prince
        if new_hand_card == Card.countess and \
                (action.discard == Card.prince or action.discard == Card.king):
            return False

        # cannot target an invalid player
        if not self._is_valid_player_target(action.player_target):
            return False

        # cannot mis-target a card
        if self.player_turn() == action.player_target and action.discard in Card.only_other:
            return False
        if self.player_turn() != action.player_target and action.discard in Card.only_self:
            return False

        if not PlayerTools.is_playing(target_player):
            return False

        # Cannot guess guard or no card
        if action.discard == Card.guard and (
                action.guess == Card.guard or action.guess == Card.noCard):
            return False

        return True
Example #26
0
    def test_move_priest(self):
        """Getting a priest move"""
        game = Game.new(4, 5)
        action = PlayerAction(Card.priest, 1, Card.noCard, Card.noCard)
        action_expected = PlayerAction(Card.priest, 1, Card.noCard, Card.guard)
        game, _ = game.move(action)

        self.assertEqual(game.round(), 0)
        self.assertEqual(game.player_turn(), 1)
        self.assertEqual(game.cards_left(), 10)
        self.assertTrue(game.active())
        self.assertFalse(game.over())

        players = game.players()
        player = players[0]
        target = players[1]
        recent_action = player.actions[0]

        self.assertTrue(PlayerTools.is_playing(target))
        self.assertEqual(recent_action, action_expected)
        self.assertFalse(PlayerActionTools.is_blank(recent_action))
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #27
0
    def test_move_guard_failure(self):
        """Getting a guard move, with a wrong guess"""
        game = Game.new()
        action = PlayerAction(Card.guard, 1, Card.handmaid, 0)
        game, _ = game.move(action)

        self.assertEqual(game.round(), 0)
        self.assertEqual(game.player_turn(), 1)
        self.assertEqual(game.cards_left(), 10)
        self.assertTrue(game.active())
        self.assertFalse(game.over())

        players = game.players()
        player = players[0]
        target = players[1]
        recent_action = player.actions[0]

        self.assertTrue(PlayerTools.is_playing(target))
        self.assertEqual(recent_action, action)
        self.assertEqual(player.hand_card, Card.handmaid)
        self.assertFalse(PlayerActionTools.is_blank(recent_action))
        for action in player.actions[1:]:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #28
0
    def test_move_guard_guess_guard(self):
        """Getting a guard move and guessing guard"""
        game = Game.new()
        action = PlayerAction(Card.guard, 1, Card.guard, 0)
        game, _ = game.move(action)

        self.assertEqual(game.round(), 0)
        self.assertEqual(game.player_turn(), 0)
        self.assertEqual(game.cards_left(), 11)
        self.assertTrue(game.active())
        self.assertFalse(game.over())

        players = game.players()
        player = players[0]
        target = players[1]
        recent_action = player.actions[0]

        self.assertTrue(PlayerTools.is_playing(player))
        self.assertEqual(player.hand_card, Card.handmaid)
        self.assertEqual(game.deck()[0], Card.guard)
        self.assertTrue(PlayerActionTools.is_blank(recent_action))
        for action in player.actions:
            self.assertTrue(PlayerActionTools.is_blank(action))
Example #29
0
 def opponent_turn(self):
     """Returns the opposing players indices"""
     return [idx for idx, player in enumerate(self._players)
             if idx != self.player_turn() and
             PlayerTools.is_playing(player)]
Example #30
0
 def _is_valid_player_target(self, player_target):
     """True iff the player can be targeted by an action"""
     return PlayerTools.is_playing(self._players[player_target])