Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sc2/bot_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
36 changes: 27 additions & 9 deletions sc2/game_info.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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):
"""
Expand Down
36 changes: 13 additions & 23 deletions sc2/unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand Down
2 changes: 1 addition & 1 deletion test/test_pickled_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down