Exemple #1
0
    def actions(
        self
    ) -> List[Union[ActionRawUnitCommand, ActionRawToggleAutocast,
                    ActionRawCameraMove]]:
        """
        List of successful actions since last frame.
        See https://github.com/Blizzard/s2client-proto/blob/01ab351e21c786648e4c6693d4aad023a176d45c/s2clientprotocol/sc2api.proto#L630-L637

        Each action is converted into Python dataclasses: ActionRawUnitCommand, ActionRawToggleAutocast, ActionRawCameraMove
        """
        previous_frame_actions = self.previous_observation.actions if self.previous_observation else []
        actions = []
        for action in chain(previous_frame_actions,
                            self.response_observation.actions):
            action_raw = action.action_raw
            game_loop = action.game_loop
            if action_raw.HasField("unit_command"):
                # Unit commands
                raw_unit_command = action_raw.unit_command
                if raw_unit_command.HasField("target_world_space_pos"):
                    # Actions that have a point as target
                    actions.append(
                        ActionRawUnitCommand(
                            game_loop,
                            raw_unit_command.ability_id,
                            raw_unit_command.unit_tags,
                            raw_unit_command.queue_command,
                            Point2.from_proto(
                                raw_unit_command.target_world_space_pos),
                        ))
                else:
                    # Actions that have a unit as target
                    actions.append(
                        ActionRawUnitCommand(
                            game_loop,
                            raw_unit_command.ability_id,
                            raw_unit_command.unit_tags,
                            raw_unit_command.queue_command,
                            None,
                            raw_unit_command.target_unit_tag,
                        ))
            elif action_raw.HasField("toggle_autocast"):
                # Toggle autocast actions
                raw_toggle_autocast_action = action_raw.toggle_autocast
                actions.append(
                    ActionRawToggleAutocast(
                        game_loop,
                        raw_toggle_autocast_action.ability_id,
                        raw_toggle_autocast_action.unit_tags,
                    ))
            else:
                # Camera move actions
                actions.append(
                    ActionRawCameraMove(
                        Point2.from_proto(
                            action.action_raw.camera_move.center_world_space)))
        return actions
Exemple #2
0
    def __init__(self, proto):
        self._proto = proto
        self.players: List[Player] = [
            Player.from_proto(p) for p in self._proto.player_info
        ]
        self.map_name: str = self._proto.map_name
        self.local_map_path: str = self._proto.local_map_path
        self.map_size: Size = Size.from_proto(self._proto.start_raw.map_size)

        # self.pathing_grid[point]: if 0, point is not pathable, if 1, point is pathable
        self.pathing_grid: PixelMap = PixelMap(
            self._proto.start_raw.pathing_grid, in_bits=True, mirrored=False)
        # self.terrain_height[point]: returns the height in range of 0 to 255 at that point
        self.terrain_height: PixelMap = PixelMap(
            self._proto.start_raw.terrain_height, mirrored=False)
        # self.placement_grid[point]: if 0, point is not placeable, if 1, point is pathable
        self.placement_grid: PixelMap = PixelMap(
            self._proto.start_raw.placement_grid, in_bits=True, mirrored=False)
        self.playable_area = Rect.from_proto(
            self._proto.start_raw.playable_area)
        self.map_center = self.playable_area.center
        self.map_ramps: List[
            Ramp] = None  # Filled later by BotAI._prepare_first_step
        self.vision_blockers: FrozenSet[
            Point2] = None  # Filled later by BotAI._prepare_first_step
        self.player_races: Dict[int, Race] = {
            p.player_id: p.race_actual or p.race_requested
            for p in self._proto.player_info
        }
        self.start_locations: List[Point2] = [
            Point2.from_proto(sl)
            for sl in self._proto.start_raw.start_locations
        ]
        self.player_start_location: Point2 = None  # Filled later by BotAI._prepare_first_step
Exemple #3
0
    def pending_building_positions(self,
                                   unit_type: UnitTypeId) -> List[Point2]:
        """Returns positions of buildings of the specified type that have either been ordered to be built by a worker
        or are currently being built."""
        positions: List[Point2] = list()
        creation_ability: AbilityId = self.ai._game_data.units[
            unit_type.value].creation_ability

        # Workers ordered to build
        for worker in self.ai.workers:
            worker: Unit = worker
            for order in worker.orders:
                order: UnitOrder = order
                if order.ability.id == creation_ability.id:
                    p2: Point2 = Point2.from_proto(order.target)
                    positions.append(p2)

        # Already building structures
        # Avoid counting structures twice for Terran SCVs.
        if self.knowledge.my_race != Race.Terran:
            pending_buildings: List[Point2] = list(
                map(lambda structure: structure.position,
                    self.cache.own(unit_type).structure.not_ready))
            positions.extend(pending_buildings)

        return positions
Exemple #4
0
    def solve_combat(self, goal: CombatGoal, command: CombatAction, enemies: EnemyData) -> List[CombatAction]:
        sentry = goal.unit
        time = self.knowledge.ai.time

        if goal.unit.orders and goal.unit.orders[0].ability.id == AbilityId.FORCEFIELD_FORCEFIELD:
            return [CombatAction(sentry, Point2.from_proto(goal.unit.orders[0].target), False, ability=AbilityId.FORCEFIELD_FORCEFIELD)]

        if sentry.energy < FORCE_FIELD_ENERGY_COST:
            return [] # do nothing

        if self.knowledge.expansion_zones[1].is_ours and self.knowledge.expansion_zones[1].is_under_attack:
            # TODO: Should we force field here?
            ...
        else:

            not_flying = enemies.close_enemies.filter(lambda u: not u.is_flying and not u.is_structure)
            if not_flying:
                closest_to_ramp = not_flying.closest_to(self.main_ramp_position)
                closest_to_ramp_distance = closest_to_ramp.distance_to(self.main_ramp_position)
                if closest_to_ramp_distance < 7 \
                        and self.ai.get_terrain_height(sentry) > self.ai.get_terrain_height(closest_to_ramp)\
                        and sentry.distance_to(self.main_ramp_position) < 9:

                    if self.ramp_field_used + self.force_field_cooldown < time:
                        if closest_to_ramp_distance < 2:
                            self.ramp_field_used = time
                            return [CombatAction(sentry, self.main_ramp_position, False, ability= AbilityId.FORCEFIELD_FORCEFIELD)]

                    return []


        if self.last_hallu_time + self.hallu_timer < time and sentry.energy >= HALLUCINATION_ENERGY_COST and enemies.close_enemies.exists:
            hallu_type: Optional[AbilityId] = None

            if self.knowledge.enemy_race == Race.Terran:
                if enemies.close_enemies.of_type([UnitTypeId.BANSHEE, UnitTypeId.BATTLECRUISER]):
                    hallu_type = AbilityId.HALLUCINATION_STALKER
                elif enemies.close_enemies.of_type([UnitTypeId.VIKINGFIGHTER, UnitTypeId.RAVEN]):
                    hallu_type = AbilityId.HALLUCINATION_COLOSSUS
                elif sentry.health + sentry.shield < 30:
                    hallu_type = AbilityId.HALLUCINATION_IMMORTAL
            elif self.knowledge.enemy_race == Race.Zerg:
                if enemies.close_enemies(UnitTypeId.HYDRALISK):
                    hallu_type = AbilityId.HALLUCINATION_VOIDRAY
                if enemies.close_enemies(UnitTypeId.ROACH) or sentry.health + sentry.shield < 30:
                    hallu_type = AbilityId.HALLUCINATION_IMMORTAL
            else:
                if sentry.health + sentry.shield < 30:
                    hallu_type = AbilityId.HALLUCINATION_IMMORTAL

            if hallu_type is not None:
                self.last_hallu_time = time
                return [CombatAction(sentry, None, False, ability=hallu_type)]

        result = self.use_guardian_shield(enemies, sentry, time)
        if len(result) > 0:
            return result

        return self.use_force_field(enemies, sentry, time)
Exemple #5
0
    async def scout_with_percentage_of_army(self, percentage, use_overlords,
                                            pull_back_if_damaged):
        map_width = self.mainAgent.map_width
        map_height = self.mainAgent.map_height

        army = self.army

        if use_overlords:
            army += self.mainAgent.units(OVERLORD)

        desired_strike_force_size = int(percentage * army.amount)
        if self.mainAgent.strike_force is None:
            self.mainAgent.strike_force = army.take(desired_strike_force_size)

        # If strike force should include more members (If a unit was built)
        # Do not add more units if the entire army is already in strike force
        if len(self.mainAgent.strike_force
               ) < desired_strike_force_size and len(army) > len(
                   self.mainAgent.strike_force):
            self.mainAgent.strike_force += (
                army - self.mainAgent.strike_force
            ).take(desired_strike_force_size -
                   len(self.mainAgent.strike_force))

        for unit_ref in self.mainAgent.strike_force:
            # Need to reacquire unit from self.mainAgent.units to see that a command has been queued
            id = unit_ref.tag
            unit = self.mainAgent.units.find_by_tag(id)

            if unit is None:
                # Unit died
                self.mainAgent.strike_force.remove(unit_ref)
                continue
            if pull_back_if_damaged and unit.health < unit.health_max:
                # If pull_back is true and unti is damaged, move to random hatchery
                if (len(self.mainAgent.bases) > 0):
                    await self.mainAgent.do(
                        unit.move(self.mainAgent.bases[random.randrange(
                            0, len(self.mainAgent.bases))].position))
            elif unit.noqueue:
                # Go to a new random position
                pos = lambda: None  # https://stackoverflow.com/questions/19476816/creating-an-empty-object-in-python
                pos.x = random.randrange(0, map_width)
                pos.y = random.randrange(0, map_height)
                position_to_search = Point2.from_proto(pos)
                await self.mainAgent.do(unit.move(position_to_search))
Exemple #6
0
 def _worker_orders(self) -> Counter:
     """ This function is used internally, do not use! It is to store all worker abilities. """
     abilities_amount = Counter()
     structures_in_production: Set[Union[Point2, int]] = set()
     for structure in self.structures:
         if structure.type_id in TERRAN_STRUCTURES_REQUIRE_SCV:
             structures_in_production.add(structure.position)
             structures_in_production.add(structure.tag)
     for worker in self.workers:
         for order in worker.orders:
             # Skip if the SCV is constructing (not isinstance(order.target, int))
             # or resuming construction (isinstance(order.target, int))
             is_int = isinstance(order.target, int)
             if (is_int and order.target in structures_in_production
                     or not is_int and Point2.from_proto(
                         order.target) in structures_in_production):
                 continue
             abilities_amount[order.ability] += 1
     return abilities_amount
Exemple #7
0
    async def perform_strategy(self, iteration, strategy_num):
        self.mainAgent.clean_strike_force(
        )  # Clear dead units from strike force
        self.mainAgent.is_army_cached = False  # Must re obtain army data
        if self.mainAgent.predicted_enemy_position_num == -1:
            # Initializing things that are needed after game data is loaded

            # Prevent game from crashing
            hatchery = self.mainAgent.bases
            if hatchery == None or hatchery.amount == 0:
                return
            else:
                hatchery = self.mainAgent.bases.ready.random

            # Assume first position
            self.mainAgent.predicted_enemy_position = 0
            self.mainAgent.num_enemy_positions = len(
                self.mainAgent.enemy_start_locations)
            self.mainAgent.start_location = self.mainAgent.bases.ready.random.position  # Should only be 1 hatchery at this time
            self.mainAgent.map_width = self.mainAgent.game_info.map_size[0]
            self.mainAgent.map_height = self.mainAgent.game_info.map_size[1]

            # Get a point in the corner of the map
            p = lambda: None  # https://stackoverflow.com/questions/19476816/creating-an-empty-object-in-python
            p.x = self.mainAgent.game_info.map_center.x * 1.9
            p.y = self.mainAgent.game_info.map_center.y * 1.9
            self.mainAgent.map_corner = Point2.from_proto(p)

        # Make sure given strategy num is valid
        if Strategies.has_value(strategy_num):
            # Valid strategy num, convert int into enum value
            strategy = Strategies(strategy_num)

            # Mark strategy as changed or not
            if strategy != self.mainAgent.prev_strategy:
                self.mainAgent.log("New strategy is " + str(strategy))
                self.mainAgent.did_strategy_change = True
                self.mainAgent.strike_force = None
            else:
                self.mainAgent.did_strategy_change = False

            self.mainAgent.prev_strategy = strategy  # Prepare for next iteration
        else:
            self.log_error(f"Unknown strategy number {strategy_num}")
            return

        # Call the proper strategy function

        # Prevent game from crashing
        hatchery = self.mainAgent.bases
        if hatchery == None or hatchery.amount == 0:
            return
        else:
            hatchery = self.mainAgent.bases.ready.random

        # Attack
        if strategy == Strategies.HEAVY_ATTACK:
            await self.heavy_attack(iteration)
        elif strategy == Strategies.MEDIUM_ATTACK:
            await self.medium_attack(iteration)
        elif strategy == Strategies.LIGHT_ATTACK:
            await self.light_attack(iteration)

        # Scouting
        elif strategy == Strategies.HEAVY_SCOUTING:
            await self.heavy_scouting(iteration)
        elif strategy == Strategies.MEDIUM_SCOUTING:
            await self.medium_scouting(iteration)
        elif strategy == Strategies.LIGHT_SCOUTING:
            await self.light_scouting(iteration)

        # Defense
        elif strategy == Strategies.HEAVY_DEFENSE:
            await self.heavy_defense(iteration)
        elif strategy == Strategies.MEDIUM_DEFENSE:
            await self.medium_defense(iteration)
        elif strategy == Strategies.LIGHT_DEFENSE:
            await self.light_defense(iteration)

        # Harass
        elif strategy == Strategies.HEAVY_HARASS:
            await self.heavy_harass(iteration)
        elif strategy == Strategies.MEDIUM_HARASS:
            await self.medium_harass(iteration)
        elif strategy == Strategies.LIGHT_HARASS:
            await self.light_harass(iteration)

        # Unknown
        else:
            self.log("Unknown strategy was given: " + str(strategy))
Exemple #8
0
 def from_proto(cls, proto):
     return cls(Point2.from_proto(proto.pos), proto.radius, proto.tag)
Exemple #9
0
 def position(self) -> Point2:
     """2d position of the blip."""
     return Point2.from_proto(self._proto.pos)
Exemple #10
0
 def positions(self) -> Set[Point2]:
     if self.fake:
         return {Point2.from_proto(self._proto.pos)}
     return {Point2.from_proto(p) for p in self._proto.pos}