Skip to content

Commit

Permalink
Merge pull request Dentosal#175 from Dentosal/develop
Browse files Browse the repository at this point in the history
Merge master branch from develop branch with performance improvements
  • Loading branch information
BurnySc2 authored Nov 30, 2018
2 parents 695faf2 + 5e11517 commit f00c142
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 37 deletions.
3 changes: 2 additions & 1 deletion sc2/bot_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def expansion_locations(self) -> Dict[Point2, Units]:
]
# order by distance to resources, 7.162 magic distance number (avg resource distance of current ladder maps)
possible_points.sort(
key=lambda p: statistics.mean([abs(p.distance_to(resource) - 7.162) for resource in resources])
key=lambda p: statistics.mean([abs(p.distance_to(resource) - 7.162) for resource in resources if resource in self.state.mineral_field])
)
# choose best fitting point
centers[possible_points[0]] = resources
Expand Down Expand Up @@ -496,6 +496,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()

def _prepare_step(self, state):
"""Set attributes from new state before on_step."""
Expand Down
16 changes: 13 additions & 3 deletions sc2/game_data.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from bisect import bisect_left
from functools import lru_cache, reduce
from typing import List, Dict, Set, Tuple, Any, Optional, Union # mypy type checking

Expand Down Expand Up @@ -70,10 +71,19 @@ def calculate_ability_cost(self, ability) -> "Cost":
return Cost(0, 0)

class AbilityData(object):
@staticmethod
def id_exists(ability_id: int) -> bool:
ability_ids: List[int] = [] # sorted list
for ability_id in AbilityId: # 1000 items Enum is slow
ability_ids.append(ability_id.value)
ability_ids.remove(0)
ability_ids.sort()

@classmethod
def id_exists(cls, ability_id):
assert isinstance(ability_id, int), f"Wrong type: {ability_id} is not int"
return ability_id != 0 and ability_id in (a.value for a in AbilityId)
if ability_id == 0:
return False
i = bisect_left(cls.ability_ids, ability_id) # quick binary search
return i != len(cls.ability_ids) and cls.ability_ids[i] == ability_id

def __init__(self, game_data, proto):
self._game_data = game_data
Expand Down
77 changes: 49 additions & 28 deletions sc2/game_info.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Tuple, Set, FrozenSet, Sequence, Generator

from collections import deque
from copy import deepcopy
import itertools

Expand Down Expand Up @@ -152,7 +153,7 @@ def __init__(self, proto):
self.terrain_height: PixelMap = PixelMap(proto.start_raw.terrain_height)
self.placement_grid: PixelMap = PixelMap(proto.start_raw.placement_grid)
self.playable_area = Rect.from_proto(proto.start_raw.playable_area)
self.map_ramps: List[Ramp] = self._find_ramps()
self.map_ramps: List[Ramp] = 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 proto.player_info}
self.start_locations: List[Point2] = [Point2.from_proto(sl) for sl in proto.start_raw.start_locations]
self.player_start_location: Point2 = None # Filled later by BotAI._prepare_first_step
Expand All @@ -176,33 +177,53 @@ def _find_ramps(self) -> List[Ramp]:

def _find_groups(self, points: Set[Point2], minimum_points_per_group: int=8, max_distance_between_points: int=2) -> List[Set[Point2]]:
""" From a set/list of points, this function will try to group points together """
foundGroups = []
currentGroup = set()
newlyAdded = set()
pointsPool = set(points)

while pointsPool or currentGroup:
if not currentGroup:
randomPoint = pointsPool.pop()
currentGroup.add(randomPoint)
newlyAdded.add(randomPoint)

newlyAddedOld = newlyAdded
newlyAdded = set()
for p1 in newlyAddedOld:
# create copy as we change set size during iteration
for p2 in pointsPool.copy():
if abs(p1.x - p2.x) + abs(p1.y - p2.y) <= max_distance_between_points:
currentGroup.add(p2)
newlyAdded.add(p2)
pointsPool.discard(p2)

# Check if all connected points were found
if not newlyAdded:
# Add to group if number of points reached threshold - discard group if not enough points
if len(currentGroup) >= minimum_points_per_group:
foundGroups.append(currentGroup)
currentGroup = set()
""" Paint clusters of points in rectangular map using flood fill algorithm. """
NOT_INTERESTED = -2
NOT_COLORED_YET = -1
currentColor: int = NOT_COLORED_YET
picture: List[List[int]] = [[NOT_INTERESTED
for j in range (self.pathing_grid.width)]
for i in range (self.pathing_grid.height)]

def paint (pt: Point2) -> None:
picture[pt.y][pt.x] = currentColor

nearby: Set[Point2] = set ()
for dx in range (-max_distance_between_points, max_distance_between_points + 1):
for dy in range (-max_distance_between_points, max_distance_between_points + 1):
if abs (dx) + abs (dy) <= max_distance_between_points:
nearby.add (Point2 ((dx, dy)))

for point in points:
paint (point)

remaining: Set[Point2] = set (points)
queue: Deque[Point2] = deque ()
foundGroups: List[Set[Point2]] = []
while remaining:
currentGroup: Set[Point2] = set ()
if not queue:
currentColor += 1
start = remaining.pop ()
paint (start)
queue.append (start)
currentGroup.add (start)
while queue:
base: Point2 = queue.popleft ()
for offset in nearby:
px, py = base.x + offset.x, base.y + offset.y
if px < 0 or py < 0 or px >= self.pathing_grid.width or py >= self.pathing_grid.height:
continue
if picture[py][px] != NOT_COLORED_YET:
continue
point: Point2 = Point2 ((px, py))
remaining.remove (point)
paint (point)
queue.append (point)
currentGroup.add (point)
if len (currentGroup) >= minimum_points_per_group:
foundGroups.append (currentGroup)

""" Returns groups of points as list
[{p1, p2, p3}, {p4, p5, p6, p7, p8}]
"""
Expand Down
16 changes: 12 additions & 4 deletions sc2/ids/unit_typeid.py
Original file line number Diff line number Diff line change
Expand Up @@ -1935,10 +1935,18 @@ class UnitTypeId(enum.Enum):
TYCHUSHERCACGLUESCREENDUMMY = 1928
TYCHUSGHOSTACGLUESCREENDUMMY = 1929
TYCHUSSCVAUTOTURRETACGLUESCREENDUMMY = 1930
RENEGADELONGBOLTMISSILEWEAPON = 1931
VIKING = 1932
RENEGADEMISSILETURRET = 1933
PARASITICBOMBRELAYDUMMY = 1934
ZERATULSTALKERACGLUESCREENDUMMY = 1931
ZERATULSENTRYACGLUESCREENDUMMY = 1932
ZERATULDARKTEMPLARACGLUESCREENDUMMY = 1933
ZERATULIMMORTALACGLUESCREENDUMMY = 1934
ZERATULOBSERVERACGLUESCREENDUMMY = 1935
ZERATULDISRUPTORACGLUESCREENDUMMY = 1936
ZERATULWARPPRISMACGLUESCREENDUMMY = 1937
ZERATULPHOTONCANNONACGLUESCREENDUMMY = 1938
RENEGADELONGBOLTMISSILEWEAPON = 1939
VIKING = 1940
RENEGADEMISSILETURRET = 1941
PARASITICBOMBRELAYDUMMY = 1942

for item in UnitTypeId:
assert not item.name in globals()
Expand Down
2 changes: 1 addition & 1 deletion sc2/unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def target_in_range(self, target: "Unit", bonus_distance: Union[int, float]=0) -
""" Includes the target's radius when calculating distance to target """
if self.can_attack_ground and not target.is_flying:
unit_attack_range = self.ground_range
elif self.can_attack_air and target.is_flying:
elif self.can_attack_air and (target.is_flying or target.type_id == UnitTypeId.COLOSSUS):
unit_attack_range = self.air_range
else:
unit_attack_range = -1
Expand Down

0 comments on commit f00c142

Please sign in to comment.