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)
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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)
Example #5
0
    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
Example #6
0
    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)
Example #8
0
    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)
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
 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
Example #13
0
    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
Example #14
0
    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)
Example #15
0
    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)
Example #16
0
    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)
Example #17
0
    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
Example #19
0
    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)
Example #21
0
    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
Example #24
0
    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
Example #25
0
    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
Example #26
0
    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)
Example #28
0
    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)
Example #29
0
    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)
Example #30
0
    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)