Skip to content

Commit fd8c08a

Browse files
authored
Merge pull request #270 from tweakimp/develop
Update distribute_workers
2 parents 8913174 + 7de0c49 commit fd8c08a

File tree

1 file changed

+61
-57
lines changed

1 file changed

+61
-57
lines changed

sc2/bot_ai.py

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -293,64 +293,68 @@ async def distribute_workers(self):
293293
Distributes workers across all the bases taken.
294294
WARNING: This is quite slow when there are lots of workers or multiple bases.
295295
"""
296-
297-
# TODO:
298-
# OPTIMIZE: Assign idle workers smarter
299-
# OPTIMIZE: Never use same worker mutltiple times
300-
owned_expansions = self.owned_expansions
301-
worker_pool = []
296+
if not self.state.mineral_field or not self.workers or not self.owned_expansions.ready:
297+
return
302298
actions = []
299+
worker_pool = [worker for worker in self.workers.idle]
300+
bases = self.owned_expansions.ready
301+
geysers = self.geysers.ready
303302

304-
for idle_worker in self.workers.idle:
305-
mf = self.state.mineral_field.closest_to(idle_worker)
306-
actions.append(idle_worker.gather(mf))
307-
308-
for location, townhall in owned_expansions.items():
309-
workers = self.workers.closer_than(20, location)
310-
actual = townhall.assigned_harvesters
311-
ideal = townhall.ideal_harvesters
312-
excess = actual - ideal
313-
if actual > ideal:
314-
worker_pool.extend(workers.random_group_of(min(excess, len(workers))))
315-
continue
316-
for g in self.geysers:
317-
workers = self.workers.closer_than(5, g)
318-
actual = g.assigned_harvesters
319-
ideal = g.ideal_harvesters
320-
excess = actual - ideal
321-
if actual > ideal:
322-
worker_pool.extend(workers.random_group_of(min(excess, len(workers))))
323-
continue
303+
# list of places that need more workers
304+
deficit_mining_places = []
324305

325-
for g in self.geysers:
326-
actual = g.assigned_harvesters
327-
ideal = g.ideal_harvesters
328-
deficit = ideal - actual
329-
330-
for _ in range(deficit):
331-
if worker_pool:
332-
w = worker_pool.pop()
333-
if len(w.orders) == 1 and w.orders[0].ability.id is AbilityId.HARVEST_RETURN:
334-
actions.append(w.move(g))
335-
actions.append(w.return_resource(queue=True))
336-
else:
337-
actions.append(w.gather(g))
338-
339-
for location, townhall in owned_expansions.items():
340-
actual = townhall.assigned_harvesters
341-
ideal = townhall.ideal_harvesters
342-
343-
deficit = ideal - actual
344-
for _ in range(deficit):
345-
if worker_pool:
346-
w = worker_pool.pop()
347-
mf = self.state.mineral_field.closest_to(townhall)
348-
if len(w.orders) == 1 and w.orders[0].ability.id is AbilityId.HARVEST_RETURN:
349-
actions.append(w.move(townhall))
350-
actions.append(w.return_resource(queue=True))
351-
actions.append(w.gather(mf, queue=True))
352-
else:
353-
actions.append(w.gather(mf))
306+
for mining_place in bases | geysers:
307+
difference = mining_place.surplus_harvesters
308+
# perfect amount of workers, skip mining place
309+
if not difference:
310+
continue
311+
if mining_place.has_vespene:
312+
# get all workers that target the gas extraction site
313+
# or are on their way back from it
314+
local_workers = self.workers.filter(
315+
lambda unit: unit.order_target == mining_place.tag
316+
or (unit.is_carrying_vespene and unit.order_target == bases.closest_to(mining_place).tag)
317+
)
318+
else:
319+
# get minerals around expansion
320+
local_minerals = self.expansion_locations[mining_place.position].filter(
321+
lambda resource: resource.has_minerals
322+
)
323+
# get all target tags a worker can have
324+
# tags of the minerals he could mine at that base
325+
# get workers that work at that gather site
326+
local_workers = self.workers.filter(
327+
lambda unit: unit.order_target in local_minerals.tags
328+
or (unit.is_carrying_minerals and unit.order_target == mining_place.tag)
329+
)
330+
# too many workers
331+
if difference > 0:
332+
worker_pool.append(local_workers[:difference])
333+
# too few workers
334+
# add mining place to deficit bases for every missing worker
335+
else:
336+
deficit_mining_places += [mining_place for _ in range(-difference)]
337+
338+
# distribute every worker in the pool
339+
for worker in worker_pool:
340+
# as long as have workers and mining places
341+
if deficit_mining_places:
342+
# remove current place from the list for next loop
343+
current_place = deficit_mining_places.pop(0)
344+
# if current place is a gas extraction site, go there
345+
if current_place.has_vespene:
346+
actions.append(worker.gather(current_place))
347+
# if current place is a gas extraction site,
348+
# go to the mineral field that is near and has the most minerals left
349+
else:
350+
local_minerals = self.expansion_locations[current_place.position].filter(
351+
lambda resource: resource.has_minerals
352+
)
353+
target_mineral = max(local_minerals, key=lambda mineral: mineral.mineral_contents)
354+
actions.append(worker.gather(target_mineral))
355+
# more workers to distribute than free mining spots
356+
# else:
357+
# pass
354358

355359
await self.do_actions(actions)
356360

@@ -693,8 +697,8 @@ def get_terrain_height(self, pos: Union[Point2, Point3, Unit]) -> int:
693697
assert isinstance(pos, (Point2, Point3, Unit)), f"pos is not of type Point2, Point3 or Unit"
694698
pos = pos.position.to2.rounded
695699
return self._game_info.terrain_height[pos]
696-
697-
def get_terrain_z_height(self, pos: Union[Point2, Point3, Unit]) -> int:
700+
701+
def get_terrain_z_height(self, pos: Union[Point2, Point3, Unit]) -> int:
698702
""" Returns terrain z-height at a position. """
699703
assert isinstance(pos, (Point2, Point3, Unit)), f"pos is not of type Point2, Point3 or Unit"
700704
pos = pos.position.to2.rounded

0 commit comments

Comments
 (0)