55from collections import Counter
66from typing import Any , Dict , List , Optional , Set , Tuple , Union # mypy type checking
77
8- from s2clientprotocol import common_pb2 as common_pb
9-
108from .cache import property_cache_forever , property_cache_once_per_frame
119from .data import ActionResult , Alert , Race , Result , Target , race_gas , race_townhalls , race_worker
1210from .game_data import AbilityData , GameData
@@ -126,15 +124,24 @@ def known_enemy_structures(self) -> Units:
126124
127125 @property
128126 def main_base_ramp (self ) -> "Ramp" :
129- """ Returns the Ramp instance of the closest main-ramp to start location. Look in game_info.py for more information """
127+ """ Returns the Ramp instance of the closest main-ramp to start location.
128+ Look in game_info.py for more information """
130129 if hasattr (self , "cached_main_base_ramp" ):
131130 return self .cached_main_base_ramp
132- """ The reason for len(ramp.upper) in {2, 5} is:
133- 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) """
134- self .cached_main_base_ramp = min (
135- (ramp for ramp in self .game_info .map_ramps if len (ramp .upper ) in {2 , 5 }),
136- key = lambda r : self .start_location .distance_to (r .top_center ),
137- )
131+ # The reason for len(ramp.upper) in {2, 5} is:
132+ # ParaSite map has 5 upper points, and most other maps have 2 upper points at the main ramp.
133+ # The map Acolyte has 4 upper points at the wrong ramp (which is closest to the start position).
134+ try :
135+ self .cached_main_base_ramp = min (
136+ (ramp for ramp in self .game_info .map_ramps if len (ramp .upper ) in {2 , 5 }),
137+ key = lambda r : self .start_location .distance_to (r .top_center ),
138+ )
139+ except ValueError :
140+ # Hardcoded hotfix for Honorgrounds LE map, as that map has a large main base ramp with inbase natural
141+ self .cached_main_base_ramp = min (
142+ (ramp for ramp in self .game_info .map_ramps if len (ramp .upper ) in {4 , 9 }),
143+ key = lambda r : self .start_location .distance_to (r .top_center ),
144+ )
138145 return self .cached_main_base_ramp
139146
140147 @property_cache_forever
@@ -149,13 +156,9 @@ def expansion_locations(self) -> Dict[Point2, Units]:
149156
150157 # Distance we group resources by
151158 RESOURCE_SPREAD_THRESHOLD = 8.5
152- minerals = self .state .mineral_field
153159 geysers = self .state .vespene_geyser
154- all_resources = minerals | geysers
155- # Presort resources to get faster clustering
156- all_resources .sort (key = lambda resource : (resource .position .x , resource .position .y ))
157160 # Create a group for every resource
158- resource_groups = [[resource ] for resource in all_resources ]
161+ resource_groups = [[resource ] for resource in self . state . resources ]
159162 # Loop the merging process as long as we change something
160163 found_something = True
161164 while found_something :
@@ -177,9 +180,8 @@ def expansion_locations(self) -> Dict[Point2, Units]:
177180 offset_range = 7
178181 offsets = [
179182 (x , y )
180- for x in range (- offset_range , offset_range + 1 )
181- for y in range (- offset_range , offset_range + 1 )
182- if 4 <= math .hypot (x , y ) <= 7
183+ for x , y in itertools .product (range (- offset_range , offset_range + 1 ), repeat = 2 )
184+ if math .hypot (x , y ) <= 8
183185 ]
184186 # Dict we want to return
185187 centers = {}
@@ -189,20 +191,19 @@ def expansion_locations(self) -> Dict[Point2, Units]:
189191 amount = len (resources )
190192 # Calculate center, round and add 0.5 because expansion location will have (x.5, y.5)
191193 # coordinates because bases have size 5.
192- center_x = round (sum (resource .position .x for resource in resources ) / amount ) + 0.5
193- center_y = round (sum (resource .position .y for resource in resources ) / amount ) + 0.5
194+ center_x = int (sum (resource .position .x for resource in resources ) / amount ) + 0.5
195+ center_y = int (sum (resource .position .y for resource in resources ) / amount ) + 0.5
194196 possible_points = (Point2 ((offset [0 ] + center_x , offset [1 ] + center_y )) for offset in offsets )
195197 # Filter out points that are too near
196198 possible_points = (
197199 point
198200 for point in possible_points
199201 # Check if point can be built on
200- if self ._game_info .placement_grid [point .rounded ] != 0
202+ if self ._game_info .placement_grid [point .rounded ] == 1
201203 # Check if all resources have enough space to point
202- and all (point .distance_to (resource ) >= (7 if resource in geysers else 6 ) for resource in resources )
204+ and all (point .distance_to (resource ) > (7 if resource in geysers else 6 ) for resource in resources )
203205 )
204206 # Choose best fitting point
205- # TODO can we improve this by calculating the distance only one time?
206207 result = min (possible_points , key = lambda point : sum (point .distance_to (resource ) for resource in resources ))
207208 centers [result ] = resources
208209 return centers
0 commit comments