def get_empty_geysers(self, gas_structs): return [ vg for vg in list_flatten([ self.vespene_geyser.closer_than(15, th) for th in self.townhalls.ready ]) if gas_structs.empty or gas_structs.closer_than(1.0, vg).empty ]
def unallocated(self, unit_types=None, urgency=Urgency.NONE): units = self.units.ready(unit_types) if unit_types else self.units.ready.filter(lambda u: not is_worker(u)) return units.tags_not_in(list_flatten([ list(module.allocated) if module.urgency >= urgency else [] for module in self.modules ]))
def generate_targets(self): # if the situation is anything other than a single base in the main, # this *might* be hit once but that scout is going home soon base = self.enemy_structures(BaseStructures).first def distance_to_enemy(ramp): return ramp.top_center.distance_to(base) # TODO: figure out ramp better likely_main_ramp = min(self.game_info.map_ramps, key=distance_to_enemy) def distance_to_ramp(base): return base.distance_to(likely_main_ramp.bottom_center) possible_naturals = [ position for position in self.expansion_locations_dict.keys() if position.is_further_than(1.0, base.position) ] likely_natural = min(possible_naturals, key=distance_to_ramp) corners = [ Point2([8, 8]), Point2([8, -8]), Point2([-8, -8]), Point2([-8, 8]) ] self.targets = list_flatten([[pos + base.position for pos in corners], [likely_natural]])
def _get_large_positions(self, near): acceptable_positions = self.plans[near.tag].large_positions if near else list_flatten([ p.large_positions for p in self.plans.values() ]) return [ p for p in acceptable_positions if all(self.bot.has_creep(p + offset) for offset in _3X3_OFFSETS) and not self.structures.closer_than(1.0, p).exists ]
def _get_non_pylon_positions(self, near): acceptable_positions = self.plans[ near.tag].structure_positions if near else list_flatten( [p.structure_positions for p in self.plans.values()]) return [ p for p in acceptable_positions if self.state.psionic_matrix.covers(p) and not self.structures.closer_than(1.0, p).exists ]
def _get_small_positions(self): existing_structures = [ structure.position for structure in self.structures(_SMALL_STRUCTURES) ] acceptable_positions = [ p for p in list_flatten([ p.small_positions for p in self.plans.values() ]) if p not in existing_structures and all(self.bot.has_creep(p + offset) for offset in _2X2_OFFSETS) ] random.shuffle(acceptable_positions) return [p for p in acceptable_positions if not self.structures.closer_than(1.0, p).exists]
def _get_pylon_positions(self): base_locations = [nex.position for nex in self.townhalls] existing_pylons = [ pylon.position for pylon in self.structures(UnitTypeId.PYLON) ] acceptable_positions = [ p for p in list_flatten( [p.pylon_positions for p in self.plans.values()]) if p not in existing_pylons ] random.shuffle(acceptable_positions) # Move god pylons to the front - minimizes POOR PLANNING issues, and gives all bases pylons for i in range(len(acceptable_positions)): if any(acceptable_positions[i] - base_location in god_pylons for base_location in base_locations): acceptable_positions.insert(0, acceptable_positions.pop(i)) return [ p for p in acceptable_positions if not self.structures.closer_than(1.0, p).exists ]
def get_mineable_nodes(self): return list_flatten([ # intentionally includes expansions in progress self.mineral_field.closer_than(15, th) for th in self.townhalls ])
def distribute_workers(self): # Only do anything once every 3 seconds if self.time - self.last_distribute < 3: return self.last_distribute = self.time # Kinda hard to gather anything without a base if not self.townhalls.ready.exists: return # mineral patches near one of our bases acceptable_minerals = self.mineral_field.filter(lambda node: any([ nex.position.is_closer_than(15, node.position) for nex in self.townhalls.ready ])) workers_per_gas = 1 + min( 2, int(self.workers.amount / acceptable_minerals.amount)) if self.minerals < 50 and self.vespene > 300: workers_per_gas -= 1 # gas buildings probably at bases that have been destroyed bad_geysers = self.structures( self.shared.gas_structure).filter(lambda a: all( ex.is_further_than(15, a) for ex in self.owned_expansions.keys( )) or a.vespene_contents == 0 or a.assigned_harvesters > workers_per_gas) # gas buildings that don't have enough harvesters needy_geysers = self.structures( self.shared.gas_structure).ready.tags_not_in([ a.tag for a in bad_geysers ]).filter(lambda a: a.assigned_harvesters < workers_per_gas) # tag collections for easy selection and matching acceptable_mineral_tags = [f.tag for f in acceptable_minerals] needy_mineral_tags = [ f.tag for f in acceptable_minerals if self.townhalls.closest_to(f.position).surplus_harvesters < 0 ] # anywhere else is strictly forbidden unacceptable_mineral_tags = [ f.tag for f in self.mineral_field.tags_not_in(acceptable_mineral_tags) ] bad_workers = self.unallocated(self.shared.worker_types).filter( lambda p: # Grab these suckers first p.is_idle or (p.is_gathering and p.orders[0].target in unacceptable_mineral_tags) or (p.is_gathering and p.orders[0].target in bad_geysers) or p.orders[ 0].ability.id in [ AbilityId.ATTACK_ATTACKTOWARDS, AbilityId.ATTACK_ATTACK, AbilityId.ATTACK ]) # up to N workers, where N is the number of surplus harvesters, from each base where there are any # may not grab them all every time (it gets only the ones returning minerals), but it'll get enough excess_workers = Units( list_flatten([ self.workers.filter(lambda w: w.is_carrying_minerals and w. orders and w.orders[0].target == base.tag) [0:base.surplus_harvesters] for base in self.townhalls.filter( lambda base: base.surplus_harvesters > 0) ]), self) # to fill up your first gas building, you'll need these mining_workers = self.workers.filter( lambda p: # if more are needed, this is okay too p.is_gathering and (p.orders[0].target in acceptable_mineral_tags or p.is_carrying_minerals)) - (bad_workers + excess_workers) usable_workers = bad_workers + excess_workers + mining_workers taken_workers = 0 def get_workers(num): nonlocal taken_workers if taken_workers + num > usable_workers.amount: return [] taken_workers += num return usable_workers[taken_workers - num:taken_workers] for needy_geyser in needy_geysers: workers = get_workers(workers_per_gas - needy_geyser.assigned_harvesters) for worker in workers: self.do(worker.gather(needy_geyser)) if taken_workers < bad_workers.amount and acceptable_mineral_tags: remaining_bad_workers = get_workers(bad_workers.amount - taken_workers) for worker in remaining_bad_workers: self.do( worker.gather( self.mineral_field.tags_in( acceptable_mineral_tags).random)) if taken_workers < bad_workers.amount + excess_workers.amount and needy_mineral_tags: remaining_excess_workers = get_workers(bad_workers.amount + excess_workers.amount - taken_workers) for worker in remaining_excess_workers: self.do( worker.gather( self.mineral_field.tags_in(needy_mineral_tags).random))