Example #1
0
    def test_is_closed_tail(self):
        # fmt: off
        gm = create_map_with([[1, E, 1], [E, E, E], [1, E, E]])
        # fmt: on

        # player without tail
        player = PlayerState(id=1,
                             name="1",
                             game_map=gm,
                             position=Position(1, 1))
        self.assertEqual(player.tail, [Position(1, 1)])
        self.assertFalse(GameState.is_closed_tail(player, gm))

        # player with tail
        player.tail = [Position(1, 1), Position(2, 1)]
        self.assertFalse(GameState.is_closed_tail(player, gm))

        player.tail = [Position(1, 1), Position(2, 1), Position(3, 1)]
        player.direction = Direction(Direction.UP)
        self.assertTrue(GameState.is_closed_tail(player, gm))

        player.tail = [Position(1, 1), Position(2, 1), Position(2, 2)]
        self.assertFalse(GameState.is_closed_tail(player, gm))

        player.tail = [
            Position(1, 1),
            Position(2, 1),
            Position(2, 2),
            Position(1, 2),
            Position(1, 1)
        ]
        self.assertTrue(GameState.is_closed_tail(player, gm))
Example #2
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 #3
0
 def test_fill_planet_to_planet_2(self):
     # fmt: off
     game_map = create_map_with([[E, E, E, P1, 1], [E, E, E, 1, P1],
                                 [E, E, E, E, E], [E, E, E, E, E],
                                 [E, E, E, E, E]])
     # fmt: on
     ps = PlayerState(id=1,
                      name="dummy",
                      game_map=game_map,
                      position=Position(4, 1))
     gs = GameState(game_map, [ps])
     ps.tail = [
         Position(4, 1),
         Position(3, 1),
         Position(2, 1),
         Position(2, 2),
         Position(2, 3),
         Position(2, 4),
         Position(3, 4),
         Position(4, 4),
         Position(5, 4),
         Position(5, 3),
         Position(5, 2),
     ]
     self.assertEqual(len(gs.fill(ps)), 12)
     # fmt: off
     self.assertEqual(
         game_map,
         create_map_with([[E, 1, 1, P1, 1], [E, 1, 1, 1,
                                             P1], [E, 1, 1, 1, 1],
                          [E, 1, 1, 1, 1], [E, E, E, E, E]]))
     # fmt: on
     self.assertEqual(ps.score, 12)
Example #4
0
 def test_fill_island(self):
     # fmt: off
     game_map = create_map_with([[1, 1, E, 1], [1, 1, E, 1], [E, E, E, E],
                                 [E, E, E, E]])
     # fmt: on
     ps = PlayerState(id=1,
                      name="dummy",
                      game_map=game_map,
                      position=Position(2, 2))
     gs = GameState(game_map, [ps])
     ps.tail = [
         ps.spawn_position,
         Position(2, 3),
         Position(3, 3),
         Position(4, 3),
         Position(4, 2)
     ]
     self.assertEqual(len(gs.fill(ps)), 3)
     # fmt: off
     self.assertEqual(
         game_map,
         create_map_with([[1, 1, E, 1], [1, 1, E, 1], [E, 1, 1, 1],
                          [E, E, E, E]]))
     # fmt: on
     self.assertEqual(ps.score, 3)
Example #5
0
 def test_fill_kill_other_player_tail(self):
     # fmt: off
     game_map = create_map_with([[E, E, E, E], [E, E, E, E], [0, 0, 0, 0],
                                 [E, 1, E, E]])
     # fmt: on
     p0 = PlayerState(id=0,
                      name="dummy",
                      game_map=game_map,
                      position=Position(1, 3))
     p1 = PlayerState(id=1,
                      name="dummy",
                      game_map=game_map,
                      position=Position(2, 4))
     gs = GameState(game_map, [p0, p1])
     p0.tail = [
         p0.spawn_position.copy(),
         Position(1, 2),
         Position(1, 1),
         Position(2, 1),
         Position(3, 1),
         Position(4, 1),
         Position(4, 2),
     ]
     p0.position = p0.tail[-1].copy()
     p0.direction = Direction(Direction.DOWN)
     p1.tail = [
         p1.spawn_position.copy(),
         Position(2, 3),
         Position(2, 2),
         Position(3, 2),
         Position(3, 3),
         Position(3, 4),
     ]
     p1.position = p1.tail[-1].copy()
     p1.direction = Direction(Direction.DOWN)
     # print(game_state_to_dict(0, 0, gs, 999)['game']['pretty_map'])
     gs.apply_action(0, p0, Action.FORWARD)
     # print(game_state_to_dict(0, 0, gs, 999)['game']['pretty_map'])
     self.assertTrue(p1.killed)
Example #6
0
    def test_conquer_some_player_tile(self):
        # fmt: off
        gm = create_map_with([
            [1, 1, E],
            [1, 2, 2],
            [E, 2, 2],
        ])
        # fmt: on
        p1 = PlayerState(id=1, name="p1", game_map=gm, position=Position(1, 1))
        p2 = PlayerState(id=2, name="p2", game_map=gm, position=Position(2, 2))
        gs = GameState(gm, [p1, p2])

        p1.position = Position(2, 2)
        p1.tail = [Position(1, 2), Position(2, 2)]

        p2.position = Position(2, 3)
        p2.tail = [Position(2, 3)]

        p1.direction = Direction(Direction.RIGHT)
        gs.apply_action(0, p1, Action.TURN_LEFT)

        self.assertFalse(p2.killed)
        self.assertFalse(p1.killed)
Example #7
0
    def test_respawn_on_someone_tail(self):
        gm = GameMap(5)
        p1 = PlayerState(id=1, name="p1", game_map=gm, position=Position(1, 1))
        p2 = PlayerState(id=2, name="p2", game_map=gm, position=Position(1, 2))
        gs = GameState(gm, [p1, p2])

        # p1: spawn position = 1,1, current position=3,1, tail [2,1 and 3,1]
        # p2: spawn position = 1,2, current position=2,2, tail [1,1, 1,2 and 2,2]
        p1.position = Position(3, 1)
        p1.tail = [Position(2, 1), Position(3, 1)]
        gm.set_tile(Position(1, 1), GameMap.EMPTY, 2)
        p2.position = Position(2, 2)
        p2.tail = [Position(1, 1), Position(1, 2), Position(2, 2)]
        p2.direction = Direction(Direction.UP)
        gs.apply_action(0, p2, Action.FORWARD)

        self.assertTrue(p1.killed)
        self.assertFalse(p2.killed)

        # respawn
        gs.respawn_player(p1)
        self.assertFalse(p1.killed)
        self.assertTrue(p2.killed)
Example #8
0
    def test_kill_player(self):
        # fmt: off
        gm = create_map_with([[E, E, E], [E, E, E], [1, 1, 1]])
        # fmt: on
        p1 = PlayerState(id=1, name="1", game_map=gm, position=Position(1, 1))
        gs = GameState(gm, [p1])

        p1.tail = [Position(1, 1), Position(1, 2)]
        p1.position = p1.tail[-1]
        gs.kill_player(p1)

        self.assertEqual(p1.tail, [p1.spawn_position])
        self.assertEqual(p1.position, p1.spawn_position)
        self.assertTrue(p1.killed)
        self.assertEqual(0, p1.stats.stats[PlayerStats.CONQUERED])
Example #9
0
    def test_relocate_item(self):
        # fmt: off
        gm = create_map_with([[1, E, E], [E, W, E], [E, E, E]])
        # fmt: on
        ps = PlayerState(id=1,
                         name="dummy",
                         game_map=gm,
                         position=Position(1, 1))
        gs = GameState(gm, [ps])
        ps.position = Position(3, 1)
        ps.tail = [Position(2, 1), Position(3, 1)]

        asteroids_pos = Position(2, 2)
        self.assertTrue(gm.is_asteroids(asteroids_pos))

        planet_pos = Position(3, 3)
        gm.set_tile(planet_pos, GameMap.PLANET)
        self.assertTrue(gm.is_planet(planet_pos))

        black_hole_pos = Position(1, 2)
        gm.set_tile(black_hole_pos, GameMap.BLACK_HOLE)
        self.assertTrue(gm.is_black_hole(black_hole_pos))

        blitzium_pos = Position(1, 3)
        gm.set_tile(blitzium_pos, GameMap.BLITZIUM)
        self.assertTrue(gm.is_blitzium(blitzium_pos))

        # Cannot relocate asteroids and planets
        with self.assertRaises(Exception):
            gs.relocate_item(asteroids_pos)
        with self.assertRaises(Exception):
            gs.relocate_item(planet_pos)

        # relocate black holes and blitziums
        for i in range(30):
            new_black_hole_pos = gs.relocate_item(black_hole_pos)
            self.assertTrue(new_black_hole_pos not in list(
                {asteroids_pos, planet_pos, black_hole_pos, blitzium_pos}) +
                            ps.tail)
            black_hole_pos = new_black_hole_pos

        new_blitzium_pos = gs.relocate_item(blitzium_pos)
        self.assertTrue(new_blitzium_pos != blitzium_pos)
Example #10
0
 def test_fill_with_asteroids(self):
     # fmt: off
     game_map = create_map_with([[E, E, E, E, E], [E, 1, E, 1, E],
                                 [E, 1, W, 1, E], [E, 1, 1, 1, E],
                                 [E, E, E, E, E]])
     # fmt: on
     ps = PlayerState(id=1,
                      name="dummy",
                      game_map=game_map,
                      position=Position(2, 2))
     ps.tail = [ps.spawn_position, Position(3, 2), Position(4, 2)]
     gs = GameState(game_map, [ps])
     self.assertEqual(len(gs.fill(ps)), 1)
     # fmt: off
     self.assertEqual(
         game_map,
         create_map_with([[E, E, E, E, E], [E, 1, 1, 1, E], [E, 1, W, 1, E],
                          [E, 1, 1, 1, E], [E, E, E, E, E]]))
     # fmt: on
     self.assertEqual(ps.score, 1)
Example #11
0
def dict_to_player_state(data: Dict, game_map: GameMap) -> PlayerState:
    position = dict_to_position(data["position"])
    ps = PlayerState(id=data["id"],
                     name=data["name"],
                     game_map=game_map,
                     position=position,
                     init=False)
    ps.active = data["active"]
    ps.killed = data["killed"]
    ps.position = position
    ps.spawn_position = dict_to_position(data["spawn_position"])
    ps.direction = Direction(data["direction"])
    ps.spawn_direction = Direction(data["spawn_direction"])
    ps.tail = [dict_to_position(p) for p in data["tail"]]
    ps.score = data["score"]
    ps.stats = dict_to_player_stats(data["stats"])

    for hist in data["history"]:
        dt = datetime.fromisoformat(hist["timestamp"])
        ps.history.append(
            HistoryItem(game_tick=hist["tick"], message=hist["message"],
                        ts=dt))

    return ps
Example #12
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
Example #13
0
    def test_game_state_to_json_player(self):
        gm = create_map_with([[E, E, E], [E, E, E], [E, E, E]])
        gm.set_tile(Position(1, 3), GameMap.BLACK_HOLE)
        gm.set_tile(Position(2, 3), GameMap.BLITZIUM)
        gm.set_tile(Position(3, 3), GameMap.PLANET)
        gm.set_tile(Position(3, 2), GameMap.PLANET, 0)
        p0 = PlayerState(0,
                         "p0",
                         gm,
                         Position(1, 1),
                         direction=Direction(Direction.UP))
        p0.killed = True
        p0.position = Position(3, 1)
        p0.direction = Direction(Direction.RIGHT)
        p0.tail = [p0.spawn_position, Position(2, 1), p0.position]
        p0.score = 123.4
        p0.stats.kill_player("p1")
        p0.stats.killed_by_player("p2")
        p0.stats.add_stat(PlayerStats.SUICIDES)
        p0.history.append(
            HistoryItem(11, "message-11",
                        datetime.datetime(1900, 1, 1, 13, 14, 15, 555)))
        p0.history.append(
            HistoryItem(10, "message-10",
                        datetime.datetime(1900, 1, 1, 13, 14, 15, 444)))
        p1 = PlayerState(1,
                         "p1",
                         gm,
                         position=Position(1, 2),
                         direction=Direction(Direction.DOWN))
        p1.active = False
        p1.direction = Direction(Direction.LEFT)
        p2 = PlayerState(2,
                         "p2",
                         gm,
                         Position(2, 2),
                         direction=Direction(Direction.RIGHT))
        p2.direction = Direction(Direction.LEFT)
        gs = GameState(gm, [p0, p1, p2])
        self.maxDiff = 4000
        expected = {
            "type":
            "tick",
            "game": {
                "map": [
                    ["W", "W", "W", "W", "W"],
                    ["W", "C-0", " ", " ", "W"],
                    ["W", "C-1", "C-2", "%-0", "W"],
                    ["W", "!", "$", "%", "W"],
                    ["W", "W", "W", "W", "W"],
                ],
                "player_id":
                1,
                # fmt: off
                "pretty_map":
                '[[W     W     W     W     W    ]\n'
                ' [W     C0T0  T0    P0    W    ]\n'
                ' [W     C1P1  C2P2  %0    W    ]\n'
                ' [W     !     $     %     W    ]\n'
                ' [W     W     W     W     W    ]]\n'
                'DISCLAMER: this map does not have the same content as '
                'the json game map. Symbols are combined to help you '
                'visualize every spot on this turn. Hyphen are also '
                'removed. See the documentation for the json map '
                'symbol signification.\n'
                'Symbols added or modified on this map for '
                'visualization purpose are:\n'
                'Px: Position of player x - Cx: Conquered by player x '
                '- Tx: Tail of player x',
                # fmt: on
                "tick":
                123,
                "ticks_left":
                500,
            },
            "players": [
                {
                    "active":
                    True,
                    "killed":
                    True,
                    "position": {
                        "x": 3,
                        "y": 1
                    },
                    "spawn_position": {
                        "x": 1,
                        "y": 1
                    },
                    "direction":
                    "RIGHT",
                    "spawn_direction":
                    "UP",
                    "tail": [{
                        "x": 1,
                        "y": 1
                    }, {
                        "x": 2,
                        "y": 1
                    }, {
                        "x": 3,
                        "y": 1
                    }],
                    "id":
                    0,
                    "name":
                    "p0",
                    "score":
                    123.4,
                    "stats": {
                        PlayerStats.CONQUERED: 1,
                        PlayerStats.KILLS: 1,
                        PlayerStats.KILLED: 1,
                        PlayerStats.SUICIDES: 1,
                        PlayerStats.NEMESIS: "p2",
                        "players_killed": {
                            "p1": 1
                        },
                        "killed_by_players": {
                            "p2": 1
                        },
                    },
                    "history": [
                        {
                            "timestamp": "1900-01-01T13:14:15.000555",
                            "tick": 11,
                            "message": "message-11"
                        },
                        {
                            "timestamp": "1900-01-01T13:14:15.000444",
                            "tick": 10,
                            "message": "message-10"
                        },
                    ],
                },
                {
                    "active": False,
                    "killed": False,
                    "position": {
                        "x": 1,
                        "y": 2
                    },
                    "spawn_position": {
                        "x": 1,
                        "y": 2
                    },
                    "direction": "LEFT",
                    "spawn_direction": "DOWN",
                    "tail": [{
                        "x": 1,
                        "y": 2
                    }],
                    "id": 1,
                    "name": "p1",
                    "score": 0,
                    "stats": {
                        PlayerStats.CONQUERED: 1,
                        "players_killed": {},
                        "killed_by_players": {}
                    },
                    "history": [],
                },
                {
                    "active": True,
                    "killed": False,
                    "position": {
                        "x": 2,
                        "y": 2
                    },
                    "spawn_position": {
                        "x": 2,
                        "y": 2
                    },
                    "direction": "LEFT",
                    "spawn_direction": "RIGHT",
                    "tail": [{
                        "x": 2,
                        "y": 2
                    }],
                    "id": 2,
                    "name": "p2",
                    "score": 0,
                    "stats": {
                        PlayerStats.CONQUERED: 1,
                        "players_killed": {},
                        "killed_by_players": {}
                    },
                    "history": [],
                },
            ],
        }

        # players have an id
        message = game_state_to_json(game_tick=123,
                                     player_id=1,
                                     game_state=gs,
                                     ticks_left=500)
        # print(message)
        data = json.loads(message)

        self.assertEqual(expected, data)