def avoid_effects(self, unit): """Dodge any effects""" if not self.main.state.effects or unit.type_id == UnitTypeId.ULTRALISK: return False effects_radius = { EffectId.PSISTORMPERSISTENT: 1.5, EffectId.THERMALLANCESFORWARD: 0.3, EffectId.NUKEPERSISTENT: 8, EffectId.BLINDINGCLOUDCP: 2, EffectId.RAVAGERCORROSIVEBILECP: 0.5, EffectId.LURKERMP: 0.3, } # Exchange it for '.radius' when the data gets implemented ignored_effects = ( EffectId.SCANNERSWEEP, EffectId.GUARDIANSHIELDPERSISTENT, EffectId.LIBERATORTARGETMORPHDELAYPERSISTENT, EffectId.LIBERATORTARGETMORPHPERSISTENT, ) # Placeholder(must find better way to handle some of these) for effect in self.main.state.effects: if effect.id in ignored_effects: continue danger_zone = effects_radius[effect.id] + unit.radius + 0.4 if unit.position.distance_to_closest(effect.positions) > danger_zone: break perimeter_of_effect = Point2.center(effect.positions).furthest(list(unit.position.neighbors8)) self.main.add_action(unit.move(perimeter_of_effect.towards(unit.position, -danger_zone))) return True return False
def should_retreat(self, unit: Unit) -> bool: if unit.shield_max + unit.health_max > 0: health_percentage = (unit.shield + unit.health) / (unit.shield_max + unit.health_max) else: health_percentage = 0 if health_percentage < 0.2: # or unit.weapon_cooldown < 0: # low hp or unit can't attack return True for effect in self.ai.state.effects: if effect.id == EffectId.RAVAGERCORROSIVEBILECP: if Point2.center(effect.positions).distance_to(unit) < 3: return True if effect.id == EffectId.BLINDINGCLOUDCP: if Point2.center(effect.positions).distance_to(unit) < 4: return True if effect.id == EffectId.PSISTORMPERSISTENT: if Point2.center(effect.positions).distance_to(unit) < 4: return True return False
def is_revealed_by_enemy(self, dt: Unit) -> bool: detectors = self.knowledge.unit_cache.enemy_in_range(dt.position, 20) \ .filter(lambda x: (x.detect_range - 1) > dt.distance_to(x.position)) if detectors.exists: if detectors.filter(lambda x: x.is_flying).exists: self.enemy_air_detector = True return True for effect in self.ai.state.effects: if effect.id == EffectId.SCANNERSWEEP: if Point2.center(effect.positions).distance_to( dt.position) < 15: return True return False
async def DefendMainBase(self): close_enemies = self.known_enemy_units.closer_than( 50, self.start_location) if close_enemies.amount > 3: enemy_center = Point2.center( [enemy.position for enemy in close_enemies]) defensors = self.belicUnits defensors = defensors.sorted(lambda unit: unit.distance_to( enemy_center)).take(close_enemies.amount + 10) await self.do_actions( [defensor.attack(enemy_center) for defensor in defensors])
async def on_step(self, iteration): self.shared.attackers = self.units.tags_in(self.allocated) if self.attack_objective: await self.attack_objective.tick() if self.attack_objective.is_complete(): self.shared.victims = Units([], self.bot) self.attack_objective = None else: self.shared.victims = self.attack_objective.enemies return # for cleanup in self.cleanup_objectives: # await cleanup.tick() # if cleanup.is_complete(): # self.shared.victims = Units([], self.bot) # self.cleanup_objectives.remove(cleanup) if (self.supply_used > 196 or self.shared.optimism > 1.5) and not self.attack_objective: known_enemy_units = self.shared.known_enemy_units.values() enemy_bases = self.enemy_structures(BaseStructures) if enemy_bases.exists: self.attack_objective = AttackObjective( self, enemy_bases.furthest_to( Point2.center([u.position for u in known_enemy_units] ) if known_enemy_units else self. enemy_start_locations[0]).position) elif self.enemy_structures.exists: self.attack_objective = AttackObjective( self, self.enemy_structures.closest_to( self.units.center).position) else: self.attack_objective = AttackObjective( self, self.enemy_start_locations[0])
def dodge_effects(self, unit: Unit) -> bool: """Dodge any effects""" if not self.ai.state.effects: return False for effect in self.ai.state.effects: if effect.id == GUARDIANSHIELDPERSISTENT: continue effect_data = self.ai.game_data.effects[effect.id] danger_zone = effect_data.radius + unit.radius + .1 closest_effect_position_to_unit = unit.position.closest( effect.positions) if not unit.position.distance_to_point2( closest_effect_position_to_unit) < danger_zone: continue neighbors8_of_unit = list(unit.position.neighbors8) center_of_effect = Point2.center(effect.positions) furthest_neighbor_to_effect = center_of_effect.furthest( neighbors8_of_unit) move_away = -1 * danger_zone self.ai.add_action( unit.move( furthest_neighbor_to_effect.towards( unit.position, move_away))) return True
async def update_influence(self): power = ExtendedPower(self.unit_values) self.path_finder_terrain.reset() # Reset self.path_finder_ground.reset() # Reset positions = [] for mf in self.ai.mineral_field: # type: Unit # In 4.8.5+ minerals are no linger visible in pathing grid positions.append(mf.position) # for mf in self.ai.mineral_walls: # type: Unit # # In 4.8.5+ minerals are no linger visible in pathing grid # positions.append(mf.position) self.path_finder_terrain.create_block(positions, (2, 1)) self.path_finder_ground.create_block(positions, (2, 1)) self.set_rocks(self.path_finder_terrain) self.set_rocks(self.path_finder_ground) for building in self.ai.structures + self.knowledge.known_enemy_structures: # type: Unit if building.type_id in buildings_2x2: self.path_finder_ground.create_block(building.position, (2, 2)) elif building.type_id in buildings_3x3: self.path_finder_ground.create_block(building.position, (3, 3)) elif building.type_id in buildings_5x5: self.path_finder_ground.create_block(building.position, (5, 3)) self.path_finder_ground.create_block(building.position, (3, 5)) self.set_rocks(self.path_finder_ground) self.path_finder_ground.normalize_influence(20) self.path_finder_air.normalize_influence(20) for enemy_type in self.cache.enemy_unit_cache: # type: UnitTypeId enemies: Units = self.cache.enemy_unit_cache.get( enemy_type, Units([], self.ai)) if len(enemies) == 0: continue example_enemy: Unit = enemies[0] power.clear() power.add_unit(enemy_type, 100) if self.unit_values.can_shoot_air(example_enemy): positions: List[Point2] = map( lambda u: u.position, enemies) # need to be specified in both places s_range = self.unit_values.air_range(example_enemy) if example_enemy.type_id == UnitTypeId.CYCLONE: s_range = 7 self.path_finder_air.add_influence(positions, power.air_power, s_range + 3) if self.unit_values.can_shoot_ground(example_enemy): positions = map(lambda u: u.position, enemies) # need to be specified in both places s_range = self.unit_values.ground_range(example_enemy) if example_enemy.type_id == UnitTypeId.CYCLONE: s_range = 7 if s_range < 2: self.path_finder_ground.add_influence_walk( positions, power.ground_power, 7) elif s_range < 5: self.path_finder_ground.add_influence_walk( positions, power.ground_power, 7) else: self.path_finder_ground.add_influence( positions, power.ground_power, s_range + 3) # influence, radius, points, can it hit air? effect_dict: Dict[EffectId, Tuple[float, float, List[Point2], bool]] = dict() for effect in self.ai.state.effects: values: Tuple[float, float, List[Point2], bool] = None if effect.id == EffectId.RAVAGERCORROSIVEBILECP: values = effect_dict.get(effect.id, (1000, 2.5, [], True)) values[2].append(Point2.center(effect.positions)) elif effect.id == EffectId.BLINDINGCLOUDCP: values = effect_dict.get(effect.id, (400, 3.5, [], False)) values[2].append(Point2.center(effect.positions)) elif effect.id == EffectId.NUKEPERSISTENT: values = effect_dict.get(effect.id, (900, 9, [], True)) values[2].append(Point2.center(effect.positions)) elif effect.id == EffectId.PSISTORMPERSISTENT: values = effect_dict.get(effect.id, (300, 3.5, [], True)) values[2].append(Point2.center(effect.positions)) elif effect.id == EffectId.LIBERATORTARGETMORPHDELAYPERSISTENT: values = effect_dict.get(effect.id, (200, 6, [], False)) values[2].append(Point2.center(effect.positions)) elif effect.id == EffectId.LIBERATORTARGETMORPHPERSISTENT: values = effect_dict.get(effect.id, (300, 6, [], False)) values[2].append(Point2.center(effect.positions)) elif effect.id == EffectId.LURKERMP: # Each lurker spine deals splash damage to a radius of 0.5 values = effect_dict.get(effect.id, (1000, 1, [], False)) values[2].extend(effect.positions) if values is not None and effect.id not in effect_dict: effect_dict[effect.id] = values for effects in effect_dict.values(): if effects[3]: self.path_finder_air.add_influence(effects[2], effects[0], effects[1]) self.path_finder_ground.add_influence(effects[2], effects[0], effects[1])
def bases_centroid(self): return Point2.center([base.position for base in self.townhalls])
def resource_centroid(self, townhall): nodes = self.mineral_field.closer_than(15, townhall) if nodes.exists: return Point2.center([r.position for r in nodes]) else: return self.game_info.map_center
async def on_enemy_unit_left_vision(self, unit_tag: int): last_known_unit = self._enemy_units_previous_map.get(unit_tag, None) self.fogged_queue.append(last_known_unit.position) if len(self.fogged_queue) > 7: self.fogged_units.append(Point2.center(self.fogged_queue)) self.fogged_queue = []
async def attack(self): """ Covers military AI, from the moment unit is recruited into defense, into staging to offense and retreat, and cycle. """ global defense, offense, staging, retreat global staging_loc, isStaging forceMin = 4 + (self.time / 30) #Add all new units to defense force first for unit in (units for units in self.getAllUnits() if not units in defense and \ not units in offense and not units in staging and not units in retreat): defense.append(unit) await self.do(unit.move(self.next_node)) if defense: defense = list(units for units in self.getAllUnits() if units in defense) if staging: staging = list(units for units in self.getAllUnits() if units in staging) if offense: offense = list(units for units in self.getAllUnits() if units in offense) #If defense force is big enough to attack, attack if len(defense) > forceMin: staging += defense defense = [] for unit in staging: await self.do(unit.attack(staging_loc)) print("D>S Defense:", len(defense), "|Staging:", len(staging), "|Offense:", len(offense), "|ForceMin:", forceMin) elif defense: for unit in defense: if staging and len(staging) < forceMin: staging += defense defense = [] await self.do(unit.attack(staging_loc)) elif unit.is_idle and unit.position.distance_to( self.next_node) > 5: await self.do(unit.attack(self.next_node)) #Staging force until units are grouped together # OPTIMIZE: Need to organify the max radius of staging->offense if len(staging) >= forceMin: #p=[] #for unit in (units for units in self.getAllUnits() if units in staging): #p.append(unit.position) p = list(units.position for units in staging) if Point2.center(p).distance_to_furthest(p) < 8: target = self.find_enemy(self.state) offense += staging staging = [] for unit in offense: if unit in self.units(MEDIVAC) and unit.is_idle: await self.do( unit.attack(random.choice(offense).position)) elif unit.is_idle: await self.do(unit.attack(target)) print("S>O Defense:", len(defense), "|Staging:", len(staging), "|Offense:", len(offense), "|ForceMin:", forceMin) else: for unit in staging: if unit.is_idle and unit.position.distance_to( staging_loc) > 5: await self.do(unit.attack(staging_loc)) if offense: #Retreat if offense too weak if len(offense) < forceMin / 4: retreat = offense offense = [] for unit in retreat: await self.do(unit.move(self.next_node)) defense += retreat retreat = [] print("O>D Defense:", len(defense), "|Staging:", len(staging), "|Offense:", len(offense), "|ForceMin:", forceMin) #If part of/offense loses/kills target, retarget else: target = self.find_enemy(self.state) for unit in offense: if unit in self.units(MEDIVAC) and unit.is_idle: await self.do( unit.attack(random.choice(offense).position)) elif unit.is_idle: await self.do(unit.attack(target))
async def ArmiesMacro(self): if self.belicUnits.amount > 60 and (self.time - self.lastAttack > 5): self.lastAttack = self.time all_positions = [ item.position for sublist in [self.known_enemy_units, self.known_enemy_structures] for item in sublist ] attack_position = Point2.center(all_positions + [self.enemy_start_locations[0]]) enemy_struct = None enemy_unit = None try: enemy_struct = self.known_enemy_structures.closest_to( attack_position) except: pass try: enemy_unit = self.known_enemy_units.closest_to(attack_position) except: pass if enemy_unit is None and enemy_struct is None: actions = [ marine.attack(attack_position) for marine in self.belicUnits if not marine.is_attacking ] elif enemy_unit is None: actions = [ marine.attack(enemy_struct.position) for marine in self.belicUnits if not marine.is_attacking ] elif enemy_struct is None: actions = [ marine.attack(enemy_unit.position) for marine in self.belicUnits if not marine.is_attacking ] else: if enemy_unit.position.distance_to( attack_position) < enemy_struct.position.distance_to( attack_position): actions = [ marine.attack(enemy_unit.position) for marine in self.belicUnits if not marine.is_attacking ] else: actions = [ marine.attack(enemy_struct.position) for marine in self.belicUnits if not marine.is_attacking ] await self.do_actions(actions) #make medivac follow marines medivacs = self.units(MEDIVAC).idle if self.belicUnits.amount == 0: await self.do_actions( [medivac.move(self.start_location) for medivac in medivacs]) else: actions = [ medivac.move( self.belicUnits.sorted( lambda unit: unit.health + medivac.position. distance_to(unit.position)).first.position) for medivac in medivacs ] await self.do_actions(actions)