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 test_get_random_action(self): not_expected = Action.get_random_action() result = Action.get_random_action() while not_expected == result: result = Action.get_random_action() self.assertNotEqual(not_expected, result)
def test_get_actions_randomly(self): not_expected = [] for action in list(Action): not_expected.append(action) result = Action.get_actions(True) while result == not_expected: result = Action.get_actions(True) self.assertNotEqual(not_expected, 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 __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)
def test_should_select_action_to_let_player_survive_next_two_rounds(self): game = self.data_loader.load(tests.read_test_file("ai/game_4.json")) result = Value('i') self.sut.create_next_action(game, result) self.assertEqual(Action.turn_left, Action.get_by_index(result.value))
def do_action(self, player: Player, action: Action): """Performs the action for a player. Additionally it checks if the game is finished and which players have died when a new turn starts. Args: player: The player who wants to perform the action. action: The action to perform. Raises: InvalidPlayerMoveException: The player is outside the field, has reached an invalid player speed or was not allowed to take any further action this turn. """ try: new_turn = self.turn.action(player) action_to_perform = action \ if self.__ignore_deadline or datetime.now(self.game.deadline.tzinfo) <= self.game.deadline \ else Action.get_default() self.visited_cells_by_player[player.id] = self.get_and_visit_cells( player, action_to_perform) if new_turn: self.check_and_set_died_players() except InvalidPlayerMoveException: self.set_player_inactive(player) self.game.running = self.is_game_running()
def test_create_combinations_as_product(self): player_count = 4 result = Action.get_combinations(player_count) self.assertEqual(pow(len(Action), player_count), len(result)) self.assertEqual(player_count, len(result[0]))
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 test_get_actions_in_order(self): expected = [] for action in list(Action): expected.append(action) result = Action.get_actions() self.assertEqual(expected, result)
def test_should_select_action_to_let_player_survive_next_two_rounds_2(self): game = self.data_loader.load(tests.read_test_file("ai/game_3.json")) sut = SearchTreeAI(game.you, 2, 2) result = Value('i') sut.create_next_action(game, result) self.assertEqual(Action.slow_down, Action.get_by_index(result.value))
def test_should_select_action_to_let_player_survive_next_round(self): game = self.data_loader.load(tests.read_test_file("ai/game_1.json")) sut = SearchTreeAI(game.you, 1, 3, True) result = Value('i') sut.create_next_action(game, result) self.assertEqual(Action.turn_right, Action.get_by_index(result.value))
def test_online_connection_can_be_executed(self, connect_mock): connect_mock.return_value = mock view = Mock() data_loader = Mock() data_loader.load.side_effect = [create_game(True), create_game(False)] data_writer = Mock() data_writer.write.return_value = Action.get_default() controller = OnlineController(view, "", "", "", data_loader, data_writer, PathfindingAI.__name__, (2, 75)) controller.play() view.update.assert_has_calls([call(ANY), call(ANY)], any_order=False) view.end.assert_called_once() data_loader.load.assert_has_calls([call(""), call("")], any_order=False) mock.send.assert_has_calls([call(Action.get_default())])
def test_read_next_action_should_return_correct_action_input_close(self, sys_exit, time_sleep): mock_event = Mock() mock_event.type = pygame_mock.QUIT = 2 pygame_mock.event.get.return_value = [mock_event] result = self.sut.read_next_action() self.assertEqual(Action.get_default(), result) pygame_mock.display.quit.assert_called() pygame_mock.quit.assert_called()
def test_create_action_should_return_action_with_best_connection(self): player1 = Player(1, 0, 0, Direction.down, 1, True, "") player2 = Player(2, 0, 2, Direction.down, 3, True, "") players = [player1, player2] cells = [[Cell([player1]), Cell(), Cell()], [Cell(), Cell([player2]), Cell()], [Cell([player2]), Cell(), Cell()]] game = Game(3, 3, cells, players, 2, True, datetime.now()) sut = PathfindingAI(player1, 2, 10) result = Value('i') sut.create_next_action(game, result) self.assertEqual(Action.turn_left, Action.get_by_index(result.value))
def create_next_action(self, game: Game, return_value: Value): """See base class.""" self._turn_ctr += 1 surviving_actions = self.create_all_next_surviving_actions(game) if surviving_actions is not None and len(surviving_actions) > 0: return_value.value = choice(surviving_actions).get_index() return_value.value = self.find_actions_by_best_path_connection( surviving_actions, game)[0][0].get_index() else: surviving_pathfinding_actions = self.find_actions_by_best_path_connection( self.find_surviving_actions(GameService(game), 1), game) return_value.value = surviving_pathfinding_actions[0][0].get_index() \ if surviving_pathfinding_actions is not None and len(surviving_pathfinding_actions) > 0 \ else Action.get_default().get_index()
def __choose_action(self, game: Game, time_zone: datetime.tzinfo) -> Action: return_value = multiprocessing.Value('i') self.__default_ai.create_next_action(game, return_value) own_time = datetime.now(time_zone) seconds_for_calculation = (game.deadline - own_time).seconds process = multiprocessing.Process(target=Controller.call_ai, args=( self.__ai, game, return_value, )) process.start() process.join(seconds_for_calculation - 1) if process.is_alive(): process.terminate() return Action.get_by_index(return_value.value)
def read_next_action(self) -> Action: # noqa: C901 """See base class.""" while True: for event in self.__pygame.event.get(): if event.type == self.__pygame.QUIT: # Allows to close the pygame-window self.end() return Action.get_default() elif event.type == self.__pygame.KEYDOWN: pressed_key = self.__pygame.key.get_pressed() self.__next_action = False if pressed_key[self.__pygame.K_UP]: return Action.speed_up elif pressed_key[self.__pygame.K_DOWN]: return Action.slow_down elif pressed_key[self.__pygame.K_RIGHT]: return Action.turn_right elif pressed_key[self.__pygame.K_LEFT]: return Action.turn_left elif pressed_key[self.__pygame.K_SPACE]: return Action.change_nothing elif event.type == self.__pygame.KEYUP: self.__next_action = True
def __get_actions(root: bool, first_actions: List[Action], randomize: bool) -> List[Action]: if root and first_actions is not None and len(first_actions) >= 1: return first_actions return Action.get_actions(randomize)
def create_next_action(self, game: Game, return_value: Value): """See base class.""" self._turn_ctr += 1 action = Action.get_random_action() return_value.value = action.get_index()
def create_next_action(self, game: Game, return_value: Value): """See base class.""" self._turn_ctr += 1 actions = self.create_next_actions_ranked(game) action = actions[0][0] if actions is not None and len(actions) > 0 else Action.get_random_action() return_value.value = action.get_index()