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 _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)
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)
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])
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)