def create_all_next_surviving_actions(self, game: Game) -> List[Action]: """Calculates not only one but all actions that will let the player survive for the next rounds. Args: game: The current state of the game. Returns: A list of actions which will let the player survive for the next rounds. """ root = SearchTreeRoot(game.copy()) player_ids_to_watch = game.get_other_player_ids( self.player, self.__distance_to_check, True) combinations = Action.get_combinations(len(player_ids_to_watch)) search_tree_actions = [] for action in Action.get_actions(): if root.calculate_action(self.player, player_ids_to_watch, combinations, self.__depth, self._turn_ctr, True, [action], self._max_speed, True) is not None: search_tree_actions.append(action) return search_tree_actions
def __try_combination(game: Game, player_ids_to_watch: List[int], combination: Tuple[Action], turn_counter: int): modified_game = game.copy() game_service = GameService(modified_game) game_service.turn.turn_ctr = turn_counter players = modified_game.get_players_by_ids(player_ids_to_watch) for j in range(len(combination)): action = combination[j] player = players[j] SearchTreeRoot.__perform_simulation(game_service, action, player) game_service.check_and_set_died_players() return SearchTreeRoot(modified_game.copy())
def test_copying_a_game_should_return_same_game_but_different_identity( self): player1 = Player(1, 1, 1, Direction.up, 0, True, "Name") player2 = Player(2, 1, 0, Direction.up, 0, True, "Name2") player3 = Player(3, 0, 0, Direction.up, 0, True, "Name3") players = [player1, player2, player3] cells = [[Cell([player3]), Cell([player2])], [Cell([]), Cell([player1])]] game = Game(2, 2, cells, players, 2, True, datetime.now()) result = game.copy() self.assertEqual(game, result) self.assertNotEqual(id(game), id(result))
def create_next_action(self, game: Game, return_value: Value): """See base class.""" self._turn_ctr += 1 root = SearchTreeRoot(game.copy()) player_ids_to_watch = game.get_other_player_ids( self.player, self.__distance_to_check, True) combinations = Action.get_combinations(len(player_ids_to_watch)) action = root.calculate_action(self.player, player_ids_to_watch, combinations, self.__depth, self._turn_ctr, True, [], self._max_speed, self.__randomize) return_value.value = (action if action is not None else Action.get_random_action()).get_index()
def find_actions_by_best_path_connection(self, actions: List[Action], game: Game) -> Optional[ List[Tuple[Action, int]]]: """ Calculates for the passed actions how many paths are still accessible after the execution of the action. For this purpose, points are randomly generated on the playing field and an algorithm for finding paths is used to check whether the point can be reached. Args: actions: List of actions to check. game: The game that contains the current state of the game. Returns: List of actions with the accessible paths. """ if actions is None or len(actions) == 0: return None # shuffle the actions, so that different actions are chosen if they have the same quality and the AI is not so # easily predictable. shuffle(actions) actions_with_possible_paths: List[Tuple[Action, int]] = [] free_cells_for_pathfinding = self.get_random_free_cells_from_playground(game) path_finder = BestFirst(diagonal_movement=DiagonalMovement.never) for action in actions: game_copy = game.copy() game_service = GameService(game_copy) try: player = game_service.game.get_player_by_id(self.player.id) game_service.visited_cells_by_player[player.id] = game_service.get_and_visit_cells(player, action) except InvalidPlayerMoveException: continue matrix = game_copy.translate_cell_matrix_to_pathfinding_matrix() current_possible_paths = 0 length_free_cells = len(free_cells_for_pathfinding) for i in range(length_free_cells): grid = Grid(matrix=matrix) start = grid.node(player.x, player.y) end = grid.node(free_cells_for_pathfinding[i][0], free_cells_for_pathfinding[i][1]) path, _ = path_finder.find_path(start, end, grid) if len(path) > 0: # a path exists current_possible_paths += 1 actions_with_possible_paths.append((action, current_possible_paths)) # Action with most accessible paths at index 0 actions_with_possible_paths.sort(key=operator.itemgetter(1), reverse=True) return actions_with_possible_paths
class OfflineController(Controller): def __init__(self, view: View): """Creates a new offline controller. Args: view: The UI that should be used. """ super().__init__(view) self.__you = None self._game = None self._game_round = 0 self._ais = [] def play(self): """See base class.""" self._create_game() game_service = GameService(self._game, ignore_deadline=False) self._view.update(self._game) while self._game.running: self._game_round += 1 time_to_react = randint(4, 16) self.__reset_game_deadline(time_to_react) # Read input from user if there is a human player player_action = None if self.__you is not None and self.__you.active: player_action = self._view.read_next_action() if datetime.now(time_zone) > self._game.deadline: player_action = Action.get_default() self.__reset_game_deadline(time_to_react) for ai in self._ais: if ai is not None and ai.player.active: action = self.__choose_ai_action(ai, time_to_react) game_service.do_action(ai.player, action) self.__reset_game_deadline(time_to_react) # Perform action of human player after AIs finished their calculations # Otherwise the AIs would already know the players move if self.__you is not None and player_action is not None: game_service.do_action(self.__you, player_action) self._view.update(self._game) def _create_game(self) -> None: player1 = Player(1, 5, 5, Direction.down, 1, True, "Human Player 1") player2 = Player(2, 25, 5, Direction.down, 1, True, "AI Player 1") player3 = Player(3, 5, 15, Direction.up, 1, True, "AI Player 2") player4 = Player(4, 25, 15, Direction.up, 1, True, "AI Player 4") players = [player1, player2, player3, player4] height = 20 width = 30 cells = [[Cell() for _ in range(width)] for _ in range(height)] cells[player1.y][player1.x] = Cell([player1]) cells[player2.y][player2.x] = Cell([player2]) cells[player3.y][player3.x] = Cell([player3]) cells[player4.y][player4.x] = Cell([player4]) self._game = Game(width, height, cells, players, 1, True, datetime.now(time_zone)) self._game_round = 0 self.__you = None ai0 = ai_classes.RandomAI(player1) # Make a comment out of the next two lines if you do not want to play on your own. self.__you = player1 ai0 = None ai1 = ai_classes.PathfindingAI(player2, 2, 75) ai2 = ai_classes.NotKillingItselfAI(player3, [ai_classes.AIOptions.max_distance], 1, 0, 3) ai3 = ai_classes.SearchTreeAI(player4, 2, 1, True, 10) self._ais = [ai0, ai1, ai2, ai3] def _log_execution_time(self, ai: ArtificialIntelligence, execution_time: float): pass def __reset_game_deadline(self, time_to_react: int): self._game.deadline = datetime.now(time_zone) + timedelta(0, time_to_react) def __choose_ai_action(self, ai: ArtificialIntelligence, time_to_react: int) -> Action: return_value = multiprocessing.Value('i', Action.get_default().get_index()) process = multiprocessing.Process(target=Controller.call_ai, args=(ai, self._game.copy(), return_value,)) start = datetime.now(time_zone) process.start() process.join(time_to_react - 1) # wait time_to_react seconds minus one for the calculation to be finished if process.is_alive(): # If an execution is terminated, the execution time is set to 1 minute. start = datetime.now(time_zone) - timedelta(seconds=60) process.terminate() self._log_execution_time(ai, (datetime.now(time_zone) - start).total_seconds()) return Action.get_by_index(return_value.value)