Skip to content

Commit 6b05f82

Browse files
authored
Merge pull request Dentosal#277 from Dentosal/Add-more-test-maps
Add more test maps
2 parents f2e915a + 78cd842 commit 6b05f82

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+135
-103
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- docker exec app pip install --upgrade pipenv
3131
# Create pipenv virtual environment and install dev packages for testing
3232
# Command origins from here: https://stackoverflow.com/a/28037991/10882657
33-
- docker exec -i app bash -c "cd /root/template && pipenv install --dev"
33+
- docker exec -i app bash -c "cd /root/template && pipenv install --dev --python 3.7"
3434
# Run tests
3535
- docker exec -i app bash -c "cd /root/template && pipenv run pytest test/"
3636
# Shut down and remove container
@@ -43,7 +43,7 @@ jobs:
4343
- docker run -it -d --name app python:3.8-rc-slim
4444
- docker cp . app:/root/template
4545
- docker exec app pip install --upgrade pipenv
46-
- docker exec -i app bash -c "cd /root/template && pipenv --python python install --dev"
46+
- docker exec -i app bash -c "cd /root/template && pipenv --python python install --dev --python 3.8"
4747
- docker exec -i app bash -c "cd /root/template && pipenv run pytest test/"
4848
- docker rm -f app
4949

examples/terran/ramp_wall.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,36 @@ async def on_step(self, iteration):
4343
# Uncomment the following if you want to build 3 supplydepots in the wall instead of a barracks in the middle + 2 depots in the corner
4444
# depot_placement_positions = self.main_base_ramp.corner_depots | {self.main_base_ramp.depot_in_middle}
4545

46-
barracks_placement_position = None
4746
barracks_placement_position = self.main_base_ramp.barracks_correct_placement
4847
# If you prefer to have the barracks in the middle without room for addons, use the following instead
4948
# barracks_placement_position = self.main_base_ramp.barracks_in_middle
5049

5150
depots = self.units(SUPPLYDEPOT) | self.units(SUPPLYDEPOTLOWERED)
5251

5352
# Draw ramp points
54-
# def terrain_to_z_height(h):
55-
# return round(16 * h / 255, 2)
56-
57-
# for ramp in self.game_info.map_ramps:
58-
# for p in ramp.points:
59-
# h = self.get_terrain_height(p)
60-
# h2 = terrain_to_z_height(h)
61-
# pos = Point3((p.x, p.y, h2))
62-
# p0 = Point3((pos.x - 0.25, pos.y - 0.25, pos.z))
63-
# p1 = Point3((pos.x + 0.25, pos.y + 0.25, pos.z - 0.5))
64-
# print(f"drawing {p0} to {p1}")
65-
# self._client.debug_box_out(p0, p1, color=Point3((255, 0, 0)))
66-
#
67-
# await self._client.send_debug()
68-
69-
# Filter locations close to finished supply depots
53+
def terrain_to_z_height(h):
54+
return round(16 * h / 255, 2)
55+
56+
for ramp in self.game_info.map_ramps:
57+
for p in ramp.points:
58+
h = self.get_terrain_height(p)
59+
h2 = terrain_to_z_height(h)
60+
pos = Point3((p.x, p.y, h2))
61+
p0 = Point3((pos.x - 0.25, pos.y - 0.25, pos.z))
62+
p1 = Point3((pos.x + 0.25, pos.y + 0.25, pos.z - 0.5))
63+
# print(f"Drawing {p0} to {p1}")
64+
color=Point3((255, 0, 0))
65+
if p in ramp.upper:
66+
color = Point3((0, 255, 0))
67+
if p in ramp.upper2_for_ramp_wall:
68+
color = Point3((0, 255, 255))
69+
if p in ramp.lower:
70+
color = Point3((0, 0, 255))
71+
self._client.debug_box_out(p0, p1, color=color)
72+
73+
await self._client.send_debug()
74+
75+
# # Filter locations close to finished supply depots
7076
if depots:
7177
depot_placement_positions = {d for d in depot_placement_positions if depots.closest_distance_to(d) > 1}
7278

@@ -82,7 +88,7 @@ async def on_step(self, iteration):
8288
await self.do(w.build(SUPPLYDEPOT, target_depot_location))
8389

8490
# Build barracks
85-
if depots.ready.exists and self.can_afford(BARRACKS) and not self.already_pending(BARRACKS):
91+
if depots.ready and self.can_afford(BARRACKS) and not self.already_pending(BARRACKS):
8692
if self.units(BARRACKS).amount + self.already_pending(BARRACKS) > 0:
8793
return
8894
ws = self.workers.gathering
@@ -108,6 +114,7 @@ def main():
108114
"DarknessSanctuaryLE",
109115
"ParaSiteLE", # Has 5 upper points at the main ramp
110116
"AcolyteLE", # Has 4 upper points at the ramp to the in-base natural and 2 upper points at the small ramp
117+
"HonorgroundsLE", # Has 4 or 9 upper points at the large main base ramp
111118
]
112119
)
113120
sc2.run_game(

sc2/bot_ai.py

Lines changed: 75 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,24 @@ def known_enemy_structures(self) -> Units:
126126

127127
@property
128128
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 """
129+
""" Returns the Ramp instance of the closest main-ramp to start location.
130+
Look in game_info.py for more information """
130131
if hasattr(self, "cached_main_base_ramp"):
131132
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-
)
133+
# The reason for len(ramp.upper) in {2, 5} is:
134+
# ParaSite map has 5 upper points, and most other maps have 2 upper points at the main ramp.
135+
# The map Acolyte has 4 upper points at the wrong ramp (which is closest to the start position).
136+
try:
137+
self.cached_main_base_ramp = min(
138+
(ramp for ramp in self.game_info.map_ramps if len(ramp.upper) in {2, 5}),
139+
key=lambda r: self.start_location.distance_to(r.top_center),
140+
)
141+
except ValueError:
142+
# Hardcoded hotfix for Honorgrounds LE map, as that map has a large main base ramp with inbase natural
143+
self.cached_main_base_ramp = min(
144+
(ramp for ramp in self.game_info.map_ramps if len(ramp.upper) in {4, 9}),
145+
key=lambda r: self.start_location.distance_to(r.top_center),
146+
)
138147
return self.cached_main_base_ramp
139148

140149
@property_cache_forever
@@ -148,64 +157,65 @@ def expansion_locations(self) -> Dict[Point2, Units]:
148157
# any resource in a group is closer than 6 to any resource of another group
149158

150159
# Distance we group resources by
151-
RESOURCE_SPREAD_THRESHOLD = 8.5
152-
minerals = self.state.mineral_field
153-
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))
157-
# Create a group for every resource
158-
resource_groups = [[resource] for resource in all_resources]
159-
# Loop the merging process as long as we change something
160-
found_something = True
161-
while found_something:
162-
found_something = False
163-
# Check every combination of two groups
164-
for group_a, group_b in itertools.combinations(resource_groups, 2):
165-
# Check if any pair of resource of these groups is closer than threshold together
166-
if any(
167-
resource_a.distance_to(resource_b) <= RESOURCE_SPREAD_THRESHOLD
168-
for resource_a, resource_b in itertools.product(group_a, group_b)
169-
):
170-
# Remove the single groups and add the merged group
171-
resource_groups.remove(group_a)
172-
resource_groups.remove(group_b)
173-
resource_groups.append(group_a + group_b)
174-
found_something = True
175-
break
176-
# Distance offsets we apply to center of each resource group to find expansion position
177-
offset_range = 7
178-
offsets = [
179-
(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-
]
184-
# Dict we want to return
185-
centers = {}
186-
# For every resource group:
187-
for resources in resource_groups:
188-
# Possible expansion points
189-
amount = len(resources)
190-
# Calculate center, round and add 0.5 because expansion location will have (x.5, y.5)
191-
# 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-
possible_points = (Point2((offset[0] + center_x, offset[1] + center_y)) for offset in offsets)
195-
# Filter out points that are too near
196-
possible_points = (
197-
point
198-
for point in possible_points
199-
# Check if point can be built on
200-
if self._game_info.placement_grid[point.rounded] != 0
201-
# 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)
203-
)
204-
# Choose best fitting point
205-
# TODO can we improve this by calculating the distance only one time?
206-
result = min(possible_points, key=lambda point: sum(point.distance_to(resource) for resource in resources))
207-
centers[result] = resources
208-
return centers
160+
from .helpers.devtools import time_this
161+
162+
with time_this("expo locations"):
163+
RESOURCE_SPREAD_THRESHOLD = 8.5
164+
minerals = self.state.mineral_field
165+
geysers = self.state.vespene_geyser
166+
all_resources = minerals | geysers
167+
# Presort resources to get faster clustering
168+
# all_resources.sort(key=lambda resource: resource.position.x ** 2 + resource.position.y ** 2)
169+
all_resources.sort(key=lambda resource: resource.position.x ** 2 + resource.position.y ** 2)
170+
# Create a group for every resource
171+
resource_groups = [[resource] for resource in all_resources]
172+
# Loop the merging process as long as we change something
173+
found_something = True
174+
while found_something:
175+
found_something = False
176+
# Check every combination of two groups
177+
for group_a, group_b in itertools.combinations(resource_groups, 2):
178+
# Check if any pair of resource of these groups is closer than threshold together
179+
if any(
180+
resource_a.distance_to(resource_b) <= RESOURCE_SPREAD_THRESHOLD
181+
for resource_a, resource_b in itertools.product(group_a, group_b)
182+
):
183+
# Remove the single groups and add the merged group
184+
resource_groups.remove(group_a)
185+
resource_groups.remove(group_b)
186+
resource_groups.append(group_a + group_b)
187+
found_something = True
188+
break
189+
# Distance offsets we apply to center of each resource group to find expansion position
190+
offset_range = 7
191+
offsets = [(x, y) for x, y in itertools.product(range(-offset_range, offset_range + 1), repeat=2)]
192+
# Dict we want to return
193+
centers = {}
194+
# For every resource group:
195+
for resources in resource_groups:
196+
# Possible expansion points
197+
amount = len(resources)
198+
# Calculate center, round and add 0.5 because expansion location will have (x.5, y.5)
199+
# coordinates because bases have size 5.
200+
center_x = round(sum(resource.position.x for resource in resources) / amount) + 0.5
201+
center_y = round(sum(resource.position.y for resource in resources) / amount) + 0.5
202+
possible_points = (Point2((offset[0] + center_x, offset[1] + center_y)) for offset in offsets)
203+
# Filter out points that are too near
204+
possible_points = (
205+
point
206+
for point in possible_points
207+
# Check if point can be built on
208+
if self._game_info.placement_grid[point.rounded] == 1
209+
# Check if all resources have enough space to point
210+
and all(point.distance_to(resource) > (7 if resource in geysers else 6) for resource in resources)
211+
)
212+
# Choose best fitting point
213+
# TODO can we improve this by calculating the distance only one time?
214+
result = min(
215+
possible_points, key=lambda point: sum(point.distance_to(resource) for resource in resources)
216+
)
217+
centers[result] = resources
218+
return centers
209219

210220
def _correct_zerg_supply(self):
211221
""" The client incorrectly rounds zerg supply down instead of up (see

sc2/game_info.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ def bottom_center(self) -> Point2:
9292
return pos
9393

9494
@property_immutable_cache
95-
def barracks_in_middle(self) -> Point2:
95+
def barracks_in_middle(self) -> Optional[Point2]:
9696
""" Barracks position in the middle of the 2 depots """
97+
if len(self.upper) not in {2, 5}:
98+
return None
9799
if len(self.upper2_for_ramp_wall) == 2:
98100
points = self.upper2_for_ramp_wall
99101
p1 = points.pop().offset((self.x_offset, self.y_offset))
@@ -105,27 +107,35 @@ def barracks_in_middle(self) -> Point2:
105107
raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.")
106108

107109
@property_immutable_cache
108-
def depot_in_middle(self) -> Point2:
110+
def depot_in_middle(self) -> Optional[Point2]:
109111
""" Depot in the middle of the 3 depots """
110112
if len(self.upper2_for_ramp_wall) == 2:
111113
points = self.upper2_for_ramp_wall
112-
p1 = points.pop().offset((self.x_offset, self.y_offset)) # still an error with pixelmap?
114+
p1 = points.pop().offset((self.x_offset, self.y_offset))
113115
p2 = points.pop().offset((self.x_offset, self.y_offset))
114116
# Offset from top point to depot center is (1.5, 0.5)
115-
intersects = p1.circle_intersection(p2, 2.5 ** 0.5)
117+
try:
118+
intersects = p1.circle_intersection(p2, 2.5 ** 0.5)
119+
except AssertionError:
120+
# Returns None when no placement was found, this is the case on the map Honorgrounds LE with an exceptionally large main base ramp
121+
return None
116122
anyLowerPoint = next(iter(self.lower))
117123
return max(intersects, key=lambda p: p.distance_to_point2(anyLowerPoint))
118124
raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.")
119125

120126
@property_mutable_cache
121127
def corner_depots(self) -> Set[Point2]:
122128
""" Finds the 2 depot positions on the outside """
129+
if not self.upper2_for_ramp_wall:
130+
return set()
123131
if len(self.upper2_for_ramp_wall) == 2:
124132
points = self.upper2_for_ramp_wall
125-
p1 = points.pop().offset((self.x_offset, self.y_offset)) # still an error with pixelmap?
133+
p1 = points.pop().offset((self.x_offset, self.y_offset))
126134
p2 = points.pop().offset((self.x_offset, self.y_offset))
127135
center = p1.towards(p2, p1.distance_to_point2(p2) / 2)
128136
depotPosition = self.depot_in_middle
137+
if depotPosition is None:
138+
return set()
129139
# Offset from middle depot to corner depots is (2, 1)
130140
intersects = center.circle_intersection(depotPosition, 5 ** 0.5)
131141
return intersects
@@ -140,8 +150,10 @@ def barracks_can_fit_addon(self) -> bool:
140150
raise Exception("Not implemented. Trying to access a ramp that has a wrong amount of upper points.")
141151

142152
@property_immutable_cache
143-
def barracks_correct_placement(self) -> Point2:
153+
def barracks_correct_placement(self) -> Optional[Point2]:
144154
""" Corrected placement so that an addon can fit """
155+
if self.barracks_in_middle is None:
156+
return None
145157
if len(self.upper2_for_ramp_wall) == 2:
146158
if self.barracks_can_fit_addon:
147159
return self.barracks_in_middle

test/generate_pickle_files_bot.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@
3737

3838
class ExporterBot(sc2.BotAI):
3939
async def on_step(self, iteration):
40-
if iteration == 0:
41-
await self.on_first_iteration()
42-
4340
actions = []
4441
await self.do_actions(actions)
4542

@@ -76,7 +73,7 @@ async def on_start_async(self):
7673
def main():
7774

7875
maps = [
79-
"16-BitLE.SC2Map",
76+
"16-BitLE",
8077
"AbiogenesisLE",
8178
"AbyssalReefLE",
8279
"AcidPlantLE",
@@ -108,7 +105,6 @@ def main():
108105
"HonorgroundsLE",
109106
"InterloperLE",
110107
"KairosJunctionLE",
111-
"KairosJunctionLE",
112108
"KingsCoveLE",
113109
"LostandFoundLE",
114110
"MechDepotLE",
@@ -119,7 +115,6 @@ def main():
119115
"OldSunshine",
120116
"PaladinoTerminalLE",
121117
"ParaSiteLE",
122-
"ParaSiteLE",
123118
"PortAleksanderLE",
124119
"PrimusQ9",
125120
"ProximaStationLE",

test/pickle_data/16-Bit LE.pkl

424 KB
Binary file not shown.
430 KB
Binary file not shown.
442 KB
Binary file not shown.

test/pickle_data/Acid Plant LE.pkl

428 KB
Binary file not shown.

test/pickle_data/Acolyte LE.pkl

437 KB
Binary file not shown.

0 commit comments

Comments
 (0)