Example #1
0
    def use_guardian_shield(self, enemies: EnemyData, sentry: Unit, time: float):
        if sentry.has_buff(BuffId.GUARDIANSHIELD):
            return []

        if self.tag_shield_used_dict.get(sentry.tag, 0) + self.shield_cooldown < time:
            for key, value in self.tag_shield_used_dict.items():
                if value + 3 < time:
                    # Another sentry just used it's shield, wait a while
                    return []

            enemies_at_close_range = enemies.close_enemies.closer_than(GUARDIAN_SHIELD_TRIGGER_RANGE,
                                                                       sentry.position)
            shooter_power = 0
            for enemy in enemies_at_close_range:
                enemy: Unit = enemy
                if self.unit_values.is_ranged_unit(enemy):
                    shooter_power += self.unit_values.power(enemy)

            if shooter_power > 3:
                self.tag_shield_used_dict[sentry.tag] = time
                return [CombatAction(sentry, None, False, ability=AbilityId.GUARDIANSHIELD_GUARDIANSHIELD)]
        return []
Example #2
0
    def follow_path(self, unit: Unit) -> None:
        """
        Follow the path set or set a new one if none exists.

        Args:
            unit (Unit): the unit moving

        Returns:
            None
        """
        if unit.tag not in self.pathing_dict:
            self.add_to_path_dict(unit, self.target)
        else:
            advance_factor = int(unit.movement_speed) + 2
            self.pathing_dict[unit.tag]["step"] += advance_factor
        curr_step = self.pathing_dict[unit.tag]["step"]
        if curr_step >= len(self.pathing_dict[unit.tag]["path"]):
            curr_step = len(self.pathing_dict[unit.tag]["path"]) - 1
        self.do(
            unit.attack(Point2(
                self.pathing_dict[unit.tag]["path"][curr_step])))
        if curr_step == len(self.pathing_dict[unit.tag]["path"]) - 1:
            del self.pathing_dict[unit.tag]
Example #3
0
    def in_attack_range_of(self, unit: Unit, bonus_distance: Union[int, float] = 0) -> Units:
        """
        Filters units that are in attack range of the given unit.
        This uses the unit and target unit.radius when calculating the distance, so it should be accurate.
        Caution: This may not work well for static structures (bunker, sieged tank, planetary fortress, photon cannon, spine and spore crawler) because it seems attack ranges differ for static / immovable units.

        Example::

            enemy_zerglings = self.enemy_units(UnitTypeId.ZERGLING)
            my_marine = next((unit for unit in self.units if unit.type_id == UnitTypeId.MARINE), None)
            if my_marine:
                all_zerglings_my_marine_can_attack = enemy_zerglings.in_attack_range_of(my_marine)

        Example::

            enemy_mutalisks = self.enemy_units(UnitTypeId.MUTALISK)
            my_marauder = next((unit for unit in self.units if unit.type_id == UnitTypeId.MARAUDER), None)
            if my_marauder:
                all_mutalisks_my_marauder_can_attack = enemy_mutaliskss.in_attack_range_of(my_marauder)
                # Is empty because mutalisk are flying and marauder cannot attack air

        :param unit:
        :param bonus_distance:"""
        return self.filter(lambda x: unit.target_in_range(x, bonus_distance=bonus_distance))
Example #4
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
Example #5
0
            def sort_method(unit: Unit):
                role = self.roles.unit_role(unit)
                # if self.knowledge.my_race == Race.Protoss and role == UnitTask.Building:
                #     return 0

                if unit.distance_to(self.knowledge.enemy_main_zone.behind_mineral_position_center) <= 50:
                    return 10

                if role == UnitTask.Idle:
                    return 1

                if role == UnitTask.Gathering:
                    if unit.is_gathering and isinstance(unit.order_target, int):
                        target = self.cache.by_tag(unit.order_target)
                        if target and target.is_mineral_field:
                            return 2
                        else:
                            return 4
                    if unit.is_carrying_vespene:
                        return 5
                    if unit.is_carrying_minerals:
                        return 3
                    return 3
                return 10
Example #6
0
    async def do_queen_micro(self, queen: Unit, enemy: Units) -> None:
        if not queen or not enemy:
            return
        in_range_enemies: Units = enemy.in_attack_range_of(queen)
        if in_range_enemies:
            if queen.weapon_cooldown == 0:
                lowest_hp: Unit = min(
                    in_range_enemies, key=lambda e: (e.health + e.shield, e.tag)
                )
                queen.attack(lowest_hp)
            else:
                closest_enemy: Unit = in_range_enemies.closest_to(queen)
                distance: float = (
                    queen.ground_range + queen.radius + closest_enemy.radius
                )

                queen.move(closest_enemy.position.towards(queen, distance))

        else:
            queen.attack(enemy.center)
Example #7
0
    async def do_queen_micro(self, queen: Unit, enemy: Units) -> None:
        if not queen or not enemy:
            return
        in_range_enemies: Units = self.in_attack_range_of(queen, enemy)
        if in_range_enemies:
            if queen.weapon_cooldown == 0:
                target: Unit = self._get_target_from_in_range_enemies(
                    in_range_enemies)
                queen.attack(target)
            else:
                closest_enemy: Unit = self.find_closest_enemy(
                    queen, in_range_enemies)
                distance: float = (queen.ground_range + queen.radius +
                                   closest_enemy.radius)

                queen.move(closest_enemy.position.towards(queen, distance))

        else:
            target = self.find_closest_enemy(queen, enemy)
            queen.attack(target)
Example #8
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 == MoveType.DefensiveRetreat:
            if self.ready_to_shoot(unit):
                closest = self.closest_units.get(unit.tag, None)
                if closest and self.is_target(closest):
                    range = self.unit_values.real_range(unit, closest)
                    if range > 0 and range > unit.distance_to(closest):
                        return Action(closest, True)
            return current_command

        elif self.move_type == MoveType.PanicRetreat:
            return current_command

        if self.is_locked_on(
                unit
        ) and self.enemies_near_by and not self.ready_to_shoot(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)
                if unit.is_flying:
                    backstep = self.pather.find_weak_influence_air(backstep, 4)
                else:
                    backstep = self.pather.find_weak_influence_ground(
                        backstep, 4)
                return Action(backstep, False)

        if self.should_retreat(
                unit) and self.closest_group and not self.ready_to_shoot(unit):
            backstep: Point2 = unit.position.towards(self.closest_group.center,
                                                     -3)
            if unit.is_flying:
                backstep = self.pather.find_weak_influence_air(backstep, 4)
            else:
                backstep = self.pather.find_weak_influence_ground(backstep, 4)
            return Action(backstep, False)

        if self.model == CombatModel.StalkerToSiege:
            siege_units = self.enemies_near_by.of_type(siege)
            if siege_units:
                target = siege_units.closest_to(unit)
                if target.distance_to(unit) < 7:
                    return Action(target, True)

        if self.model == CombatModel.StalkerToRoach:
            if self.ready_to_shoot(unit):
                if self.closest_group:
                    current_command = Action(self.closest_group.center, True)
                else:
                    current_command = Action(current_command.target, True)
            else:
                closest = self.closest_units[unit.tag]

                # d = unit.distance_to(closest)
                range = self.unit_values.real_range(unit, closest) - 0.5

                if unit.is_flying:
                    best_position = self.pather.find_low_inside_air(
                        unit.position, closest.position, range)
                else:
                    best_position = self.pather.find_low_inside_ground(
                        unit.position, closest.position, range)

                return Action(best_position, False)

        elif self.model == CombatModel.RoachToStalker:
            if self.ready_to_shoot(unit):
                if self.closest_group:
                    current_command = Action(self.closest_group.center, True)
                else:
                    current_command = Action(current_command.target, True)
            else:
                # Instead of backstep, move forward
                current_command = self.focus_fire(unit, current_command,
                                                  self.prio_dict)
                if isinstance(current_command.target, Unit):
                    current_command.target = current_command.target.position
                    current_command.is_attack = False
                return current_command

        if self.ready_to_shoot(unit) and current_command.is_attack:
            return self.focus_fire(unit, current_command, self.prio_dict)
        return current_command
Example #9
0
 async def do_queen_offensive_micro(self, queen: Unit,
                                    offensive_pos: Point2) -> None:
     if not queen or not offensive_pos:
         return
     enemy: Units = self.bot.enemy_units.exclude_type(
         {UnitID.MULE, UnitID.EGG, UnitID.LARVA})
     enemy_structures: Units = self.bot.enemy_structures
     queens: Units = self.bot.units(UnitID.QUEEN)
     own_close_queens: Units = queens.filter(
         lambda u: u.distance_to(queen) < 5)
     if enemy:
         in_range_enemies: Units = self.in_attack_range_of(queen, enemy)
         in_range_structures: Units = self.in_attack_range_of(
             queen, enemy_structures)
         if queen.weapon_cooldown == 0:
             if in_range_enemies:
                 target: Unit = self._get_target_from_in_range_enemies(
                     in_range_enemies)
                 queen.attack(target)
             elif in_range_structures:
                 queen.attack(
                     self.find_closest_enemy(queen, in_range_structures))
             else:
                 queen.move(offensive_pos)
         else:
             if own_close_queens.amount <= 3:
                 queen.move(queens.center)
             else:
                 queen.move(offensive_pos)
     else:
         queen.attack(offensive_pos)
Example #10
0
    async def _manage_nydus_attack(
        self,
        canal: Unit,
        network: Unit,
        unit: Unit,
        unit_distance_to_target: float,
        grid: Optional[np.ndarray] = None,
    ) -> None:
        """
        Get a Queen through the nydus and out the other side!
        @param canal: The canal is the worm placed on the map
        @param network: This is built at home
        @param unit: In this case, the queen we want to move through
        @param unit_distance_to_target:
        @return:
        """
        # user does not have some predefined nydus logic, so we unload the proxy canal for them
        if len(canal.passengers_tags) > 0 and not self.policy.nydus_move_function:
            canal(AbilityId.UNLOADALL_NYDUSWORM)

        # worm has popped somewhere, but we are waiting for it to finish, move next to network ready to go
        # usually we want queens last in anyway, so this gives a chance for other units to enter the nydus
        if not canal.is_ready and unit.distance_to(canal) > 30:
            unit.move(network.position)
        # both canal and network must be ready
        else:
            # unit needs to go through the nydus
            if unit_distance_to_target > 45 and unit.distance_to(network) < 70:
                # user has some custom code for moving units through nydus
                if self.policy.nydus_move_function:
                    self.policy.nydus_move_function(unit, self.policy.nydus_target)
                # manage this ourselves
                else:
                    network(AbilityId.LOAD_NYDUSNETWORK, unit)
            # else queen should micro on the other side
            # remember that all queens already have transfuse code baked in
            else:
                # queen has enough energy for a transfuse and a tumor, so put a tumor down where she currently is
                if unit.energy >= 75 and self.bot.has_creep(unit.position):
                    # check if there are too many tumors already
                    tumors: Units = self.bot.structures.filter(
                        lambda s: s.type_id
                        in {UnitID.CREEPTUMORBURROWED, UnitID.CREEPTUMORQUEEN}
                        and s.distance_to(unit) < 15
                    )
                    if tumors.amount < 7:
                        unit(AbilityId.BUILD_CREEPTUMOR_QUEEN, unit.position)
                if unit.is_using_ability(AbilityId.BUILD_CREEPTUMOR_QUEEN):
                    return
                # get priority target, ie: target the flying enemies first
                target: Optional[Unit] = self._get_target_from_close_enemies(unit)
                if target:
                    if self.attack_ready(unit, target):
                        unit.attack(target)
                    elif self.map_data and grid is not None:
                        await self.move_towards_safe_spot(unit, grid)
                    else:
                        distance: float = (
                            unit.ground_range + unit.radius + target.radius
                        )
                        move_to: Point2 = target.position.towards(unit, distance)
                        if self.bot.in_pathing_grid(move_to):
                            unit.move(move_to)
                # check if there is anything around here to attack,
                # if not then we attack the general attack target the user has passed in
                # TODO: In the future, this is where we would want the queens to come home
                #   At the moment a nydus queen is on a one way trip
                elif (
                    self.enemy_ground_units_near_nydus_target.amount == 0
                    and self.enemy_flying_units_near_nydus_target.amount == 0
                ):
                    await self.do_queen_offensive_micro(unit, self.policy.attack_target)
                # there are targets, but nothing in range so move towards the nydus target
                else:
                    unit.move(self.policy.nydus_target)
Example #11
0
    def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action:
        if self.force_fielding(unit):
            # Don't do anything if force field is ordered
            return NoAction()

        if (
            not self.shield_up
            and self.should_shield_up
            and unit.energy >= SHIELD_ENERGY_COST
            and self.last_shield_up + 0.5 < self.ai.time
        ):
            self.shield_up = True
            self.last_shield_up = self.ai.time
            return Action(None, False, AbilityId.GUARDIANSHIELD_GUARDIANSHIELD)

        if unit.shield_percentage < 0.1:
            if self.range_power > 5 and unit.energy >= HALLUCINATION_ENERGY_COST:
                return Action(None, False, AbilityId.HALLUCINATION_ARCHON)
            if self.melee_power > 5 and unit.energy >= FORCE_FIELD_ENERGY_COST:
                melee = self.knowledge.unit_cache.enemy(self.unit_values.melee)
                if melee:
                    closest = melee.closest_to(unit)
                    pos = unit.position.towards(closest, 0.6)
                    return Action(pos, False, AbilityId.FORCEFIELD_FORCEFIELD)

        if self.move_type == MoveType.SearchAndDestroy and unit.energy >= FORCE_FIELD_ENERGY_COST:
            # Look for defensive force field on ramp or other choke
            natural: Zone = self.knowledge.expansion_zones[1]
            main: Zone = self.knowledge.expansion_zones[0]
            d_natural = unit.distance_to(natural.center_location)
            d_main = unit.distance_to(main.center_location)

            if d_natural < 15 and d_natural < d_main and self.closest_group_distance < 10:
                # Sentry is at the natural
                zealot_pos: Point2 = self.knowledge.building_solver.zealot_position
                if self.knowledge.enemy_race == Race.Zerg and natural.our_wall() and zealot_pos:
                    # Protect gate keeper
                    our_keepers = self.cache.own_in_range(zealot_pos, 2).not_structure
                    combined_health = 0
                    for keeper in our_keepers:  # type: Unit
                        combined_health += keeper.health + keeper.shield

                    if combined_health < 70:
                        action = self.should_force_field(zealot_pos.towards(self.closest_group.center, 0.6))
                        if action:
                            return action

                if self.model == CombatModel.StalkerToSpeedlings:
                    # Protect buildings
                    buildings = self.cache.own_in_range(unit.position, 8).structure
                    for building in buildings:  # type: Unit
                        if building.health + building.shield < 300:
                            action = self.should_force_field(building.position.towards(self.closest_group.center, 1.2))
                            if action:
                                return action

            elif not natural.is_ours or natural.power_balance < 0 and d_main < main.radius:
                # Protect main base ramp
                not_flying = self.cache.enemy_in_range(self.main_ramp_position, 3).filter(
                    lambda u: not u.is_flying and not u.is_structure
                )
                if not_flying:
                    action = self.should_force_field(self.main_ramp_position)
                    if action:
                        return action

            #  and self.model == CombatModel.StalkerToSpeedlings
        return super().unit_solve_combat(unit, current_command)
Example #12
0
    async def do_queen_offensive_micro(self, queen: Unit,
                                       offensive_pos: Point2) -> None:
        if not queen or not offensive_pos:
            return
        enemy: Units = self.bot.enemy_units.exclude_type(
            {UnitID.MULE, UnitID.EGG, UnitID.LARVA})
        enemy_structures: Units = self.bot.enemy_structures
        queens: Units = self.bot.units(UnitID.QUEEN)
        own_close_queens: Units = queens.filter(
            lambda u: u.distance_to(queen) < 5)
        if enemy:
            in_range_enemies: Units = enemy.in_attack_range_of(queen)
            in_range_structures: Units = enemy_structures.in_attack_range_of(
                queen)
            if in_range_enemies:
                target: Unit = self._get_target_from_in_range_enemies(
                    in_range_enemies)
                if self.attack_ready(queen, target):
                    queen.attack(target)
                else:
                    # loose queen_control should try to rejoin the queen pack
                    if own_close_queens.amount <= 3:
                        queen.move(queens.center)
                    # otherwise move forward between attacks, since Queen is slow and can get stuck behind each other
                    else:
                        queen.move(offensive_pos)
            elif in_range_structures:
                target: Unit = self._get_target_from_in_range_enemies(
                    in_range_structures)
                if self.attack_ready(queen, target):
                    queen.attack(target)
                else:
                    if own_close_queens.amount <= 3:
                        queen.move(queens.center)
                    else:
                        queen.move(offensive_pos)
            else:
                queen.move(offensive_pos)

        else:
            queen.attack(offensive_pos)
Example #13
0
        def do_some_unit_property_tests(attacker: Unit, defender: Unit):
            """ Some tests that are not covered by test_pickled_data.py """
            # TODO move unit unrelated tests elsewhere
            self.step_time
            self.units_created

            self.structure_type_build_progress(attacker.type_id)
            self.structure_type_build_progress(defender.type_id)
            self.tech_requirement_progress(attacker.type_id)
            self.tech_requirement_progress(defender.type_id)
            self.in_map_bounds(attacker.position)
            self.in_map_bounds(defender.position)
            self.get_terrain_z_height(attacker.position)
            self.get_terrain_z_height(defender.position)

            for unit in [attacker, defender]:
                unit.shield_percentage
                unit.shield_health_percentage
                unit.energy_percentage
                unit.age_in_frames
                unit.age
                unit.is_memory
                unit.is_snapshot
                unit.cloak
                unit.is_revealed
                unit.can_be_attacked
                unit.buff_duration_remain
                unit.buff_duration_max
                unit.order_target
                unit.is_transforming
                unit.has_techlab
                unit.has_reactor
                unit.add_on_position
                unit.health_percentage
                unit.bonus_damage
                unit.air_dps

            attacker.target_in_range(defender)
            defender.target_in_range(attacker)
            attacker.calculate_dps_vs_target(defender)
            defender.calculate_dps_vs_target(attacker)
            attacker.is_facing(defender)
            defender.is_facing(attacker)
            attacker == defender
            defender == attacker
Example #14
0
 async def execute_command(self, unit: Unit, command: CombatAction):
     if command.is_attack:
         self.do(unit.attack(command.target))
     else:
         self.do(unit.move(command.target))
Example #15
0
 def _upgrade_building(self, building: Unit, upgrade_to: UnitTypeId):
     """Upgrade an existing building to another phase (e.g Lair to Hive)"""
     building.build(upgrade_to)
 async def on_building_construction_complete(self, unit: Unit):
     """ Set rally point of new hatcheries. """
     if unit.type_id == UnitTypeId.HATCHERY and self.mineral_field:
         mf = self.mineral_field.closest_to(unit)
         unit.smart(mf)
Example #17
0
 def has_stim(self, unit: Unit) -> bool:
     return unit.has_buff(BuffId.STIMPACK) or unit.has_buff(
         BuffId.STIMPACKMARAUDER)
Example #18
0
    def _prepare_units(self):
        # Set of enemy units detected by own sensor tower, as blips have less unit information than normal visible units
        self.blips: Set[Blip] = set()
        self.all_units: Units = Units([], self)
        self.units: Units = Units([], self)
        self.workers: Units = Units([], self)
        self.larva: Units = Units([], self)
        self.structures: Units = Units([], self)
        self.townhalls: Units = Units([], self)
        self.gas_buildings: Units = Units([], self)
        self.all_own_units: Units = Units([], self)
        self.enemy_units: Units = Units([], self)
        self.enemy_structures: Units = Units([], self)
        self.all_enemy_units: Units = Units([], self)
        self.resources: Units = Units([], self)
        self.destructables: Units = Units([], self)
        self.watchtowers: Units = Units([], self)
        self.mineral_field: Units = Units([], self)
        self.vespene_geyser: Units = Units([], self)
        self.placeholders: Units = Units([], self)
        self.techlab_tags: Set[int] = set()
        self.reactor_tags: Set[int] = set()

        worker_types: Set[UnitTypeId] = {
            UnitTypeId.DRONE, UnitTypeId.DRONEBURROWED, UnitTypeId.SCV,
            UnitTypeId.PROBE
        }

        index: int = 0
        for unit in self.state.observation_raw.units:
            if unit.is_blip:
                self.blips.add(Blip(unit))
            else:
                unit_type: int = unit.unit_type
                # Convert these units to effects: reaper grenade, parasitic bomb dummy, forcefield
                if unit_type in FakeEffectID:
                    self.state.effects.add(EffectData(unit, fake=True))
                    continue
                unit_obj = Unit(unit,
                                self,
                                distance_calculation_index=index,
                                base_build=self.base_build)
                index += 1
                self.all_units.append(unit_obj)
                if unit.display_type == IS_PLACEHOLDER:
                    self.placeholders.append(unit_obj)
                    continue
                alliance = unit.alliance
                # Alliance.Neutral.value = 3
                if alliance == 3:
                    # XELNAGATOWER = 149
                    if unit_type == 149:
                        self.watchtowers.append(unit_obj)
                    # mineral field enums
                    elif unit_type in mineral_ids:
                        self.mineral_field.append(unit_obj)
                        self.resources.append(unit_obj)
                    # geyser enums
                    elif unit_type in geyser_ids:
                        self.vespene_geyser.append(unit_obj)
                        self.resources.append(unit_obj)
                    # all destructable rocks
                    else:
                        self.destructables.append(unit_obj)
                # Alliance.Self.value = 1
                elif alliance == 1:
                    self.all_own_units.append(unit_obj)
                    unit_id: UnitTypeId = unit_obj.type_id
                    if unit_obj.is_structure:
                        self.structures.append(unit_obj)
                        if unit_id in race_townhalls[self.race]:
                            self.townhalls.append(unit_obj)
                        elif unit_id in ALL_GAS or unit_obj.vespene_contents:
                            # TODO: remove "or unit_obj.vespene_contents" when a new linux client newer than version 4.10.0 is released
                            self.gas_buildings.append(unit_obj)
                        elif unit_id in {
                                UnitTypeId.TECHLAB,
                                UnitTypeId.BARRACKSTECHLAB,
                                UnitTypeId.FACTORYTECHLAB,
                                UnitTypeId.STARPORTTECHLAB,
                        }:
                            self.techlab_tags.add(unit_obj.tag)
                        elif unit_id in {
                                UnitTypeId.REACTOR,
                                UnitTypeId.BARRACKSREACTOR,
                                UnitTypeId.FACTORYREACTOR,
                                UnitTypeId.STARPORTREACTOR,
                        }:
                            self.reactor_tags.add(unit_obj.tag)
                    else:
                        self.units.append(unit_obj)
                        if unit_id in worker_types:
                            self.workers.append(unit_obj)
                        elif unit_id == UnitTypeId.LARVA:
                            self.larva.append(unit_obj)
                # Alliance.Enemy.value = 4
                elif alliance == 4:
                    self.all_enemy_units.append(unit_obj)
                    if unit_obj.is_structure:
                        self.enemy_structures.append(unit_obj)
                    else:
                        self.enemy_units.append(unit_obj)

        # Force distance calculation and caching on all units using scipy pdist or cdist
        if self.distance_calculation_method == 1:
            _ = self._pdist
        elif self.distance_calculation_method in {2, 3}:
            _ = self._cdist
Example #19
0
    def __init__(self, response_observation):
        self.response_observation = response_observation
        self.actions = response_observation.actions  # successful actions since last loop
        self.action_errors = response_observation.action_errors  # error actions since last loop

        # https://github.com/Blizzard/s2client-proto/blob/51662231c0965eba47d5183ed0a6336d5ae6b640/s2clientprotocol/sc2api.proto#L575
        # TODO: implement alerts https://github.com/Blizzard/s2client-proto/blob/51662231c0965eba47d5183ed0a6336d5ae6b640/s2clientprotocol/sc2api.proto#L640
        self.observation = response_observation.observation
        self.observation_raw = self.observation.raw_data
        self.dead_units: Set[
            int] = self.observation_raw.event.dead_units  # returns set of tags of units that died
        self.alerts = self.observation.alerts
        self.player_result = response_observation.player_result
        self.chat = response_observation.chat
        self.common: Common = Common(self.observation.player_common)

        # Area covered by Pylons and Warpprisms
        self.psionic_matrix: PsionicMatrix = PsionicMatrix.from_proto(
            self.observation_raw.player.power_sources)
        self.game_loop: int = self.observation.game_loop  # 22.4 per second on faster game speed

        # https://github.com/Blizzard/s2client-proto/blob/33f0ecf615aa06ca845ffe4739ef3133f37265a9/s2clientprotocol/score.proto#L31
        self.score: ScoreDetails = ScoreDetails(self.observation.score)
        self.abilities = self.observation.abilities  # abilities of selected units

        self._blipUnits = []
        self.own_units: Units = Units([])
        self.enemy_units: Units = Units([])
        self.mineral_field: Units = Units([])
        self.vespene_geyser: Units = Units([])
        self.resources: Units = Units([])
        self.destructables: Units = Units([])
        self.watchtowers: Units = Units([])
        self.units: Units = Units([])

        for unit in self.observation.raw_data.units:
            if unit.is_blip:
                self._blipUnits.append(unit)
            else:
                unit_obj = Unit(unit)
                self.units.append(unit_obj)
                alliance = unit.alliance
                # Alliance.Neutral.value = 3
                if alliance == 3:
                    unit_type = unit.unit_type
                    # XELNAGATOWER = 149
                    if unit_type == 149:
                        self.watchtowers.append(unit_obj)
                    # mineral field enums
                    elif unit_type in mineral_ids:
                        self.mineral_field.append(unit_obj)
                        self.resources.append(unit_obj)
                    # geyser enums
                    elif unit_type in geyser_ids:
                        self.vespene_geyser.append(unit_obj)
                        self.resources.append(unit_obj)
                    # all destructable rocks
                    else:
                        self.destructables.append(unit_obj)
                # Alliance.Self.value = 1
                elif alliance == 1:
                    self.own_units.append(unit_obj)
                # Alliance.Enemy.value = 4
                elif alliance == 4:
                    self.enemy_units.append(unit_obj)
        self.upgrades: Set[UpgradeId] = {
            UpgradeId(upgrade)
            for upgrade in self.observation_raw.player.upgrade_ids
        }

        # Set of unit tags that died this step
        self.dead_units: Set[int] = {
            dead_unit_tag
            for dead_unit_tag in self.observation_raw.event.dead_units
        }
        # Set of enemy units detected by own sensor tower, as blips have less unit information than normal visible units
        self.blips: Set[Blip] = {Blip(unit) for unit in self._blipUnits}
        # self.visibility[point]: 0=Hidden, 1=Fogged, 2=Visible
        self.visibility: PixelMap = PixelMap(
            self.observation_raw.map_state.visibility, mirrored=True)
        # HSPARK 수정 시작
        # self.creep[point]: 0=No creep, 1=creep
        # self.creep: PixelMap = PixelMap(self.observation_raw.map_state.creep, mirrored=True)
        self.creep: PixelMap = PixelMap(self.observation_raw.map_state.creep,
                                        mirrored=True,
                                        in_bits=True)
        # HSPARK 수정 끝

        # Effects like ravager bile shot, lurker attack, everything in effect_id.py
        self.effects: Set[EffectData] = {
            EffectData(effect)
            for effect in self.observation_raw.effects
        }
        """ Usage:
Example #20
0
 def from_proto(cls, units, bot_object: BotAI):
     # pylint: disable=E1120
     return cls((Unit(raw_unit, bot_object=bot_object) for raw_unit in units))
Example #21
0
    def unit_solve_combat(self, unit: Unit, current_command: Action) -> Action:

        if self.move_type == MoveType.DefensiveRetreat or self.move_type == MoveType.PanicRetreat:
            if self.ready_to_shoot(unit):
                closest = self.closest_units.get(unit.tag, None)
                if closest:
                    real_range = self.unit_values.real_range(unit, closest)
                    if 0 < real_range < unit.distance_to(closest):
                        return Action(closest.position, True)

            return current_command

        # Phoenixes are generally faster than the rest of the army

        # if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25:
        #     if self.group.ground_units:
        #         # Regroup with the ground army
        #         return Action(self.group.center, False)

        has_energy = unit.energy > GRAVITON_BEAM_ENERGY

        if has_energy and self.allow_lift:
            best_target: Optional[Unit] = None
            best_score: float = 0
            close_enemies = self.cache.enemy_in_range(unit.position, 14)

            for enemy in close_enemies:  # type: Unit
                if enemy.is_flying or enemy.is_structure or enemy.has_buff(
                        BuffId.GRAVITONBEAM):
                    continue

                if self.move_type != MoveType.Harass and enemy.type_id in self.unit_values.worker_types:
                    # If we are not doing any harass, don't lift low priority workers up.
                    # We need to prioritize energy to actual combat units
                    continue

                pos: Point2 = enemy.position
                score = self.lift_priority.get(
                    enemy.type_id, -1) + (1 - pos.distance_to(unit) / 10)
                if score > best_score:
                    best_target = enemy
                    best_score = score

            if best_target:
                if best_score > 5 or not close_enemies.flying.exists:
                    self.print(
                        f"Phoenix at {unit.position} lifting {best_target.type_id} at {best_target.position}"
                    )

                    if unit.distance_to(best_target) > 8:
                        destination = self.knowledge.pathing_manager.find_influence_air_path(
                            unit.position, best_target.position)
                        return Action(destination, False)
                    return Action(best_target, False,
                                  AbilityId.GRAVITONBEAM_GRAVITONBEAM)

        if self.engage_ratio < 0.25 and self.can_engage_ratio < 0.25:
            # Not in combat
            return current_command

        targets = self.enemies_near_by.flying

        if targets:
            closest = targets.closest_to(unit)
            # d = unit.distance_to(closest)
            real_range = self.unit_values.real_range(unit, closest) - 1
            best_position = self.pather.find_low_inside_air(
                unit.position, closest.position, real_range)

            return Action(best_position, False)

        return current_command
Example #22
0
 def is_locked_on(self, unit: Unit) -> bool:
     if unit.has_buff(BuffId.LOCKON):
         return True
     return 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 enemy.type_id in ignored_types:
                continue

            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 #24
0
 def one_of_targets_in_range(self, unit: Unit, targets: Units):
     for target in targets:
         if unit.target_in_range(target):
             return True
     return False