async def on_step(self, iteration): if not self.version_reported: self.version_reported = True repo = Repo(search_parent_directories=True) if not repo.is_dirty(): sha = repo.head.object.hexsha await self.chat_send("ModuBot verified hash: " + sha[0:10]) await self.chat_send("(glhf)(cake)(sc2)") if self.time < 120: return if self.highest_optimism_reported < 10 and self.shared.optimism > 10: self.highest_optimism_reported = 10 await self.chat_send("-- Enemy contained --") if self.highest_optimism_reported < 50 and self.shared.optimism > 50: self.highest_optimism_reported = 50 await self.chat_send("-- Victory confidence 99% --") enemy_fighters = self.enemy_units.filter(lambda u: not is_worker(u)) if enemy_fighters.amount > 10: if self.lowest_optimism_reported > 0.3 and self.shared.optimism < 0.3: self.lowest_optimism_reported = 0.3 await self.chat_send("whoa... (scared) ") if self.lowest_optimism_reported > 0.15 and self.shared.optimism < 0.15: self.lowest_optimism_reported = 0.15 await self.chat_send("this is not good. (salty)")
async def on_step(self, iteration): if self.surrender_declared and self.time - self.surrender_declared > 5: await self._client.leave() raise SurrenderedException("Surrendered") self.shared.optimism = optimism( self.units.ready.filter( lambda u: not is_worker(u) and not u.is_structure), (u for u in self.shared.known_enemy_units.values() if not is_worker(u) and not u.is_structure)) if self.shared.optimism < 0.02 and not self.surrender_declared: self.surrender_declared = self.time await self.chat_send("(gameheart)(gg)(gameheart)") for unit in self.enemy_units: self.shared.known_enemy_units[unit.tag] = unit
def minimum_supply(self, enemy_units): #everyone...! but no less supply than we know we'll be facing return max( sum(supply_cost(u) for u in enemy_units), sum( supply_cost(u) for u in (self.units.tags_in(self.allocated) + self.unallocated(urgency=self.urgency).filter( lambda u: not is_worker(u)))))
async def on_step(self, iteration): if self.last_camera_move < self.time - 2: self.last_camera_move = self.time # if we're defending if self.shared.threats.amount > 1: await self._client.move_camera( self.shared.threats.closest_to(self.shared.threats.center)) return # if we're attacking if self.shared.attackers and any( unit.position.is_closer_than(10, enemy) for (unit, enemy) in itertools.product( self.shared.attackers, self.shared.victims)): await self._client.move_camera( self.shared.attackers.closest_to( self.shared.victims.center)) return # if we're building a new base if self.already_pending( self.shared.new_base) > 0 and self.structures( self.shared.new_base).not_ready.empty: await self._client.move_camera(self.shared.next_base_location) return # if we can see more than half their army enemy_army_size = len([ u for u in self.shared.known_enemy_units.values() if not is_worker(u) ]) if self.enemy_units.amount > enemy_army_size / 2: await self._client.move_camera( self.enemy_units.closest_to(self.enemy_units.center)) return if self.shared.scouts.exists: interesting_scouts = self.shared.scouts.filter( lambda scout: (self.enemy_units + self.enemy_structures ).closer_than(8, scout.position).amount > 1) if interesting_scouts.exists: await self._client.move_camera( interesting_scouts.first.position) return def energy_amount(base): return base.energy await self._client.move_camera( self.shared.rally_point if self.shared.rally_point and self.units.closer_than(10, self.shared.rally_point).amount > 2 else max(self.townhalls, key=energy_amount, default=self.start_location))
def __init__(self, bot, unit_priority=[], retreat_while=lambda scout: False, start_when=None): if not start_when: start_when = lambda: self.units.filter(lambda u: not is_worker(u) ).exists super().__init__(bot, unit_priority, retreat_while) self.static_targets = False
def allocate(self): may_proceed = False enemy_units = self.enemies minimum_supply = int(self.minimum_supply(enemy_units)) optimum_supply = int(self.optimum_supply(enemy_units)) allocated_supply = int( sum(supply_cost(u) for u in self.units.tags_in(self.allocated))) if minimum_supply <= allocated_supply: may_proceed = True still_needed = minimum_supply - allocated_supply still_wanted = max(optimum_supply - allocated_supply, still_needed) usable_units = self.unallocated( urgency=self.urgency).filter(lambda u: not is_worker(u)) if enemy_units.filter(lambda e: not e.is_flying).empty: usable_units = usable_units.filter(lambda u: u.can_attack_air) preferred_units = usable_units.filter( lambda u: u.can_attack_both ) if usable_units.exists else usable_units adding_units = set() if sum(supply_cost(u) for u in preferred_units) >= still_needed: # still_wanted is actually supply, not units, so this will over-select for protoss and under-select for zerg adding_units = set(unit.tag for unit in preferred_units.closest_n_units( self.target.position, still_wanted)) elif sum(supply_cost(u) for u in usable_units) >= still_needed: adding_units = set(preferred_units) adding_units.update((unit.tag for unit in usable_units.closest_n_units( self.target.position, still_wanted))) self.deallocate(adding_units) self.allocated = self.allocated.union(adding_units) if sum(supply_cost(u) for u in self.units.tags_in(self.allocated)) >= minimum_supply: may_proceed = True if may_proceed: if self.status == ObjectiveStatus.ALLOCATING: self.status = ObjectiveStatus.STAGING self.status_since = self.time elif self.status == ObjectiveStatus.RETREATING and \ self.shared.optimism > 1.5 and \ self.units.exists and \ self.units.closer_than(15, median_position([u.position for u in self.units])).amount > self.units.amount / 2: self.status = ObjectiveStatus.STAGING self.status_since = self.time self.units = self.bot.units.tags_in(self.allocated) if len(adding_units) > 0: self.log.debug({ "message": "Allocating units", "quantity": len(adding_units), "now_allocated": len(self.allocated), }) # noisy, but possibly informative # self.log.info(f"{self.units.amount} units allocated for {self.enemies.amount} known enemies") return
def optimum_supply(self, enemy_units): return sum( supply_cost(unit) for unit in self.bot.units.filter( lambda u: u.ground_dps + u.air_dps > 5 and not is_worker(u)))
def optimum_supply(self, enemy_units): return sum( supply_cost(u) for u in self.units.filter(lambda u: not is_worker(u)))
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 maybe_expand(self, nodes, gas_structs): requests = [] # this does not have to happen very often if not self.shared.next_base_location or self.time - self.last_base_check > 11: self.last_base_check = self.time if (not self.shared.next_base_location or any( base.position.is_closer_than( 1, self.shared.next_base_location) for base in self.townhalls + self.enemy_structures(BaseStructures))): self.shared.next_base_location = self.find_next_base() if not self.shared.next_base_location: return requests destructables = self.destructables.filter( lambda d: d.position.is_closer_than(1.0, self.shared. next_base_location)) if destructables.exists: for unit in self.units.filter(lambda u: not is_worker(u)).idle: self.do(unit.attack(destructables.first)) for unit in self.units.closer_than(5, self.shared.next_base_location): # apparently, when a probe warps in a building, they become idle *before* the building has started warping if unit.is_idle and unit.type_id != self.shared.common_worker: self.do( unit.move( self.shared.next_base_location.towards( self.game_info.map_center, 10))) base_urgency = Urgency.NONE mineable = self.sum_mineral_contents(nodes) if not self.already_pending(self.shared.new_base): # don't count bases at start locations, because we know they have one, whether we've seen it or not enemy_bases = self.enemy_structures(BaseStructures).filter( lambda base: base.position not in self.enemy_start_locations) # if they're out-expanding us base_urgency = 1 + enemy_bases.amount - self.townhalls.amount total_desired_harvesters = len(nodes) * 2 + gas_structs.filter( lambda a: a.vespene_contents > 0).amount * 3 if self.race == Race.Zerg: base_urgency += 1 if self.shared.optimism > 1.1: base_urgency += 1 # if the enemy has any bases apart from the main... we got this if enemy_bases.amount > 0: base_urgency += 1 # if we're really running out of things to do if self.workers.amount >= total_desired_harvesters - 3: base_urgency += 1 # if we're down to about one base if len(nodes) < 10: base_urgency += 1 # if we're running out of minerals if mineable < 4000: base_urgency += 1 if self.townhalls.amount == 1 and self.fast_expand: base_urgency += 4 requests.append( BuildRequest(self.shared.new_base, urgency=base_urgency, force_target=self.shared.next_base_location)) return requests