@@ -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