Skip to content

Group Combat Behaviors

AMoveGroup dataclass

Bases: CombatGroupBehavior

A-Move group to a target.

Example:

from ares.behaviors.combat.group import AMoveGroup

self.register_behavior(AMoveGroup(units, self.game_info.map_center))

Attributes:

Name Type Description
group list[Unit]

Units we want to control.

group_tags set[int]

The group unit tags.

target Point2

Where the unit is going.

Source code in src/ares/behaviors/combat/group/a_move_group.py
@dataclass
class AMoveGroup(CombatGroupBehavior):
    """A-Move group to a target.

    Example:
    ```py
    from ares.behaviors.combat.group import AMoveGroup

    self.register_behavior(AMoveGroup(units, self.game_info.map_center))
    ```

    Attributes:
        group (list[Unit]): Units we want to control.
        group_tags (set[int]): The group unit tags.
        target (Point2): Where the unit is going.
    """

    group: list[Unit]
    group_tags: set[int]
    target: Union[Point2, Unit]

    def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> bool:
        if len(self.group) == 0:
            return False

        sorted_units: list[Unit] = cy_sorted_by_distance_to(
            self.group, self.target.position, reverse=True
        )
        if self.duplicate_or_similar_order(
            sorted_units[0], self.target, AbilityId.ATTACK
        ):
            return False

        ai.give_same_action(AbilityId.ATTACK, self.group_tags, self.target)
        return True

KeepGroupSafe dataclass

Bases: CombatGroupBehavior

A-Move group to a target.

Example:

from ares.behaviors.combat.group import AMoveGroup

self.register_behavior(AMoveGroup(units, self.game_info.map_center))

Attributes:

Name Type Description
group list[Unit]

Units we want to control.

close_enemy Union[Units, list[Unit]]

Nearby enemy.

grid ndarray

Grid we should check for safety.

attack_in_range_enemy bool

Whether to attack in range if weapon is ready. Defaults to True.

Source code in src/ares/behaviors/combat/group/keep_group_safe.py
@dataclass
class KeepGroupSafe(CombatGroupBehavior):
    """A-Move group to a target.

    Example:
    ```py
    from ares.behaviors.combat.group import AMoveGroup

    self.register_behavior(AMoveGroup(units, self.game_info.map_center))
    ```

    Attributes:
        group: Units we want to control.
        close_enemy: Nearby enemy.
        grid: Grid we should check for safety.
        attack_in_range_enemy: Whether to attack in range if weapon is ready.
            Defaults to True.

    """

    group: list[Unit]
    close_enemy: Union[Units, list[Unit]]
    grid: np.ndarray
    attack_in_range_enemy: bool = True

    def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> bool:
        if len(self.group) == 0:
            return False

        executed: bool = False
        for u in self.group:
            if self.attack_in_range_enemy:
                if ShootTargetInRange(u, self.close_enemy).execute(
                    ai, config, mediator
                ):
                    continue
            if KeepUnitSafe(u, self.grid).execute(ai, config, mediator):
                executed = True

        return executed

PathGroupToTarget dataclass

Bases: CombatGroupBehavior

Path a group to its target destination.

We issue only one action for the whole group and attempt to filter spammed actions.

Example:

from ares.behaviors.combat.group import PathGroupToTarget

group: list[Unit] = [u for u in self.units]
group_tags: set[int] = {u.tag for u in group}
grid: np.ndarray = self.mediator.get_ground_grid
start: Point2 = self.ai.start_location
target: Point2 = self.game_info.map_center

self.register_behavior(
    PathGroupToTarget(start, group, group_tags, grid, target)
)

Attributes:

Name Type Description
start Point2

Where to start the path query.

group list[Unit]

The actual group units.

group_tags set[int]

The units to path.

grid ndarray

2D grid to path on.

target Point2

Target destination.

success_at_distance float

If the unit has gotten this close, consider the path behavior complete. Defaults to 0.0.

sensitivity int

Path precision. Defaults to 5.

smoothing bool

Whether to smooth out the path. Defaults to False.

sense_danger bool

Whether to check for dangers. If none are present, the pathing query is skipped. Defaults to False.

danger_distance float

If sense_danger is True, how far to check for dangers. Defaults to 20.0.

danger_threshold float

Influence at which a danger is respected. Defaults to 5.0.

prevent_duplicate bool

Whether to try to prevent spamming actions. Defaults to True.

Source code in src/ares/behaviors/combat/group/path_group_to_target.py
@dataclass
class PathGroupToTarget(CombatGroupBehavior):
    """Path a group to its target destination.

    We issue only one action for the whole group and
    attempt to filter spammed actions.


    Example:
    ```py
    from ares.behaviors.combat.group import PathGroupToTarget

    group: list[Unit] = [u for u in self.units]
    group_tags: set[int] = {u.tag for u in group}
    grid: np.ndarray = self.mediator.get_ground_grid
    start: Point2 = self.ai.start_location
    target: Point2 = self.game_info.map_center

    self.register_behavior(
        PathGroupToTarget(start, group, group_tags, grid, target)
    )
    ```

    Attributes:
        start: Where to start the path query.
        group: The actual group units.
        group_tags: The units to path.
        grid: 2D grid to path on.
        target: Target destination.
        success_at_distance: If the unit has gotten this close,
            consider the path behavior complete. Defaults to 0.0.
        sensitivity: Path precision. Defaults to 5.
        smoothing: Whether to smooth out the path. Defaults to False.
        sense_danger: Whether to check for dangers. If none are present,
            the pathing query is skipped. Defaults to False.
        danger_distance: If `sense_danger` is True, how far to check for dangers.
            Defaults to 20.0.
        danger_threshold: Influence at which a danger is respected.
            Defaults to 5.0.
        prevent_duplicate: Whether to try to prevent spamming actions.
            Defaults to True.
    """

    start: Point2
    group: list[Unit]
    group_tags: set[int]
    grid: np.ndarray
    target: Point2
    distance_check_squared: float = 26.25
    success_at_distance: float = 0.0
    sensitivity: int = 12
    smoothing: bool = False
    sense_danger: bool = False
    danger_distance: float = 20.0
    danger_threshold: float = 5.0
    prevent_duplicate: bool = True

    def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> bool:
        assert isinstance(
            self.start, Point2
        ), f"{self.start} should be `Point2`, got {type(self.start)}"
        assert isinstance(
            self.target, Point2
        ), f"{self.target} should be `Point2`, got {type(self.target)}"

        if len(self.group) == 0:
            return False

        distance_to_target: float = cy_distance_to(self.start, self.target)
        # no action executed
        if distance_to_target < self.success_at_distance:
            return False

        move_to: Point2 = mediator.find_path_next_point(
            start=self.start,
            target=self.target,
            grid=self.grid,
            sensitivity=self.sensitivity,
            smoothing=self.smoothing,
            sense_danger=self.sense_danger,
            danger_distance=self.danger_distance,
            danger_threshold=self.danger_threshold,
        )

        if self.prevent_duplicate:
            sample_unit: Unit = cy_closest_to(self.start, self.group)

            if sample_unit and self.duplicate_or_similar_order(
                sample_unit,
                move_to,
                AbilityId.MOVE,
                distance_check_squared=self.distance_check_squared,
            ):
                return False

        ai.give_same_action(AbilityId.MOVE, self.group_tags, move_to)
        return True

StutterGroupBack dataclass

Bases: CombatGroupBehavior

Stutter a group back in unison.

Attributes:

Name Type Description
group list[Unit]

The group of units we want to control.

group_tags set[int]

The group unit tags.

group_position Point2

The position where this group is situated.

target Union[Point2, Unit]

Target for the group.

grid ndarray

Grid this group will use to path on.

Source code in src/ares/behaviors/combat/group/stutter_group_back.py
@dataclass
class StutterGroupBack(CombatGroupBehavior):
    """Stutter a group back in unison.


    Attributes:
        group: The group of units we want to control.
        group_tags: The group unit tags.
        group_position: The position where this group is situated.
        target: Target for the group.
        grid: Grid this group will use to path on.

    """

    group: list[Unit]
    group_tags: set[int]
    group_position: Point2
    target: Union[Point2, Unit]
    grid: np.ndarray

    def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> bool:
        if len(self.group) == 0:
            return False

        sorted_units: list[Unit] = cy_sorted_by_distance_to(
            self.group, self.target.position, reverse=True
        )
        sample_unit: Unit = sorted_units[0]

        if self.group_weapons_on_cooldown(self.group, stutter_forward=False):
            group_safe: bool = True
            for unit in self.group:
                if not mediator.is_position_safe(
                    grid=self.grid, position=unit.position
                ):
                    group_safe = False
                    break
            if group_safe:
                return True
            if len(self.group) > 1:
                move_to_target: Point2 = self._calculate_retreat_position(ai)
                safe_spot: Point2 = mediator.find_closest_safe_spot(
                    from_pos=move_to_target, grid=self.grid
                )
            else:
                safe_spot: Point2 = mediator.find_closest_safe_spot(
                    from_pos=self.group_position, grid=self.grid
                )

            if ai.in_pathing_grid(safe_spot):
                group_move_to: Point2 = mediator.find_path_next_point(
                    start=self.group_position,
                    target=safe_spot,
                    grid=self.grid,
                    sensitivity=min(len(self.group), 8),
                )
                if self.duplicate_or_similar_order(
                    sample_unit, group_move_to, AbilityId.MOVE
                ):
                    return True
                ai.give_same_action(AbilityId.MOVE, self.group_tags, group_move_to)
        else:
            if self.duplicate_or_similar_order(
                sample_unit, self.target, AbilityId.ATTACK
            ):
                return True
            ai.give_same_action(AbilityId.ATTACK, self.group_tags, self.target.position)

        return True

    def _calculate_retreat_position(self, ai: "AresBot") -> Point2:
        """Search 8 directions for somewhere to retreat to."""
        distance = len(self.group) * 1.5

        map_bounds = ai.game_info.map_size
        best_position: Point2 = self.group_position
        max_distance_from_target: float = 0.0

        for direction in DIRECTIONS:
            retreat_position: Point2 = self.group_position + direction * distance
            retreat_position = Point2(
                (
                    max(0, min(map_bounds[0] - 1, retreat_position.x)),
                    max(0, min(map_bounds[1] - 1, retreat_position.y)),
                )
            )

            if ai.in_pathing_grid(retreat_position):
                distance_from_target: float = cy_distance_to_squared(
                    self.target.position, retreat_position
                )

                if distance_from_target > max_distance_from_target:
                    max_distance_from_target = distance_from_target
                    best_position = retreat_position

        return best_position

StutterGroupForward dataclass

Bases: CombatGroupBehavior

Stutter a group forward in unison.

Attributes:

Name Type Description
group list[Unit]

The group of units we want to control.

group_tags set[int]

The group unit tags.

group_position Point2

The position where this group is situated.

target Union[Point2, Unit]

Target for the group, used if no enemies are present.

enemies Union[Units, list[Unit]]

The enemy units we want to stutter towards.

Source code in src/ares/behaviors/combat/group/stutter_group_forward.py
@dataclass
class StutterGroupForward(CombatGroupBehavior):
    """Stutter a group forward in unison.

    Attributes:
        group: The group of units we want to control.
        group_tags: The group unit tags.
        group_position: The position where this group is situated.
        target: Target for the group, used if no enemies are present.
        enemies: The enemy units we want to stutter towards.

    """

    group: list[Unit]
    group_tags: set[int]
    group_position: Point2
    target: Union[Point2, Unit]
    enemies: Union[Units, list[Unit]]

    def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> bool:
        if len(self.group) == 0:
            return False

        # no enemies, no actions to carry out
        if not self.enemies:
            return False

        sorted_units: list[Unit] = cy_sorted_by_distance_to(
            self.group, self.target.position
        )
        sample_unit: Unit = sorted_units[0]

        # if all units are in range of something, don't worry about moving
        all_in_range: bool = True
        enemy_center: Point2 = Point2(cy_center(self.enemies))
        for unit in self.group:
            in_attack_range: list[Unit] = cy_in_attack_range(unit, self.enemies)

            if not in_attack_range:
                all_in_range = False
                break

        # if the whole group are in range of something, then don't bother moving
        if all_in_range:
            if not self.duplicate_or_similar_order(
                sample_unit, self.target, AbilityId.ATTACK
            ):
                ai.give_same_action(AbilityId.ATTACK, self.group_tags, enemy_center)

            return True

        if self.group_weapons_on_cooldown(self.group, stutter_forward=True):
            if self.duplicate_or_similar_order(
                sample_unit, enemy_center, AbilityId.MOVE
            ):
                return True
            ai.give_same_action(AbilityId.MOVE, self.group_tags, enemy_center)
        else:
            if self.duplicate_or_similar_order(
                sample_unit, enemy_center, AbilityId.ATTACK
            ):
                return True
            ai.give_same_action(AbilityId.ATTACK, self.group_tags, enemy_center)

        return True

GroupUseAbility dataclass

Bases: CombatGroupBehavior

Issue a single ability command for a group of units.

Example:

from ares.behaviors.combat.group import GroupUseAbility

self.register_behavior(
    GroupUseAbility(
        AbilityId.MOVE_MOVE,
        units,
        {u.tag for u in units}
        self.game_info.map_center
    )
)

Attributes:

Name Type Description
ability AbilityId

Ability we want to use.

group list[Unit]

Units we want to control.

group_tags set[int]

The group unit tags.

target Union[Point2, Unit, None]

The target for this ability.

sync_command bool

(default=True) If True, wait for all units to be ready before trying ability.

Source code in src/ares/behaviors/combat/group/group_use_ability.py
@dataclass
class GroupUseAbility(CombatGroupBehavior):
    """Issue a single ability command for a group of units.

    Example:
    ```py
    from ares.behaviors.combat.group import GroupUseAbility

    self.register_behavior(
        GroupUseAbility(
            AbilityId.MOVE_MOVE,
            units,
            {u.tag for u in units}
            self.game_info.map_center
        )
    )
    ```

    Attributes:
        ability: Ability we want to use.
        group: Units we want to control.
        group_tags: The group unit tags.
        target: The target for this ability.
        sync_command: (default=True) If True, wait for all units
            to be ready before trying ability.
    """

    ability: AbilityId
    group: list[Unit]
    group_tags: set[int]
    target: Union[Point2, Unit, None]
    sync_command: bool = True

    def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> bool:
        if len(self.group) == 0:
            return False

        issue_command: bool
        if self.sync_command:
            issue_command = all([self.ability in u.abilities for u in self.group])
        else:
            issue_command = any([self.ability in u.abilities for u in self.group])

        if not issue_command:
            return False

        ai.give_same_action(self.ability, self.group_tags, self.target)
        return True