From 9470180d3caaa18098ebffea220719ad6083afbe Mon Sep 17 00:00:00 2001 From: BurnySc2 Date: Wed, 30 Jan 2019 00:06:10 +0100 Subject: [PATCH] Fix ramp bug on map Acolyte and add cache for ramp points --- examples/terran/ramp_wall.py | 21 +++++++++++++++++++- sc2/bot_ai.py | 4 +++- sc2/game_info.py | 37 ++++++++++++++++++------------------ sc2/position.py | 2 +- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/examples/terran/ramp_wall.py b/examples/terran/ramp_wall.py index 72f7cee28..e7d682617 100644 --- a/examples/terran/ramp_wall.py +++ b/examples/terran/ramp_wall.py @@ -5,6 +5,8 @@ from sc2.constants import * from sc2.player import Bot, Computer from sc2.position import Point2, Point3 +from sc2.unit import Unit +from sc2.units import Units class RampWallBot(sc2.BotAI): @@ -69,10 +71,27 @@ async def on_step(self, iteration): w = ws.random await self.do(w.build(BARRACKS, barracks_placement_position)) + async def on_building_construction_complete(self, unit: Unit): + print(f"Construction of building {unit} completed at {unit.position}.") def main(): - sc2.run_game(sc2.maps.get("OdysseyLE"), [ + map = random.choice( + [ + # Most maps have 2 upper points at the ramp (len(self.main_base_ramp.upper) == 2) + "AutomatonLE", + "BlueshiftLE", + "CeruleanFallLE", + "KairosJunctionLE", + "ParaSiteLE", + "PortAleksanderLE", + "StasisLE", + "DarknessSanctuaryLE", + "ParaSiteLE", # Has 5 upper points at the main ramp + "AcolyteLE", # Has 4 upper points at the ramp to the in-base natural and 2 upper points at the small ramp + ] + ) + sc2.run_game(sc2.maps.get(map), [ Bot(Race.Terran, RampWallBot()), Computer(Race.Zerg, Difficulty.Hard) ], realtime=False) diff --git a/sc2/bot_ai.py b/sc2/bot_ai.py index 33ca41fd2..e9f2fd615 100644 --- a/sc2/bot_ai.py +++ b/sc2/bot_ai.py @@ -77,8 +77,10 @@ def main_base_ramp(self) -> "Ramp": """ Returns the Ramp instance of the closest main-ramp to start location. Look in game_info.py for more information """ if hasattr(self, "cached_main_base_ramp"): return self.cached_main_base_ramp + """ The reason for len(ramp.upper) in {2, 5} is: + ParaSite map has 5 upper points, and most other maps have 2 upper points at the main ramp. The map Acolyte has 4 upper points at the wrong ramp (which is closest to the start position) """ self.cached_main_base_ramp = min( - {ramp for ramp in self.game_info.map_ramps if len(ramp.upper2_for_ramp_wall) == 2}, + {ramp for ramp in self.game_info.map_ramps if len(ramp.upper) in {2, 5}}, key=(lambda r: self.start_location.distance_to(r.top_center)), ) return self.cached_main_base_ramp diff --git a/sc2/game_info.py b/sc2/game_info.py index 62b7a9d3f..d6ff85f9c 100644 --- a/sc2/game_info.py +++ b/sc2/game_info.py @@ -4,6 +4,7 @@ from .pixel_map import PixelMap from .player import Player from .position import Point2, Rect, Size +from .cache import property_immutable_cache, property_mutable_cache class Ramp: @@ -13,6 +14,7 @@ def __init__(self, points: Set[Point2], game_info: "GameInfo"): # tested by printing actual building locations vs calculated depot positions self.x_offset = 0.5 # might be errors with the pixelmap? self.y_offset = -0.5 + self.cache = {} @property def _height_map(self): @@ -22,24 +24,24 @@ def _height_map(self): def _placement_grid(self): return self.__game_info.placement_grid - @property + @property_immutable_cache def size(self) -> int: return len(self._points) def height_at(self, p: Point2) -> int: return self._height_map[p] - @property + @property_mutable_cache def points(self) -> Set[Point2]: return self._points.copy() - @property + @property_mutable_cache def upper(self) -> Set[Point2]: """ Returns the upper points of a ramp. """ max_height = max([self.height_at(p) for p in self._points]) return {p for p in self._points if self.height_at(p) == max_height} - @property + @property_mutable_cache def upper2_for_ramp_wall(self) -> Set[Point2]: """ Returns the 2 upper ramp points of the main base ramp required for the supply depot and barracks placement properties used in this file. """ if len(self.upper) > 5: @@ -47,31 +49,31 @@ def upper2_for_ramp_wall(self) -> Set[Point2]: return set() # HACK: makes this work for now # FIXME: please do - upper2 = sorted(list(self.upper), key=lambda x: x.distance_to(self.bottom_center), reverse=True) + upper2 = sorted(self.upper, key=lambda x: x.distance_to(self.bottom_center), reverse=True) while len(upper2) > 2: upper2.pop() return set(upper2) - @property + @property_immutable_cache def top_center(self) -> Point2: pos = Point2( - (sum([p.x for p in self.upper]) / len(self.upper), sum([p.y for p in self.upper]) / len(self.upper)) + (sum(p.x for p in self.upper) / len(self.upper), sum(p.y for p in self.upper) / len(self.upper)) ) return pos - @property + @property_mutable_cache def lower(self) -> Set[Point2]: - min_height = min([self.height_at(p) for p in self._points]) + min_height = min(self.height_at(p) for p in self._points) return {p for p in self._points if self.height_at(p) == min_height} - @property + @property_immutable_cache def bottom_center(self) -> Point2: pos = Point2( - (sum([p.x for p in self.lower]) / len(self.lower), sum([p.y for p in self.lower]) / len(self.lower)) + (sum(p.x for p in self.lower) / len(self.lower), sum(p.y for p in self.lower) / len(self.lower)) ) return pos - @property + @property_immutable_cache def barracks_in_middle(self) -> Point2: """ Barracks position in the middle of the 2 depots """ if len(self.upper2_for_ramp_wall) == 2: @@ -84,7 +86,7 @@ def barracks_in_middle(self) -> Point2: return max(intersects, key=lambda p: p.distance_to(anyLowerPoint)) raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.") - @property + @property_immutable_cache def depot_in_middle(self) -> Point2: """ Depot in the middle of the 3 depots """ if len(self.upper2_for_ramp_wall) == 2: @@ -97,7 +99,7 @@ def depot_in_middle(self) -> Point2: return max(intersects, key=lambda p: p.distance_to(anyLowerPoint)) raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.") - @property + @property_mutable_cache def corner_depots(self) -> Set[Point2]: """ Finds the 2 depot positions on the outside """ if len(self.upper2_for_ramp_wall) == 2: @@ -111,7 +113,7 @@ def corner_depots(self) -> Set[Point2]: return intersects raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.") - @property + @property_immutable_cache def barracks_can_fit_addon(self) -> bool: """ Test if a barracks can fit an addon at natural ramp """ # https://i.imgur.com/4b2cXHZ.png @@ -119,14 +121,13 @@ def barracks_can_fit_addon(self) -> bool: return self.barracks_in_middle.x + 1 > max(self.corner_depots, key=lambda depot: depot.x).x raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.") - @property + @property_immutable_cache def barracks_correct_placement(self) -> Point2: """ Corrected placement so that an addon can fit """ if len(self.upper2_for_ramp_wall) == 2: if self.barracks_can_fit_addon: return self.barracks_in_middle - else: - return self.barracks_in_middle.offset((-2, 0)) + return self.barracks_in_middle.offset((-2, 0)) raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.") diff --git a/sc2/position.py b/sc2/position.py index 28c6b2227..4e8374d06 100644 --- a/sc2/position.py +++ b/sc2/position.py @@ -185,7 +185,7 @@ def circle_intersection(self, p: "Point2", r: Union[int, float]) -> Set["Point2" Used in ramp finding """ assert self != p distanceBetweenPoints = self.distance_to(p) - assert r > distanceBetweenPoints / 2 + assert r >= distanceBetweenPoints / 2 # remaining distance from center towards the intersection, using pythagoras remainingDistanceFromCenter = (r ** 2 - (distanceBetweenPoints / 2) ** 2) ** 0.5 # center of both points