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))
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
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
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))
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
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]
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))
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))
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))
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))
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))
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))
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
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))
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))
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))
def is_current_player_playing(self): """True if the current player has not been eliminated""" return PlayerTools.is_playing(self.player())
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)]
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])