def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: # before death if unit.shield_health_percentage <= 0.3: if self.cd_manager.is_ready(unit.tag, AbilityId.FEEDBACK_FEEDBACK): feedback_enemies = self.cache.enemy_in_range( unit.position, 10).filter(lambda u: u.energy_percentage > 0.5 and not u.is_structure) if feedback_enemies: closest = feedback_enemies.closest_to(unit) return Action(closest, False, AbilityId.FEEDBACK_FEEDBACK) if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command # center range = 9 and radius ~= 2 if self.cd_manager.is_ready(unit.tag, AbilityId.PSISTORM_PSISTORM): enemies = self.cache.enemy_in_range(unit.position, 12).not_structure target = enemies.filter(lambda u: not u.has_buff(BuffId.PSISTORM)) if len(target) >= 5: center = target.center return Action(center, False, AbilityId.PSISTORM_PSISTORM) if self.cd_manager.is_ready(unit.tag, AbilityId.FEEDBACK_FEEDBACK): feedback_enemies = self.cache.enemy_in_range( unit.position, 11).filter( lambda u: u.energy_percentage > 0.5 and not u.is_structure) if feedback_enemies: closest = feedback_enemies.closest_to(unit) return Action(closest, False, AbilityId.FEEDBACK_FEEDBACK) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.closest_group and self.engaged_power.melee_percentage > 0.9: backstep: Point2 = unit.position.towards(self.closest_group.center, -3) if (unit.health + unit.shield <= 5 and not self.ready_to_shoot(unit)) or ( unit.shield_health_percentage < 0.5 and unit.weapon_cooldown > 9): backstep = self.pather.find_weak_influence_ground(backstep, 4) if self.cache.own_in_range(unit.position, 1) or self.cache.enemy_in_range( unit.position, 1): # Mineral walk angle = sc2math.line_angle(unit.position, backstep) best_angle = sc2math.pi / 6 best_mf = None for mf in self.ai.mineral_field: # type: Unit new_angle = sc2math.line_angle(unit.position, mf.position) angle_distance = sc2math.angle_distance( angle, new_angle) if angle_distance < best_angle: best_mf = mf best_angle = angle_distance if best_mf: # Use backstep with gather command to pass through own units return Action(best_mf, False, ability=AbilityId.HARVEST_GATHER) return Action(backstep, False) if self.ready_to_shoot(unit): if self.closest_group: current = Action(self.closest_group.center, True) else: current = Action(current_command.target, True) return self.melee_focus_fire(unit, current) elif self.knowledge.enemy_race == Race.Terran: # Kill scv building nearby = self.cache.enemy_in_range(unit.position, 5) scvs = nearby(UnitTypeId.SCV) if scvs and nearby.structure.not_ready: scv = scvs.closest_to(unit) current_command = Action(scv, True) # if unit.health + unit.shield <= 5: # backstep = self.pather.find_weak_influence_ground(backstep, 4) # return Action(backstep, False) # if self.knowledge.enemy_race == Race.Protoss: # if self.engage_percentage < 0.25: # buildings = self.enemies_near_by.sorted_by_distance_to(unit) # if buildings: # if buildings.first.health + buildings.first.shield < 200: # return Action(buildings.first, True) # pylons = buildings(UnitTypeId.PYLON) # if pylons: # return Action(buildings.first, True) return current_command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command if self.move_type in {MoveType.PanicRetreat, MoveType.DefensiveRetreat}: return current_command if self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_VOIDRAYPRISMATICALIGNMENT): close_enemies = self.cache.enemy_in_range(unit.position, 9).filter(lambda u: u.is_armored) if close_enemies: return Action(None, False, AbilityId.EFFECT_VOIDRAYPRISMATICALIGNMENT) shoot = self.should_shoot(unit) if not shoot: if self.should_retreat(unit): pos = self.pather.find_weak_influence_air(unit.position, 4) return Action(pos, False) current_command = self.focus_fire(unit, current_command, None) # if not shoot: # if self.engaged_power.air_power < 1: # if unit.distance_to(current_command.target) > 2: # return Action(current_command.target.position, False) return current_command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command if self.move_type in {MoveType.PanicRetreat, MoveType.DefensiveRetreat}: return current_command closest = self.closest_units.get(unit.tag) if not closest or closest.distance_to(unit) > 14: # not in combat, follow the army return current_command if unit.energy < 75: focus = self.group.center best_position = self.pather.find_weak_influence_ground(focus, 6) return Action(best_position, False) if self.cd_manager.is_ready(unit.tag, AbilityId.NEURALPARASITE_NEURALPARASITE): shuffler = unit.tag % 10 best_score = 300 target: Optional[Unit] = None enemy: Unit for enemy in self.enemies_near_by: d = enemy.distance_to(unit) if d < 11 and self.unit_values.power(enemy) > 1 and not enemy.has_buff(BuffId.NEURALPARASITE): score = enemy.health + self.unit_values.power(enemy) * 50 # TODO: Needs proper target locking in order to not fire at the same target # Simple and stupid way in an attempt to not use ability on same target: score += (enemy.tag % (shuffler + 2)) if score > best_score: target = enemy best_score = score if target is not None: return Action(target, False, AbilityId.NEURALPARASITE_NEURALPARASITE) if (self.aoe_available < self.ai.time and self.cd_manager.is_ready(unit.tag, AbilityId.FUNGALGROWTH_FUNGALGROWTH) and self.engaged_power.power > 4 ): best_score = 2 target: Optional[Unit] = None enemy: Unit for enemy in self.enemies_near_by: d = enemy.distance_to(unit) if d < 11 and self.unit_values.power(enemy) > 0.5 and not enemy.has_buff(BuffId.FUNGALGROWTH): score = self.cache.enemy_in_range(enemy.position, 2).amount if score > best_score: target = enemy best_score = score if target is not None: self.aoe_available = self.ai.time + 2 return Action(target.position, False, AbilityId.FUNGALGROWTH_FUNGALGROWTH) return self.stay_safe(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command if self.move_type in {MoveType.PanicRetreat, MoveType.DefensiveRetreat}: return current_command if unit.energy < 50: focus = self.group.center best_position = self.pather.find_weak_influence_air(focus, 6) return Action(best_position, False) if self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_INTERFERENCEMATRIX): shuffler = unit.tag % 10 best_score = 300 target: Optional[Unit] = None enemy: Unit for enemy in self.enemies_near_by: d = enemy.distance_to(unit) if d < 11 and self.unit_values.power(enemy) > 1 and not enemy.has_buff(BuffId.RAVENSCRAMBLERMISSILE): score = enemy.health + self.unit_values.power(enemy) * 50 # TODO: Needs proper target locking in order to not fire at the same target # Simple and stupid way in an attempt to not use ability on same target: score += enemy.tag % (shuffler + 2) if score > best_score: target = enemy best_score = score if target is not None: return Action(target, False, AbilityId.EFFECT_INTERFERENCEMATRIX) if ( self.anti_armor_available < self.ai.time and self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_ANTIARMORMISSILE) and self.engaged_power.power > 10 ): best_score = 5 target: Optional[Unit] = None enemy: Unit for enemy in self.enemies_near_by: d = enemy.distance_to(unit) if ( d < 11 and self.unit_values.power(enemy) > 1 and not enemy.has_buff(BuffId.RAVENSHREDDERMISSILEARMORREDUCTION) ): score = self.cache.enemy_in_range(enemy.position, 5).amount if score > best_score: target = enemy best_score = score if target is not None: self.anti_armor_available = self.ai.time + 3 return Action(target, False, AbilityId.EFFECT_ANTIARMORMISSILE) return current_command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command if self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_BLINK_STALKER): if self.is_locked_on(unit): cyclones = self.enemies_near_by(UnitTypeId.CYCLONE) if cyclones: closest_cyclone = cyclones.closest_to(unit) backstep: Point2 = closest_cyclone.position.towards(unit.position, 15) backstep = self.pather.find_weak_influence_ground(backstep, 4) return Action(backstep, False, AbilityId.EFFECT_BLINK_STALKER) if self.model == CombatModel.StalkerToSiege and ( self.move_type == MoveType.Assault or self.move_type == MoveType.SearchAndDestroy ): siege_units = self.enemies_near_by.of_type(siege) if siege_units: target = siege_units.closest_to(unit) if target.distance_to(unit) > 6: return Action(target.position, False, AbilityId.EFFECT_BLINK_STALKER) if unit.shield_percentage < 0.05: # Blink to safety. target_pos = unit.position if self.closest_group: target_pos = target_pos.towards(self.closest_group.center, -5) target = self.pather.find_weak_influence_ground_blink(target_pos, 6) if target.distance_to(unit) > 3: return Action(target, False, AbilityId.EFFECT_BLINK_STALKER) return super().unit_solve_combat(unit, current_command)
def prism_evasive_move_to(self, harass_prism, position_to): position = harass_prism.position3d enemy_anti_air_structure = self.knowledge.unit_cache.enemy_in_range(harass_prism.position3d, 13) \ .of_type(UnitTypeId.BUNKER) enemy_anti_air_units = self.knowledge.unit_cache.enemy_in_range(harass_prism.position3d, 13) \ .filter(lambda unit: unit.can_attack_air) if harass_prism.has_buff(BuffId.LOCKON): cyclones = self.knowledge.unit_cache.enemy_in_range( harass_prism.position3d, 20).of_type(UnitTypeId.CYCLONE) if cyclones: closest_cyclone = cyclones.closest_to(harass_prism) position = position.towards(closest_cyclone, -18) if enemy_anti_air_units.exists or enemy_anti_air_structure.exists: for aa in enemy_anti_air_units: distance = harass_prism.distance_to(aa.position3d) amount_of_evade = 15 - distance position = position.towards(aa, -amount_of_evade) for aa in enemy_anti_air_structure: distance = harass_prism.distance_to(aa.position3d) amount_of_evade = 15 - distance position = position.towards(aa, -amount_of_evade) # after the for loop, position is the best vector away from enemy distance_to_best_evade_point = 3 should_go = position.towards(position_to, distance_to_best_evade_point) return Action(should_go, False) else: return Action(position_to, False)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: """Micro individual units.""" if self.move_type == MoveType.DefensiveRetreat: return current_command closest = self.closest_units.get(unit.tag) if not closest or closest.distance_to(unit) > HOST_RANGE: # not in combat, follow the army return current_command relevant_enemies = self.cache.enemy_in_range(unit.position, HOST_RANGE) if len(relevant_enemies) < 2: return self.stay_safe(unit, current_command) center = relevant_enemies.center if self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_SPAWNLOCUSTS): distance = self.pather.walk_distance(unit.position, center) if distance < HOST_RANGE: return Action( center, False, AbilityId.EFFECT_SPAWNLOCUSTS, debug_comment="Spawning Locusts", ) else: return Action(center, False) return self.stay_safe(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.model == CombatModel.StalkerToSiege and (self.move_type in { MoveType.SearchAndDestroy, MoveType.Assault, MoveType.Push, MoveType.ReGroup, MoveType.DefensiveRetreat, MoveType.ReGroup }): siege_units = self.enemies_near_by.of_type(siege).filter( lambda u: (not u.is_flying) and u.is_armored) if siege_units: target = siege_units.closest_to(unit) if self.ready_to_shoot(unit) and current_command.is_attack: return Action(target, True) if self.move_type in { MoveType.SearchAndDestroy, MoveType.Assault, MoveType.Push, MoveType.ReGroup, MoveType.DefensiveRetreat, MoveType.ReGroup }: enemy_ground_force_armored = self.knowledge.unit_cache.enemy_in_range(unit.position3d, 10) \ .filter(lambda u: u.is_armored and not u.is_flying and not u.is_structure) if self.ready_to_shoot( unit ) and current_command.is_attack and enemy_ground_force_armored.exists: return Action(enemy_ground_force_armored.closest_to(unit), True) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.move_type == MoveType.DefensiveRetreat: return current_command closest = self.closest_units.get(unit.tag) if not closest or closest.distance_to(unit) > 14: # not in combat, follow the army return current_command time = self.knowledge.ai.time relevant_enemies = self.cache.enemy_in_range( unit.position, 14).not_structure.not_flying if time < self.last_used_any + INTERVAL or len(relevant_enemies) < 4: return self.stay_safe(unit, current_command) center = relevant_enemies.center if self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_PURIFICATIONNOVA): distance = self.pather.walk_distance(unit.position, center) if distance < 9: self.last_used_any = time return Action(center, False, AbilityId.EFFECT_PURIFICATIONNOVA) else: return Action(center, False) return self.stay_safe(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: burrow_ready = self.cd_manager.is_ready(unit.tag, AbilityId.BURROWDOWN_ROACH) if unit.is_burrowed and unit.health_percentage > self.burrow_up_percentage: return Action(None, False, AbilityId.BURROWUP_ROACH) if not unit.is_burrowed and unit.health_percentage < self.burrow_down_percentage and burrow_ready: return Action(None, False, AbilityId.BURROWDOWN_ROACH) return super().unit_solve_combat(unit, current_command)
def group_solve_combat(self, units: Units, current_command: Action) -> Action: if self.engage_ratio > 0.5 and self.closest_group: if self.ready_to_attack_ratio > 0.8 or self.closest_group_distance < 2: return Action(self.closest_group.center, True) if self.ready_to_attack_ratio < 0.25: return Action(self.closest_group.center, True) return Action(self.closest_group.center.towards(self.center, -3), False) #if self.engage_percentage == 0 return current_command
def group_solve_combat(self, units: Units, current_command: Action) -> Action: if self.move_type == MoveType.DefensiveRetreat or self.move_type == MoveType.PanicRetreat: return current_command if self.engage_ratio > 0.25 and self.closest_group: if self.ready_to_attack_ratio > 0.25 or self.closest_group_distance < 2: return Action(self.closest_group.center, True) return Action(self.closest_group.center.towards(self.center, -3), False) #if self.engage_percentage == 0 return current_command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.stim_required > 0 and not self.has_stim(unit) and unit.shield_health_percentage > 0.5: if unit.type_id == UnitTypeId.MARAUDER and self.cd_manager.is_ready( unit.tag, AbilityId.EFFECT_STIM_MARAUDER ): self.stim_required -= 2 return Action(None, False, AbilityId.EFFECT_STIM_MARAUDER) elif unit.type_id == UnitTypeId.MARINE and self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_STIM_MARINE): self.stim_required -= 1 return Action(None, False, AbilityId.EFFECT_STIM_MARINE) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if isinstance(current_command.target, Unit): target_pos = current_command.target.position else: target_pos = current_command.target if self.move_type == MoveType.PanicRetreat or self.move_type == MoveType.DefensiveRetreat: if unit.has_buff(BuffId.ORACLEWEAPON): return Action(None, False, AbilityId.BEHAVIOR_PULSARBEAMOFF) target = self.pather.find_influence_air_path( unit.position, target_pos) return Action(target, False) if self.move_type == MoveType.Harass: targets = self.cache.enemy(self.knowledge.enemy_worker_type) if targets: close_to_me = targets.closer_than(8, unit.position) close_to_target = targets.closer_than(10, target_pos) if close_to_me: targets = close_to_me elif close_to_target: targets = close_to_target else: targets = self.cache.enemy_in_range( unit.position, 10).filter(lambda u: u.is_light and not u.is_flying) if targets: closest = targets.closest_to(unit) distance = closest.distance_to(unit) if distance > 40 and unit.has_buff(BuffId.ORACLEWEAPON): return Action(None, False, AbilityId.BEHAVIOR_PULSARBEAMOFF) if distance < 5: if not unit.has_buff(BuffId.ORACLEWEAPON): if unit.energy > 40: return Action(None, False, AbilityId.BEHAVIOR_PULSARBEAMON) else: target = self.pather.find_weak_influence_air( unit.position, 10) return Action(target, False) else: return Action(closest, True) target = self.pather.find_weak_influence_air(closest.position, 10) target = self.pather.find_influence_air_path(unit.position, target) return Action(target, False) else: if unit.has_buff(BuffId.ORACLEWEAPON): return Action(None, False, AbilityId.BEHAVIOR_PULSARBEAMOFF) target = self.pather.find_influence_air_path(unit.position, target_pos) return Action(target, False)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: shuffler = unit.tag % 10 target: Optional[Unit] = None enemy: Unit target = self.get_target(self.enemies_near_by, target, unit, shuffler) shade_tag = self.cd_manager.adept_to_shade.get(unit.tag, None) if shade_tag: shade = self.cache.by_tag(shade_tag) if shade: if target is None: nearby: Units = self.knowledge.unit_cache.enemy_in_range( shade.position, 12) target = self.get_target(nearby, target, shade, shuffler) if target is not None: pos: Point2 = target.position self.ai.do(shade.move(pos.towards(unit, -1))) if self.move_type in {MoveType.SearchAndDestroy, MoveType.Assault } and self.model == CombatModel.RoachToStalker: if self.cd_manager.is_ready( unit.tag, AbilityId.ADEPTPHASESHIFT_ADEPTPHASESHIFT): if target is not None: return Action(target.position, False, AbilityId.ADEPTPHASESHIFT_ADEPTPHASESHIFT) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if isinstance(current_command.target, Unit): target_pos = current_command.target.position else: target_pos = current_command.target target = self.pather.find_path(self.group.center, target_pos, 8) # move ahead of group enemies = self.cache.enemy_in_range(target, 12, False) other_observers = self.cache.own(UnitTypeId.OBSERVER).tags_not_in( [unit.tag]) if other_observers: # Try to keep observers separated from each other closest = other_observers.closest_to(unit) if closest.distance_to(unit) < 5: pos: Point2 = closest.position target = unit.position.towards(pos, -6) # for enemy in enemies: # type: Unit # if enemy.detect_range > 0 and enemy.detect_range > target.distance_to(enemy): # break if enemies: target = self.pather.find_weak_influence_air(target, 10) return Action(target, False)
def focus_fire(step: MicroStep, unit: Unit, current_command: Action, prio: Optional[Dict[UnitTypeId, int]]) -> Action: shoot_air = step.unit_values.can_shoot_air(unit) shoot_ground = step.unit_values.can_shoot_ground(unit) air_range = step.unit_values.air_range(unit) ground_range = step.unit_values.ground_range(unit) lookup = min(air_range + 3, ground_range + 3) enemies = step.cache.enemy_in_range(unit.position, lookup) last_target = step.last_targeted(unit) if not enemies: # No enemies to shoot at return current_command value_func: Callable[[Unit], float] if prio: value_func = ( lambda u: 1 if u.type_id in changelings else prio.get(u.type_id, -1) * (1 - u.shield_health_percentage)) else: value_func = ( lambda u: 1 if u.type_id in changelings else 2 * step.unit_values. power_by_type(u.type_id, 1 - u.shield_health_percentage)) best_target: Optional[Unit] = None best_score: float = 0 for enemy in enemies: # type: Unit if not step.is_target(enemy): continue if not shoot_air and enemy.is_flying: continue if not shoot_ground and not enemy.is_flying: continue pos: Point2 = enemy.position score = value_func(enemy) + (1 - pos.distance_to(unit) / lookup) if enemy.tag == last_target: score += 3 if step.focus_fired.get(enemy.tag, 0) > enemy.health: score *= 0.1 if score > best_score: best_target = enemy best_score = score if best_target: step.focus_fired[best_target.tag] = ( step.focus_fired.get(best_target.tag, 0) + unit.calculate_damage_vs_target(best_target)[0]) return Action(best_target, True) return current_command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: shuffler = unit.tag % 10 if not self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_CORROSIVEBILE): return super().unit_solve_combat(unit, current_command) if self.engaged_power.power > 10: best_score = 0 target: Optional[Unit] = None enemy: Unit for enemy in self.enemies_near_by: d = enemy.distance_to(unit) if d < 9: score = d * 0.2 - enemy.movement_speed + enemy.radius + self.unit_values.power(enemy) score += 0.1 * (enemy.tag % (shuffler + 2)) if score > best_score: target = enemy best_score = score if target is not None: return Action(target.position, False, AbilityId.EFFECT_CORROSIVEBILE) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.cd_manager.is_ready(unit.tag, AbilityId.TRANSFUSION_TRANSFUSION): own_close = self.cache.own_in_range(unit.position, 7) for own_unit in own_close: # type: Unit if own_unit.health_max - own_unit.health > 70 and (not own_unit.has_buff(BuffId.TRANSFUSION) or own_unit.health < 30): return Action(own_unit, False, AbilityId.TRANSFUSION_TRANSFUSION) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command bc = unit if bc.health < 50 and self.cd_manager.is_ready( bc.tag, AbilityId.EFFECT_TACTICALJUMP): zones = self.knowledge.our_zones_with_minerals if zones: position = zones[0].behind_mineral_position_center self.cd_manager.used_ability(bc.tag, AbilityId.EFFECT_TACTICALJUMP) return Action(position, False, AbilityId.EFFECT_TACTICALJUMP) if not self.cd_manager.is_ready( bc.tag, AbilityId.EFFECT_TACTICALJUMP) and bc.health_percentage < 0.9: scvs: Units = self.knowledge.unit_cache.own(UnitTypeId.SCV) if len(scvs) > 0 and scvs.closest_distance_to(bc) < 4: # Stay put! return Action(bc.position, False) if self.cd_manager.is_ready(bc.tag, AbilityId.YAMATO_YAMATOGUN): shuffler = unit.tag % 10 best_score = 100 # Let's not waste yamato on marines or zerglings target: Optional[Unit] = None enemy: Unit for enemy in self.enemies_near_by: d = enemy.distance_to(unit) if d < 11 and self.unit_values.power(enemy) > 1: score = enemy.health # TODO: Needs proper target locking in order to not fire at the same target # Simple and stupid way in an attempt to not use yamato gun on same target: score += (enemy.tag % (shuffler + 2)) if score > best_score: target = enemy best_score = score if target is not None: return Action(target, False, AbilityId.YAMATO_YAMATOGUN) return super().unit_solve_combat(unit, current_command)
def evasive_move_to(self, position_to, unit: Unit): enemy_anti_air_units = self.knowledge.unit_cache.enemy_in_range(unit.position3d, 11) \ .filter(lambda u: u.can_attack_air).visible if enemy_anti_air_units.exists: position = unit.position3d for aa in enemy_anti_air_units: distance = unit.distance_to(aa.position3d) amount_of_evade = 15 - distance if distance > 0: position = position.towards(aa, -amount_of_evade) # after the for loop, position is the best vector away from enemy distance_to_best_evade_point = unit.distance_to(position) should_go = position.towards(position_to, distance_to_best_evade_point) return Action(should_go, False) else: return Action(position_to, False)
def melee_focus_fire( step: MicroStep, unit: Unit, current_command: Action, prio: Optional[Dict[UnitTypeId, int]] ) -> Action: ground_range = step.unit_values.ground_range(unit) lookup = ground_range + 3 enemies = step.cache.enemy_in_range(unit.position, lookup) last_target = step.last_targeted(unit) if not enemies: # No enemies to shoot at return current_command def melee_value(u: Unit): val = 1 - u.shield_health_percentage range = step.unit_values.real_range(unit, u) if unit.distance_to(u) < range: val += 2 if step.knowledge.enemy_race == Race.Terran and unit.is_structure and unit.build_progress < 1: # if building isn't finished, focus on the possible scv instead val -= 2 if unit.is_structure and not unit.can_attack: # if building val -= 6 return val value_func = melee_value close_enemies = step.cache.enemy_in_range(unit.position, lookup) best_target: Optional[Unit] = None best_score: float = 0 for enemy in close_enemies: # type: Unit if enemy.type_id in ignored_types: continue if enemy.is_flying: continue pos: Point2 = enemy.position score = value_func(enemy) + (1 - pos.distance_to(unit) / lookup) if enemy.tag == last_target: score += 1 if step.focus_fired.get(enemy.tag, 0) > enemy.health: score *= 0.1 if score > best_score: best_target = enemy best_score = score if best_target: step.focus_fired[best_target.tag] = step.focus_fired.get(best_target.tag, 0) return Action(best_target, True) return current_command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if unit.tag not in self.spawned: self.spawned[unit.tag] = self.ai.time time_percentage = 1 - (self.ai.time - self.spawned.get(unit.tag, self.ai.time)) / NOVA_DURATION relevant_enemies = self.cache.enemy_in_range(unit.position, 9 * time_percentage).not_structure.not_flying if relevant_enemies.exists: target = relevant_enemies.closest_to(relevant_enemies.center) return Action(target, False) else: own_relevant = self.cache.own_in_range(unit.position, 4).not_structure.not_flying if own_relevant: closest_own = own_relevant.closest_to(unit.position) pos = unit.position.towards(closest_own.position, -3) return Action(pos, False) return current_command
def final_solve(self, unit: Unit, command: Action) -> Action: is_fighter = unit.type_id == UnitTypeId.VIKINGFIGHTER if not self.enemies_near_by: if is_fighter: return command else: return Action(None, False, AbilityId.MORPH_VIKINGFIGHTERMODE) if is_fighter: if (not self.enemies_near_by(UnitTypeId.COLOSSUS).exists and not self.enemies_near_by.flying.exists and self.enemies_near_by.not_flying.exists): return Action(None, False, AbilityId.MORPH_VIKINGASSAULTMODE) else: if self.engaged_power.air_presence > 0 or self.enemies_near_by( UnitTypeId.COLOSSUS).exists: return Action(None, False, AbilityId.MORPH_VIKINGFIGHTERMODE) return command
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: return current_command if self.cd_manager.is_ready(unit.tag, AbilityId.PSISTORM_PSISTORM): stormable_enemies = self.cache.enemy_in_range(unit.position, 10).not_structure if len(stormable_enemies) > 6: center = stormable_enemies.center target = stormable_enemies.closest_to(center) return Action(target.position, False, AbilityId.PSISTORM_PSISTORM) if self.cd_manager.is_ready(unit.tag, AbilityId.FEEDBACK_FEEDBACK): feedback_enemies = self.cache.enemy_in_range(unit.position, 10) \ .filter(lambda u: u.energy > 74 and not u.is_structure) if feedback_enemies: closest = feedback_enemies.closest_to(unit) return Action(closest, False, AbilityId.FEEDBACK_FEEDBACK) return super().unit_solve_combat(unit, current_command)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: if isinstance(current_command.target, Unit): target_pos = current_command.target.position else: target_pos = current_command.target target_return = self.pather.find_path(self.group.center, self.group.center.towards( target_pos, 3), 3) # move ahead of group enemies = self.cache.enemy_in_range(unit.position, 12, False) other_observers = self.cache.own(UnitTypeId.OBSERVER).tags_not_in( [unit.tag]) if other_observers.exists: # Try to keep observers separated from each other closest = other_observers.closest_to(unit) if closest.distance_to(unit) < 10: pos: Point2 = closest.position target = unit.position.towards(pos, -6) return Action(target, False) ACs = enemies.filter(lambda u: u.is_detector) if ACs.exists: nearest_AC = ACs.closest_to(unit) enemy_AA = self.cache.enemy_in_range( unit.position, 12, False).filter(lambda u: u.can_attack_air) if enemy_AA.exists: target = unit.position.towards(nearest_AC, -4) return Action(target, False) cloaked_enemy = self.cache.enemy_in_range(unit.position, 300, only_targetable=False). \ filter(lambda u: u.is_cloaked) if cloaked_enemy.amount > 0 and self.knowledge.enemy_race == Race.Terran: if ACs is not None: target = self.pather.find_weak_influence_air( cloaked_enemy.closest_to(unit).position, 10).position else: target = cloaked_enemy.closest_to(unit).position return Action(target, False) return Action(target_return, False)
def action_to(self, group: CombatUnits, target, move_type: MoveType, is_attack: bool): if isinstance(target, Point2) and group.ground_units: if move_type in {MoveType.DefensiveRetreat, MoveType.PanicRetreat}: target = self.pather.find_influence_ground_path( group.center, target, 14) else: target = self.pather.find_path(group.center, target, 14) own_unit_cache: Dict[UnitTypeId, Units] = {} for unit in group.units: real_type = self.unit_values.real_type(unit.type_id) units = own_unit_cache.get(real_type, Units([], self.ai)) if units.amount == 0: own_unit_cache[real_type] = units units.append(unit) for type_id, type_units in own_unit_cache.items(): micro: MicroStep = self.unit_micros.get(type_id, self.generic_micro) micro.init_group(self.rules, group, type_units, self.enemy_groups, move_type) group_action = micro.group_solve_combat(type_units, Action(target, is_attack)) for unit in type_units: final_action = micro.unit_solve_combat(unit, group_action) order = final_action.to_commmand(unit) if order: self.ai.do(order) if self.debug: if final_action.debug_comment: status = final_action.debug_comment elif final_action.ability: status = final_action.ability.name elif final_action.is_attack: status = "Attack" else: status = "Move" if final_action.target is not None: if isinstance(final_action.target, Unit): status += f": {final_action.target.type_id.name}" else: status += f": {final_action.target}" status += f" G: {group.debug_index}" status += f"\n{move_type.name}" pos3d: Point3 = unit.position3d pos3d = Point3((pos3d.x, pos3d.y, pos3d.z + 2)) self.ai._client.debug_text_world(status, pos3d, size=10)
def should_force_field(self, position: Point2) -> Optional[Action]: for ff in self.cache.force_fields: # type: EffectData for position in ff.positions: if position.distance_to(position) < 1.5: return None for ff_pos in self.upcoming_fields: # type: Point2 if ff_pos.distance_to_point2(position) < 1.5: return None self.upcoming_fields.append(position) return Action(position, False, AbilityId.FORCEFIELD_FORCEFIELD)
def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action: enemy = self.knowledge.unit_cache.enemy_in_range(unit.position3d, 12) if unit.shield_health_percentage <= 0.3 and enemy: enemy_closest = enemy.closest_to(unit.position3d) return Action(enemy_closest.position.towards(unit.position, 5), False) if self.cd_manager.is_ready(unit.tag, AbilityId.EFFECT_TIMEWARP): stormable_enemies = self.cache.enemy_in_range(unit.position, 13).not_structure if len(stormable_enemies) >= 7: center = stormable_enemies.center target = stormable_enemies.closest_to(center) return Action(target.position, False, AbilityId.EFFECT_TIMEWARP) if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25: if self.group.units.amount >= 10: # Regroup with the army return Action(self.group.center, True) return super().unit_solve_combat(unit, current_command)