From 9083ca76b68e32f6127368ebd723e6b826049d1a Mon Sep 17 00:00:00 2001 From: FW Date: Mon, 6 May 2019 00:34:00 +0200 Subject: [PATCH 1/2] Use isdisjoint in buff checks, find vision blockers in game info --- sc2/bot_ai.py | 2 +- sc2/game_info.py | 36 +++++++++++++++++++++++++++--------- sc2/unit.py | 36 +++++++++++++----------------------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/sc2/bot_ai.py b/sc2/bot_ai.py index b5dedb43d..a9120b9bd 100644 --- a/sc2/bot_ai.py +++ b/sc2/bot_ai.py @@ -783,7 +783,7 @@ def _prepare_first_step(self): """First step extra preparations. Must not be called before _prepare_step.""" if self.townhalls: self._game_info.player_start_location = self.townhalls.first.position - self._game_info.map_ramps = self._game_info._find_ramps() + self._game_info.map_ramps, self._game_info.vision_blockers = self._game_info._find_ramps_and_vision_blockers() def _prepare_step(self, state, proto_game_info): # Set attributes from new state before on_step.""" diff --git a/sc2/game_info.py b/sc2/game_info.py index c23a16ee9..6a3285ba5 100644 --- a/sc2/game_info.py +++ b/sc2/game_info.py @@ -1,6 +1,8 @@ from collections import deque from typing import Any, Deque, Dict, FrozenSet, Generator, List, Optional, Sequence, Set, Tuple, Union +import numpy as np + from .cache import property_immutable_cache, property_mutable_cache from .pixel_map import PixelMap from .player import Player @@ -168,22 +170,38 @@ def __init__(self, proto): self.playable_area = Rect.from_proto(self._proto.start_raw.playable_area) self.map_center = self.playable_area.center self.map_ramps: List[Ramp] = None # Filled later by BotAI._prepare_first_step + self.vision_blockers: Set[Point2] = None # Filled later by BotAI._prepare_first_step self.player_races: Dict[int, "Race"] = { p.player_id: p.race_actual or p.race_requested for p in self._proto.player_info } self.start_locations: List[Point2] = [Point2.from_proto(sl) for sl in self._proto.start_raw.start_locations] self.player_start_location: Point2 = None # Filled later by BotAI._prepare_first_step - def _find_ramps(self) -> List[Ramp]: - """Calculate (self.pathing_grid - self.placement_grid) (for sets) and then find ramps by comparing heights.""" + def _find_ramps_and_vision_blockers(self) -> Tuple[List[Ramp], Set[Point2]]: + """ Calculate points that are pathable but not placeable. + Then devide them into ramp points if not all points around the points are equal height + and into vision blockers if they are. """ + + def equal_height_around(tile): + # mask to slice array 1 around tile + sliced = self.terrain_height.data_numpy[tile[1] - 1 : tile[1] + 2, tile[0] - 1 : tile[0] + 2] + return len(np.unique(sliced)) == 1 + map_area = self.playable_area - rampPoints = ( - Point2((x, y)) - for x in range(map_area.x, map_area.x + map_area.width) - for y in range(map_area.y, map_area.y + map_area.height) - if self.placement_grid[(x, y)] == 0 and self.pathing_grid[(x, y)] == 1 - ) - return [Ramp(group, self) for group in self._find_groups(rampPoints)] + # all points in the playable area that are pathable but not placable + points = [ + Point2((b, a)) + for (a, b), value in np.ndenumerate(self.pathing_grid.data_numpy) + if value == 1 + and map_area.x <= a < map_area.x + map_area.width + and map_area.y <= b < map_area.y + map_area.height + and self.placement_grid[(b, a)] == 0 + ] + # devide points into ramp points and vision blockers + rampPoints = [point for point in points if not equal_height_around(point)] + visionBlockers = set(point for point in points if equal_height_around(point)) + ramps = [Ramp(group, self) for group in self._find_groups(rampPoints)] + return ramps, visionBlockers def _find_groups(self, points: Set[Point2], minimum_points_per_group: int = 8): """ diff --git a/sc2/unit.py b/sc2/unit.py index cc7b5edfa..de4559774 100644 --- a/sc2/unit.py +++ b/sc2/unit.py @@ -404,37 +404,27 @@ def buffs(self) -> Set: @property_immutable_cache def is_carrying_minerals(self) -> bool: """ Checks if a worker or MULE is carrying (gold-)minerals. """ - return any( - buff in {BuffId.CARRYMINERALFIELDMINERALS, BuffId.CARRYHIGHYIELDMINERALFIELDMINERALS} for buff in self.buffs - ) + return not {BuffId.CARRYMINERALFIELDMINERALS, BuffId.CARRYHIGHYIELDMINERALFIELDMINERALS}.isdisjoint(self.buffs) @property_immutable_cache def is_carrying_vespene(self) -> bool: """ Checks if a worker is carrying vespene gas. """ - return any( - buff - in { - BuffId.CARRYHARVESTABLEVESPENEGEYSERGAS, - BuffId.CARRYHARVESTABLEVESPENEGEYSERGASPROTOSS, - BuffId.CARRYHARVESTABLEVESPENEGEYSERGASZERG, - } - for buff in self.buffs - ) + return not { + BuffId.CARRYHARVESTABLEVESPENEGEYSERGAS, + BuffId.CARRYHARVESTABLEVESPENEGEYSERGASPROTOSS, + BuffId.CARRYHARVESTABLEVESPENEGEYSERGASZERG, + }.isdisjoint(self.buffs) @property_immutable_cache def is_carrying_resource(self) -> bool: """ Checks if a worker is carrying a resource. """ - return any( - buff - in { - BuffId.CARRYMINERALFIELDMINERALS, - BuffId.CARRYHIGHYIELDMINERALFIELDMINERALS, - BuffId.CARRYHARVESTABLEVESPENEGEYSERGAS, - BuffId.CARRYHARVESTABLEVESPENEGEYSERGASPROTOSS, - BuffId.CARRYHARVESTABLEVESPENEGEYSERGASZERG, - } - for buff in self.buffs - ) + return not { + BuffId.CARRYMINERALFIELDMINERALS, + BuffId.CARRYHIGHYIELDMINERALFIELDMINERALS, + BuffId.CARRYHARVESTABLEVESPENEGEYSERGAS, + BuffId.CARRYHARVESTABLEVESPENEGEYSERGASPROTOSS, + BuffId.CARRYHARVESTABLEVESPENEGEYSERGASZERG, + }.isdisjoint(self.buffs) @property_immutable_cache def detect_range(self) -> Union[int, float]: From 1dc888e6453ef4662a6c469c6cd42184aa927fec Mon Sep 17 00:00:00 2001 From: FW Date: Mon, 6 May 2019 00:42:23 +0200 Subject: [PATCH 2/2] Fix tests for new ramps and vision blocker function --- test/test_pickled_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_pickled_data.py b/test/test_pickled_data.py index e0e61ce12..7f292e81e 100644 --- a/test/test_pickled_data.py +++ b/test/test_pickled_data.py @@ -109,7 +109,7 @@ def test_bot_ai(self, bot: BotAI): assert bot.townhalls.random.position not in bot.enemy_start_locations assert bot.known_enemy_units == Units([]) assert bot.known_enemy_structures == Units([]) - bot._game_info.map_ramps = bot._game_info._find_ramps() + bot._game_info.map_ramps, bot._game_info.vision_blockers = bot._game_info._find_ramps_and_vision_blockers() assert bot.main_base_ramp # Test if any ramp was found # TODO: Cache all expansion positions for a map and check if it is the same assert len(bot.expansion_locations) >= 12