From bbc0575419c9c96315bd4697f539fad9356fc068 Mon Sep 17 00:00:00 2001 From: Jaroslav Wegner Date: Thu, 1 Nov 2018 21:29:32 +0300 Subject: [PATCH 1/8] Load abilities faster Eliminated bottleneck of loading abilities data at startup time --- sc2/game_data.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sc2/game_data.py b/sc2/game_data.py index 8a0c318e4..eee14a3ff 100644 --- a/sc2/game_data.py +++ b/sc2/game_data.py @@ -69,10 +69,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 From 984bbdf9629a051b1158e0a8b2a563d635bb74de Mon Sep 17 00:00:00 2001 From: Jaroslav Wegner Date: Fri, 2 Nov 2018 01:08:03 +0300 Subject: [PATCH 2/8] Import bisect --- sc2/game_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sc2/game_data.py b/sc2/game_data.py index eee14a3ff..b09bcc595 100644 --- a/sc2/game_data.py +++ b/sc2/game_data.py @@ -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 From c948f55d0c3c105b56729c3b60b84a1d8801b51e Mon Sep 17 00:00:00 2001 From: Jaroslav Wegner Date: Fri, 2 Nov 2018 01:16:35 +0300 Subject: [PATCH 3/8] Find point clusters during ramp search faster Use flood fill algorithm to only compare nearby points --- sc2/game_info.py | 75 +++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/sc2/game_info.py b/sc2/game_info.py index 3b3edbb42..450c1239b 100644 --- a/sc2/game_info.py +++ b/sc2/game_info.py @@ -1,5 +1,6 @@ from typing import Tuple, Set, FrozenSet, Sequence, Generator +from collections import deque from copy import deepcopy import itertools @@ -169,33 +170,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}] """ From e75272c6fafe6ba6b1987af1b2b054d065cd88fc Mon Sep 17 00:00:00 2001 From: Jaroslav Wegner Date: Fri, 2 Nov 2018 01:23:23 +0300 Subject: [PATCH 4/8] Import bisect --- sc2/game_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sc2/game_data.py b/sc2/game_data.py index eee14a3ff..b09bcc595 100644 --- a/sc2/game_data.py +++ b/sc2/game_data.py @@ -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 From bcbb6cf3a0c9171895628a38402d5d6651b52b16 Mon Sep 17 00:00:00 2001 From: BurnySc2 Date: Sun, 4 Nov 2018 19:22:04 +0100 Subject: [PATCH 5/8] Relocate the game_info.find_ramps() call --- sc2/bot_ai.py | 1 + sc2/game_info.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sc2/bot_ai.py b/sc2/bot_ai.py index 97c267b03..db5a49e2f 100644 --- a/sc2/bot_ai.py +++ b/sc2/bot_ai.py @@ -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.""" diff --git a/sc2/game_info.py b/sc2/game_info.py index 8495c415b..4badb2136 100644 --- a/sc2/game_info.py +++ b/sc2/game_info.py @@ -153,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 From 1f2744b07cd657b49d23b7ce5861540836ec4aa1 Mon Sep 17 00:00:00 2001 From: FW Date: Tue, 6 Nov 2018 15:27:50 +0100 Subject: [PATCH 6/8] Improve expansion location (#167) --- sc2/bot_ai.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sc2/bot_ai.py b/sc2/bot_ai.py index db5a49e2f..023714bcf 100644 --- a/sc2/bot_ai.py +++ b/sc2/bot_ai.py @@ -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 From e8134dfb56a027e1f2b11a740c9f64c544a6cb12 Mon Sep 17 00:00:00 2001 From: FW Date: Mon, 19 Nov 2018 12:55:24 +0100 Subject: [PATCH 7/8] Update unit.py (#172) * Update unit.py --- sc2/unit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sc2/unit.py b/sc2/unit.py index 48188ccea..7ee96749d 100644 --- a/sc2/unit.py +++ b/sc2/unit.py @@ -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 From 303516cd1c56e24a0a4dcd7d7bea210c893ef0c8 Mon Sep 17 00:00:00 2001 From: BurnySc2 Date: Fri, 30 Nov 2018 08:27:01 +0100 Subject: [PATCH 8/8] Update unit_typeid to fix Viking enum --- sc2/ids/unit_typeid.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sc2/ids/unit_typeid.py b/sc2/ids/unit_typeid.py index ba66d202b..417153e04 100644 --- a/sc2/ids/unit_typeid.py +++ b/sc2/ids/unit_typeid.py @@ -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()