def test_find_path_empty_tiles(self): map = create_map_with([ # fmt: off [1, E, E, E], [W, W, E, E], [W, E, E, W], [2, E, W, E] # fmt: on ]) found, path = map.find_path(start=Position(1, 1), goal=Position(4, 1)) self.assertTrue(found) self.assertEqual(len(path), 4) self.assertEqual( path, [Position(1, 1), Position(2, 1), Position(3, 1), Position(4, 1)]) found, path = map.find_path(start=Position(1, 1), goal=Position(1, 4)) self.assertTrue(found) self.assertEqual(len(path), 8) found, path = map.find_path(start=Position(1, 1), goal=Position(4, 4)) self.assertFalse(found)
def test_get_set_tile(self): """ Test that you can set a tile and get it back """ position = Position(x=2, y=1) old_is_empty = self.my_map.empty_tiles self.assertTrue(self.my_map.is_empty(position)) self.assertEqual(self.my_map.get_tile(position), GameMap.empty_tile) # conquer empty tile is the same as setting an owner on it self.my_map.set_tile(position, GameMap.EMPTY, 1) self.assertFalse(self.my_map.is_empty(position)) self.assertTrue(self.my_map.is_conquered_by(position, 1)) self.assertEqual(self.my_map.get_owner(position), 1) self.assertEqual(self.my_map.get_tile(position), (GameMap.EMPTY, 1)) self.assertEqual(self.my_map.empty_tiles, old_is_empty - 1) # clear tile remove owner self.my_map.clear_tile(position) self.assertTrue(self.my_map.is_empty(position)) self.assertEqual(self.my_map.get_tile(position)[0], GameMap.EMPTY) self.assertEqual(self.my_map.empty_tiles, old_is_empty) with self.assertRaises(OutOfBoundExeption): self.my_map.get_tile( Position(self.map_size + 10, self.map_size + 10)) with self.assertRaises(OutOfBoundExeption): self.my_map.set_tile( Position(self.map_size + 10, self.map_size + 10), GameMap.EMPTY, 1)
def test_init_with_empty_map(self): """ Test that a new map will be empty and the correct size """ self.assertEqual(self.my_map.size, self.map_size) self.assertEqual(len(self.my_map.tiles), self.my_map.size) [ self.assertEqual(len(self.my_map.tiles[y]), self.my_map.size) for y in range(self.my_map.size) ] # there is an asteroids around the map self.assertEqual(self.my_map.empty_tiles, (self.map_size - 2)**2) for y in range(self.map_size): for x in range(self.map_size): pos = Position(x=x, y=y) self.assertFalse(self.my_map.is_out_of_bound(pos)) if x == 0 or x == self.map_size - 1 or y == 0 or y == self.map_size - 1: # check asteroids self.assertFalse(self.my_map.is_empty(pos)) self.assertEqual(self.my_map.tiles[y][x], (GameMap.ASTEROIDS, None)) self.assertTrue(self.my_map.is_asteroids(pos)) self.assertEqual(self.my_map.get_tile(Position(x=x, y=y)), (GameMap.ASTEROIDS, None)) else: # must be empty self.assertTrue(self.my_map.is_empty(pos)) self.assertFalse(self.my_map.is_asteroids(pos)) self.assertEqual(self.my_map.get_tile(Position(x=x, y=y)), (GameMap.EMPTY, None))
def test_eq(self): """ Tests that the __eq__ work """ self.assertEqual(Position(3, 5), Position(3, 5)) self.assertNotEqual(Position(4, 5), Position(5, 5))
def test_with_direction(self): data = """ WWWWWW W1 3W WD DW WD DW W4 2W WWWWWW\r\n """ gc = GameConfig.from_str(data) self.assertEqual( gc.spawn_positions, [Position(1, 1), Position(4, 4), Position(4, 1), Position(1, 4)]) self.assertEqual( gc.spawn_directions, [ Direction(Direction.DOWN), Direction(Direction.UP), Direction(Direction.DOWN), Direction(Direction.UP) ], )
def test_str(self): """ Tests that __str__ work """ self.assertEqual(Position(3, 5).__str__(), "[x: 3, y: 5]") self.assertEqual(Position(3, 5).__str__(), "[x: 3, y: 5]")
def get_random_empty_position(self) -> Position: """ Find a random starting position for a player """ map_size = self.size pos = Position(x=random.randint(1, map_size - 1), y=random.randint(1, map_size - 1)) while self.empty_tiles > 0 and not self.is_empty(pos): pos = Position(x=random.randint(1, map_size - 1), y=random.randint(1, map_size - 1)) return pos
def test_copy_create_new_position(self): """ Tests that copy will create a new position """ oldPosition = Position(3, 5) newPosition = oldPosition.copy() self.assertIsNot(oldPosition, newPosition) self.assertEqual(oldPosition, newPosition)
def test_add(self): """ Tests that the move method will move the position in the right direction """ oldPosition = Position(3, 4) newPosition = oldPosition + (0, 1) self.assertIsNot(oldPosition, newPosition) self.assertEqual(Position(3, 5), newPosition)
def test_is_out_of_bound(self): """ Test that a position is out of bound for the current map """ self.assertTrue( self.my_map.is_out_of_bound(Position(-self.map_size, 5))) self.assertTrue(self.my_map.is_out_of_bound(Position(self.map_size, 5))) self.assertTrue( self.my_map.is_out_of_bound(Position(5, -self.map_size))) self.assertTrue(self.my_map.is_out_of_bound(Position(5, self.map_size))) self.assertTrue( self.my_map.is_out_of_bound(Position(self.map_size, self.map_size))) self.assertTrue( self.my_map.is_out_of_bound( Position(-self.map_size, -self.map_size))) self.assertFalse(self.my_map.is_out_of_bound(Position(0, 0))) self.assertFalse(self.my_map.is_out_of_bound(Position(5, 5))) self.assertFalse( self.my_map.is_out_of_bound( Position(self.map_size - 1, self.map_size - 1)))
def test_from_str(self): data = """ WWWWW W!%$W W1 3W W4W2W WWWWW\r\n """ gc = GameConfig.from_str(data) self.assertEqual(gc.game_map.size, 5) self.assertEqual(gc.game_map.get_tile(Position(1, 1)), (GameMap.BLACK_HOLE, None)) self.assertEqual(gc.game_map.get_tile(Position(2, 1)), (GameMap.PLANET, None)) self.assertEqual(gc.game_map.get_tile(Position(3, 1)), (GameMap.BLITZIUM, None)) self.assertEqual(gc.game_map.get_tile(Position(2, 2)), (GameMap.EMPTY, None)) self.assertEqual(gc.game_map.get_tile(Position(2, 3)), (GameMap.ASTEROIDS, None)) self.assertEqual( gc.spawn_positions, [Position(1, 2), Position(3, 3), Position(3, 2), Position(1, 3)])
def test_dir_to_center(self): map_size = 5 self.assertEqual(dir_to_center(Position(0, 0), map_size), Direction.RIGHT) self.assertEqual(dir_to_center(Position(0, 1), map_size), Direction.DOWN) self.assertEqual(dir_to_center(Position(map_size - 1, 0), map_size), Direction.LEFT) self.assertEqual( dir_to_center(Position(map_size - 1, map_size - 2), map_size), Direction.UP)
def test_cannot_overwrite_asteroids(self): # Ok to write an asteroids on an asteroids self.my_map.set_tile(Position(x=0, y=0), state=GameMap.ASTEROIDS) # Invalid to overwrite an asteroids with something else with self.assertRaises(InvalidStateException): self.my_map.set_tile(Position(x=0, y=0), state=GameMap.EMPTY, player_id=0) with self.assertRaises(InvalidStateException): self.my_map.set_tile(Position(x=0, y=0), state=GameMap.EMPTY)
def clear_tile_owned_by(self, player_id: int) -> None: """ Clear all tiles owned by the player_id; sets them to empty Parameters ---------- player_id : int The player id """ for y in range(len(self.tiles)): for x in range(len(self.tiles)): if self.get_owner(Position(x=x, y=y)) == player_id: self.clear_tile(position=Position(x=x, y=y))
def flood(flood: List[List[int]], target: int, replacement: int) -> None: x_size = len(flood[0]) y_size = len(flood) queue: SimpleQueue = SimpleQueue() queue.put(Position(0, 0)) while not queue.empty(): cur = queue.get() for _, delta in directions: sibblingX = cur.x + delta[0] sibblingY = cur.y + delta[1] if sibblingX >= 0 and sibblingX < x_size and sibblingY >= 0 and sibblingY < y_size: if flood[sibblingY][sibblingX] == target: flood[sibblingY][sibblingX] = replacement queue.put(Position(sibblingX, sibblingY))
def dict_to_game_map(data: List[List[str]]) -> GameMap: map_size = len(data) game_map = GameMap(map_size) for y in range(1, map_size - 1): for x in range(1, map_size - 1): splitted = data[y][x].split("-") tile = splitted[0] owner = None if len(splitted) > 1: owner = int(splitted[1]) pos = Position(x, y) if tile == " ": game_map.set_tile(pos, GameMap.EMPTY, None) elif tile == "W": game_map.set_tile(pos, GameMap.ASTEROIDS, None) elif tile == "!": game_map.set_tile(pos, GameMap.BLACK_HOLE, None) elif tile == "$": game_map.set_tile(pos, GameMap.BLITZIUM, None) elif tile == "%": game_map.set_tile(pos, GameMap.PLANET, owner) elif tile == "C": game_map.set_tile(pos, GameMap.EMPTY, owner) return game_map
def update_players_scores(self) -> None: # tiles count may become out of sync because a player might conquer someone else territory for player in self.players: player.stats.set_stat(PlayerStats.CONQUERED, 0) player.stats.set_stat(PlayerStats.PLANETS, 0) for x in range(0, self.game_map.size): for y in range(0, self.game_map.size): tile_state, player_id = self.game_map.get_tile(Position(x, y)) if player_id is not None: if tile_state == GameMap.PLANET: self.players[ player_id].score += GameState.SCORE_CONQUERED_PLANET self.players[player_id].stats.add_stat( PlayerStats.PLANETS) else: self.players[ player_id].score += GameState.SCORE_CONQUERED self.players[player_id].stats.add_stat( PlayerStats.CONQUERED) for p in self.players: p.score += len(p.tail) * GameState.SCORE_TAIL
def get_empty_tiles(self) -> List[Position]: empty = [] for x in range(1, self.size - 1): for y in range(1, self.size - 1): if self.tiles[y][x] == GameMap.empty_tile: empty.append(Position(x, y)) return empty
def game_state_to_frame(player_id: int, state: GameState) -> np.array: size = state.game_map.size frame = np.zeros((size, size)) for y in range(size): for x in range(size): tile_state, tile_owner = state.game_map.get_tile(Position(x, y)) value = EMPTY if tile_state == GameMap.ASTEROIDS or tile_state == GameMap.BLACK_HOLE: value = ASTEROIDS_BH elif tile_state == GameMap.BLITZIUM: value = BLITZIUM elif tile_state == GameMap.PLANET and tile_owner != player_id: value = PLANETS if tile_owner is not None: if tile_owner == player_id: value = CAPTURED_BY_PLAYER else: value = CAPTURED_BY_OTHERS frame[y, x] = value for p in state.players: if p.id == player_id: payer_state_to_frame(p, frame, head=PLAYER_HEAD, tail=PLAYER_TAIL) else: payer_state_to_frame(p, frame, head=OTHERS_HEAD, tail=OTHERS_TAIL) return frame
def count_tiles_owned_by(self, player_id: int) -> int: conquered = 0 for y in range(len(self.tiles)): for x in range(len(self.tiles)): if self.get_owner(Position(x=x, y=y)) == player_id: conquered += 1 return conquered
def test_init_with_values(self): """ Tests that the position constructor saves to right values """ position = Position(3, 4) self.assertEqual(position.x, 3) self.assertEqual(position.y, 4)
def test_init_with_direction(self): game_map = GameMap(5) pos = Position(1, 1) ps = PlayerState(id=1, name="dummy", game_map=game_map, position=pos, direction=Direction(Direction.DOWN)) self.assertEqual(ps.direction, Direction(Direction.DOWN))
def test_skip_player_turn_if_killed(self): game = get_game() player = DummyPlayer(lambda x, y: (x, Action.TURN_LEFT)) game.register_player(player) player.player_state.killed = True player.player_state.position = Position(0, 0) next_move = asyncio.run(game.request_next_move(player)) self.assertEqual(next_move, None) self.assertEqual(player.player_state.position, player.player_state.spawn_position)
def test_invalid_set_title(self): pos = Position(x=1, y=1) # cannot have player id with self.assertRaises(InvalidStateException): self.my_map.set_tile(pos, state=GameMap.ASTEROIDS, player_id=1) with self.assertRaises(InvalidStateException): self.my_map.set_tile(pos, state=GameMap.BLITZIUM, player_id=1) with self.assertRaises(InvalidStateException): self.my_map.set_tile(pos, state=GameMap.BLACK_HOLE, player_id=1)
def test_init(self): game_map = GameMap(5) pos = Position(1, 1) ps = PlayerState(id=1, name="dummy", game_map=game_map, position=pos) self.assertFalse(ps.killed) self.assertEqual(ps.score, 0) self.assertEqual(ps.spawn_position, pos) self.assertEqual(ps.position, pos) self.assertEqual(ps.tail, [ps.spawn_position]) self.assertEqual(ps.direction, Direction.RIGHT) self.assertTrue(ps.game_map.is_conquered_by(pos, 1))
def create_state(self): game_map = GameMap(21) game_state = GameState(game_map) p0 = game_state.add_player("0") p0.stats.kill_player("p1") p0.stats.killed_by_player("p2") p0.stats.add_stat(PlayerStats.SUICIDES) p0.stats.add_stat(PlayerStats.BLITZIUMS) p0.stats.add_stat(PlayerStats.CONQUERED) p0.tail = [Position(1, 2), Position(2, 3), Position(4, 5)] 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 = game_state.add_player("1") p1.stats.kill_player("p1") p1.stats.killed_by_player("p2") p1.stats.add_stat(PlayerStats.SUICIDES) p1.stats.add_stat(PlayerStats.BLITZIUMS) p1.stats.add_stat(PlayerStats.CONQUERED) p1.tail = [Position(1, 2), Position(2, 3), Position(4, 5)] p1.history.append( HistoryItem(11, "message-11", datetime.datetime(1900, 1, 1, 13, 14, 15, 555))) p1.history.append( HistoryItem(10, "message-10", datetime.datetime(1900, 1, 1, 13, 14, 15, 444))) return game_state
def test_clear_tile_owned_by(self): """ Tests that test_clear_tile_owned_by only clear tiles owned by the right player """ self.my_map.set_tile(Position(5, 5), GameMap.PLANET, player_id=None) self.my_map.conquer_tile(Position(5, 5), player_id=1) self.my_map.conquer_tile(Position(5, 6), player_id=1) self.my_map.conquer_tile(Position(5, 7), player_id=1) self.my_map.conquer_tile(Position(1, 5), player_id=2) self.assertEqual(self.my_map.count_tiles_owned_by(player_id=1), 3) self.my_map.clear_tile_owned_by(player_id=1) self.assertEqual(self.my_map.count_tiles_owned_by(player_id=1), 0) self.assertFalse(self.my_map.is_empty(Position(5, 5))) self.assertEqual(self.my_map.get_tile(Position(5, 5)), (GameMap.PLANET, None)) self.assertTrue(self.my_map.is_empty(Position(7, 5))) self.assertEqual(self.my_map.get_tile(Position(1, 5)), (GameMap.EMPTY, 2)) self.assertEqual(self.my_map.get_owner(Position(1, 5)), 2)
def test_send_winner_socket_closed(self): server = Mock() socket = AsyncMock() socket.send.side_effect = websockets.ConnectionClosed(0, "") viewer = SocketViewer(server, socket) viewer.logger = Mock() gs = GameState(GameMap(3)) ps = PlayerState(1, "p1", gs.game_map, Position(1, 1)) asyncio.run(viewer.send_winner(123, ps)) viewer.logger.warning.assert_called_once() server.game.unregister_viewer.assert_called_once()
def test_is_conquered_by(self): """ Test that you can identify tiles owned by you """ position = Position(1, 1) self.assertTrue(self.my_map.is_empty(position)) self.assertFalse(self.my_map.is_conquered_by(position, player_id=1)) self.my_map.conquer_tile(position, player_id=1) self.assertTrue(self.my_map.is_conquered_by(position, player_id=1)) self.assertFalse(self.my_map.is_conquered_by(position, player_id=0)) self.my_map.set_tile(position, GameMap.PLANET, player_id=None) self.my_map.conquer_tile(position, player_id=1) self.assertTrue(self.my_map.is_conquered_by(position, player_id=1)) self.assertEqual(self.my_map.get_tile(position), (GameMap.PLANET, 1)) with self.assertRaises(OutOfBoundExeption): self.my_map.is_conquered_by(Position(self.map_size + 10, self.map_size + 10), player_id=0)
def from_str(cls, data: str) -> "GameConfig": lines = data.strip().split("\n") map_size = len(lines) game_map = GameMap(map_size) spawn_positions = [] spawn_directions = [] spawn_direction_markers = [] for y, line in enumerate(lines): line = line.strip() if len(line) != map_size: raise Exception(f"Map is not square! (line {y} as length {len(line)})") for x, tile in enumerate(line): pos = Position(x, y) if tile == GameMap.EMPTY: game_map.set_tile(pos, GameMap.EMPTY, None) elif tile == GameMap.ASTEROIDS: game_map.set_tile(pos, GameMap.ASTEROIDS, None) elif tile == GameMap.BLACK_HOLE: game_map.set_tile(pos, GameMap.BLACK_HOLE, None) elif tile == GameMap.BLITZIUM: game_map.set_tile(pos, GameMap.BLITZIUM, None) elif tile == GameMap.PLANET: game_map.set_tile(pos, GameMap.PLANET, None) elif tile == GameMap.DIRECTION: # Directions are meta information not visible on the map game_map.set_tile(pos, GameMap.EMPTY, None) spawn_direction_markers.append(pos) elif tile.isdigit(): player_id = int(tile) spawn_positions.append((player_id, pos)) else: raise Exception(f"Invalid tile '{tile}' as position {pos}.") spawn_positions.sort() if len(spawn_direction_markers) > 0: # for each player we find the closest direction marker # this algorithm won't work if a direction marker is # close to 2 spawn_positions for player_id, spawn_position in spawn_positions: for direction_marker in spawn_direction_markers: if spawn_position.is_next_to(direction_marker): direction = spawn_position.direction_to(direction_marker) spawn_directions.append(direction) return GameConfig(game_map, [p for i, p in spawn_positions], spawn_directions)