Skip to content

Commit

Permalink
Fix ramp bug on map Acolyte and add cache for ramp points
Browse files Browse the repository at this point in the history
  • Loading branch information
BurnySc2 authored and tweakimp committed Feb 2, 2019
1 parent 2403640 commit 9470180
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 21 deletions.
21 changes: 20 additions & 1 deletion examples/terran/ramp_wall.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion sc2/bot_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 19 additions & 18 deletions sc2/game_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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):
Expand All @@ -22,56 +24,56 @@ 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:
# NOTE: this was way too slow on large ramps
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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -111,22 +113,21 @@ 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
if len(self.upper2_for_ramp_wall) == 2:
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.")


Expand Down
2 changes: 1 addition & 1 deletion sc2/position.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9470180

Please sign in to comment.