Example #1
0
    def move_player(self, player: PlayerState, action: Action) -> None:
        prev_position = player.position.copy()
        prev_direction = player.direction.copy()

        player.direction.change_direction(action)
        player.position.move(player.direction)

        self.logger.info(
            f"Player '{player.name_str()}' moving to position: {player.position}."
        )
        player.add_history(
            self.game_tick,
            f"Moving {action.value} from {prev_position}-{prev_direction.name()} to {player.position}-{player.direction.name()}.",
        )

        # Append tail if:
        # - currently on an empty (or other player) tile
        # - walking on a captured tile and have non captured tiles in the tail
        if not self.game_map.is_conquered_by(
                player.position,
                player.id) or not self.game_map.is_conquered_by(
                    player.tail[-1], player.id):
            self.logger.debug(
                f"Player '{player.name_str()}' appending position to tail.")
            player.tail.append(player.position.copy())
        else:
            player.tail = [player.position.copy()]
Example #2
0
    def apply_action(self, game_tick: int, player: PlayerState,
                     action: Optional[Action]) -> None:
        self.game_tick = game_tick
        if player.killed:
            msg = f"Player '{player.name_str()}' was killed in the last turn, skip current action."
            self.logger.info(msg)
            player.add_history(game_tick, msg)

            # Respawn a player after killed
            self.respawn_player(player)
        else:
            # Change direction and move
            self.move_player(player, action)

            # If you move outside the map or an asteroids; you die.
            if self.game_map.is_out_of_bound(
                    player.position) or self.game_map.is_asteroids(
                        player.position):
                self.logger.info(
                    f"Player '{player.name_str()}' stepped on an asteroids or outside the map."
                )
                player.stats.add_stat(PlayerStats.SUICIDES)
                player.add_history(
                    game_tick, f"Committed suicide by going out of bound.")
                self.kill_player(player)
            elif not self.stepped_on_a_black_hole(player.position, player):
                # Are we about to kill a player ?
                poor_player = self.will_kill_player(player)
                if poor_player != None:
                    self.kill_player_by(poor_player, player)

                if not player.killed:
                    # check if player captured a blitzium
                    self.check_if_captured_a_blitzium(player.position, player)

                    # Check if player conquered a territory
                    if GameState.is_closed_tail(player, self.game_map):
                        tilesOwnedBeforeConquere = dict()
                        for p in self.players:
                            tilesOwnedBeforeConquere[
                                p.id] = self.game_map.count_tiles_owned_by(
                                    p.id)

                        new_conquers: Set[Position] = self.fill(player)

                        for p in self.players:
                            # check if we conquered current position of a player
                            # if p.id != player.id and (p.position in new_conquers or p.tail[0] in new_conquers):
                            if p.id != player.id and len(
                                    set(p.tail) & new_conquers) > 0:
                                self.kill_player_by(p, player)
                            # check if we conquered the last owned tiles of a player
                            if (p.id != player.id
                                    and tilesOwnedBeforeConquere[p.id] != 0
                                    and self.game_map.count_tiles_owned_by(
                                        p.id) == 0):
                                self.kill_player_by(p, player)
Example #3
0
 def check_if_captured_a_blitzium(self, pos: Position,
                                  player: PlayerState) -> None:
     if self.game_map.is_blitzium(pos):
         self.logger.info(f"Player '{player.name_str()}' found a blitzium.")
         player.stats.add_stat(PlayerStats.BLITZIUMS)
         player.add_history(self.game_tick, "Found a blitzium.")
         player.score += GameState.SCORE_CAPTURED_BLITZIUM
         if GameState.relocate_blitzium:
             self.relocate_item(pos)
         else:
             self.game_map.clear_tile(pos)
Example #4
0
    def respawn_player(self, player: PlayerState) -> None:
        # discard current action
        msg = f"Respawning player '{player.name_str()}'."
        self.logger.info(msg)
        player.add_history(self.game_tick, msg)
        player.killed = False
        player.reset_position()

        # check if it kill another player
        poor_player = self.will_kill_player(player)
        if poor_player is not None:
            self.kill_player_by(poor_player, player)
Example #5
0
 def stepped_on_a_black_hole(self, pos: Position,
                             player: PlayerState) -> bool:
     if self.game_map.is_black_hole(pos):
         self.logger.info(
             f"Player '{player.name_str()}' stepped on a black hole.")
         player.stats.add_stat(PlayerStats.SUICIDES)
         player.add_history(
             self.game_tick,
             f"Committed suicide by stepping on a black hole.")
         self.kill_player(player)
         if GameState.relocate_black_hole:
             self.relocate_item(pos)
         # permanent black_hole else: self.game_map.clear_tile(pos)
     return player.killed
Example #6
0
    def kill_player_by(self, poor_player: PlayerState,
                       player: PlayerState) -> None:
        if poor_player.id != player.id:
            self.logger.info(
                f"Player '{player.name_str()}' will kill player '{poor_player.name_str()}'."
            )
            player.score += GameState.SCORE_KILL_PLAYER
            player.stats.kill_player(poor_player.name)
            poor_player.stats.killed_by_player(player.name)
            player.add_history(self.game_tick,
                               f"Killed player '{poor_player.name}'")
        else:
            self.logger.info(
                f"Player '{player.name_str()}' committed suicide!.")
            player.stats.add_stat(PlayerStats.SUICIDES)
            player.add_history(
                self.game_tick,
                f"Committed suicide by walking on player tail.")

        self.kill_player(poor_player)
    def test_add_history(self):
        game_map = GameMap(5)
        pos = Position(1, 1)
        ps = PlayerState(id=1, name="dummy", game_map=game_map, position=pos)

        for tick in range(10):
            # add 1 or 2 messages per tick
            for j in range(1 + tick % 2):
                ps.add_history(tick, f"message-{tick}-{j}")

        self.assertEqual(15, len(ps.history))

        # adding one message will pop one oldest
        ps.add_history(11, "message-11")
        self.assertEqual(15, len(ps.history))
        self.assertEqual("message-11", ps.history[0].message)
        self.assertEqual("message-1-1", ps.history[-2].message)
        self.assertEqual("message-1-0", ps.history[-1].message)

        # adding a second message will pop two oldests
        ps.add_history(12, "message-12")
        self.assertEqual(14, len(ps.history))
        self.assertEqual("message-12", ps.history[0].message)
        self.assertEqual("message-2-0", ps.history[-1].message)
Example #8
0
    def fill(self, player: PlayerState) -> Set[Position]:
        id = player.id

        # tail have the starting and ending tiles (last tile before going in the empty zone and first captured tile)
        assert len(player.tail) >= 3
        assert self.game_map.is_conquered_by(player.tail[0], id)
        assert self.game_map.is_conquered_by(player.tail[-1], id)

        # new captured tiles minus start and end tiles (already captured)
        new_conquers = set(player.tail[1:-1])

        # check if we have a closed loop to fill: find shortest path from start to end of tail while walking only on conquered tiles
        closed_loop, shortest_path_to_close_tail = self.game_map.find_path(
            player.tail[0], player.tail[-1], id)
        if closed_loop:
            # Conquer the closed shape made by the tail
            map_size = self.game_map.size
            flooded_tiles = [[1] * map_size for _ in range(map_size)]

            for p in player.tail:
                flooded_tiles[p.y][p.x] = 0
            for p in shortest_path_to_close_tail:
                flooded_tiles[p.y][p.x] = 0

            # flooding work by changing the value to 0 for every reachable tiles from the top left of the map (skipping bombs,
            # walls and other players)  The remainder are the tiles that will be flooded. Using the closest path from start
            # to end of the tail make sure that we are filling only the smallest zone  when wrap around and also make sure we
            # do not re-kill any players walking inside our captured zone.
            flood(flooded_tiles, target=1, replacement=0)

            for y in range(map_size):
                for x in range(map_size):
                    if flooded_tiles[y][x] == 1:
                        p = Position(x, y)

                        # skip already conquered tiles or walls (asteroids)
                        if not self.game_map.is_conquered_by(
                                p, id) and not self.game_map.is_asteroids(p):
                            if self.game_map.is_black_hole(p):
                                # if we happen to surround a black hole, we die instantly
                                self.stepped_on_a_black_hole(p, player)
                                return set()

            for y in range(map_size):
                for x in range(map_size):
                    if flooded_tiles[y][x] == 1:
                        p = Position(x, y)

                        # skip already conquered tiles or walls (asteroids)
                        if not self.game_map.is_conquered_by(
                                p, id) and not self.game_map.is_asteroids(p):
                            if self.game_map.is_blitzium(p):
                                # surrounding a coin (blitzium) will give us more points.
                                self.check_if_captured_a_blitzium(p, player)

                            # add tile to new conquered tiles for later scoring
                            new_conquers.add(p)
                            self.game_map.conquer_tile(p, id)

        # capture the tail (no-op to recapture start and end tiles)
        for t in player.tail:
            self.game_map.conquer_tile(t, id)
        player.tail = [player.position.copy()]

        # adjust stats
        nb_conquers = len(new_conquers)
        self.logger.info(
            f"Player '{player.name_str()}' is capturing {nb_conquers} new tiles.."
        )
        player.stats.set_stat(
            PlayerStats.CONQUERED,
            player.stats.stats[PlayerStats.CONQUERED] + nb_conquers)
        player.score += GameState.SCORE_NEW_CONQUERED * nb_conquers
        player.add_history(self.game_tick,
                           f"Conquered {nb_conquers} new tiles.")
        return new_conquers