def settle(self, players, result): if self.tag is None: raise GameError('Tag is undefined for rule %s.' % self.__class__.__name__) for player in players: if not player.is_out and player.has_tag(self.tag): self.settle_tagged_player(player, players, result)
def get_alignment(cls, *roles): alignment = 0 for role in roles: if role not in cls.ALIGNMENTS: raise GameError('Invalid role %s.' % role) alignment += cls.ALIGNMENTS[role] return alignment
def execute_action(self, action_name, targets, option=None): # Execute the current action and check if game is over. if not self.game.is_ongoing: raise GameError( 'Cannot execute action because game is not ongoing.') action = self.get_current_action() if action.name != action_name: raise GameError('Invalid action name %s, expected %s.' % (action_name, action.name)) logger.info('About to execute %s...' % action) result = action.execute(self.game.player_list, targets, option) self.game.log_action_result(result) self.update_game_over() # Move to next action. if not self.game.is_over: self.move_to_next_action(result) self.save_game()
def get_tag(self, option): if option == self.OPTION_CURE and not self.is_cure_used: self.is_cure_used = True return Tag.CURED_BY_WITCH elif option == self.OPTION_POISON and not self.is_poison_used: self.is_poison_used = True return Tag.POISONED_BY_WITCH else: raise GameError('Invalid option for witch: %s' % option)
def start_game(self, force=False): # Check if game can be started. if self.game.is_ongoing and not force: raise GameError('Cannot start game %s: game is ongoing.' % self.game) # Reset the game. logger.info('Resetting game...') self.reset_game() self.game.ensure_unused_players(self.get_num_unused_players()) min_num_players, max_num_players = self.get_min_max_num_players() num_players = len(self.game.player_list) if not (min_num_players <= num_players <= max_num_players): raise GameError( 'Cannot start game %s: there are %d players but requires [%d, %d].' % (self.game, num_players, min_num_players, max_num_players)) # Assign roles to players randomly. player_list = list(self.game.player_list) logger.info('Assigning roles to players...') random.shuffle(player_list) for role in self.get_role_list_on_stage(): assigned_player = None for player in player_list: if player.twin is None or self.get_alignment( player.twin.role, role) != 0: player.role = role player.save() assigned_player = player break assert assigned_player is not None player_list.remove(assigned_player) for player in player_list: player.role = self.get_civilian_role() player.save() # Initialize action list. logger.info('Initializing action list...') self.action_list = self.action_list_class.create_initial( self.get_role_list_on_stage()) # Update game state. logger.info('Updating game state...') self.game.round = 1 self.game.is_over = False self.save_game()
def execute(self, players, targets, option): """ Executes this action. """ # Perform some checks before executing this action. if not self.is_executable(players): raise GameError('%s is not executable.' % self.name) min_num_targets, max_num_targets = self.get_min_max_num_targets() if not (min_num_targets <= len(targets) <= max_num_targets): raise GameError( '%s should be executed on [%d, %d] targets but got %d.' % (self.name, min_num_targets, max_num_targets, len(targets))) for target in targets: if not self.is_executable_on(target): raise GameError('%s is not executable on %s.' % (self.name, target)) # Execute this action. result = ActionResult(self.name) self.execute_with_result(players, targets, option, result) return result
def skip(self, players): """ Skips this action. Only non-executable action can be skipped. """ # Perform some checks before skipping this action. if self.is_executable(players): raise GameError('%s is executable thus cannot be skipped.' % self.name) # Skip this action. result = ActionResult(self.name) result.log('Skipped', visibility=self.role) return result
def load_json_data(cls, json_data): if not json_data: return cls() # Load actions. actions = [] list_data = json_data.get('_list', []) for action_data in list_data: class_name = action_data.pop('_name') action_class = next((clas for clas in cls.action_classes if clas.__name__ == class_name), None) if action_class is None: raise GameError('Invalid action class name: %s' % class_name) action = action_class(**action_data) actions.append(action) # Load index. index = json_data.get('index', 0) # Recreate action list. return cls(actions, index)
def execute_with_result(self, players, targets, option, result): """ Executes this action and fills the result. By default, this method adds the action tag to all the target players (tag should not be None). """ if not targets: result.log('%s did not select anyone' % self.role, visibility=self.role) else: tag = self.get_tag(option) if not tag: raise GameError('Tag is undefined for %s.' % self.name) for target in targets: target.add_tag(tag) text = '%s tagged %s with %s' % (self.role, target, tag) answer = self.get_answer(target) if answer is not None: text += ': %s' % answer result.log(text, visibility=self.role)
def current(self): if not (0 <= self.index < len(self)): raise GameError('Action index out of bound: %d' % self.index) return self[self.index]