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

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

group : list[Unit] Units we want to control. close_enemy : Union[Units, list[Unit]] Nearby enemy. grid : np.ndarray Grid we should check for safety. attack_in_range_enemy : bool (default=True) Attack in range if weapon is ready.

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 : list[Unit]
        Units we want to control.
    close_enemy : Union[Units, list[Unit]]
        Nearby enemy.
    grid : np.ndarray
        Grid we should check for safety.
    attack_in_range_enemy : bool (default=True)
        Attack in range if weapon is ready.
    """

    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

start : Point2 Where to start the path query. group: list[Unit] The actual group units. group_tags : set[int] The units to path. grid : np.ndarray 2D Grid to path on. target : Point2 Target destination. success_at_distance : float (default: 0.0) If unit has got this close, consider path behavior complete. sensitivity : int (default: 5) Path precision. smoothing : bool (default: False) Smooth out the path. sense_danger : bool (default: False) Check for dangers, if none are present pathing query is skipped. danger_distance : float (default: 20.0) If sense_danger=True, how far to check for dangers? danger_threshold : float (default: 5.0) Influence at which a danger is respected. prevent_duplicate : bool (default: True) Try to prevent spamming action.

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 : Point2
        Where to start the path query.
    group: list[Unit]
        The actual group units.
    group_tags : set[int]
        The units to path.
    grid : np.ndarray
        2D Grid to path on.
    target : Point2
        Target destination.
    success_at_distance : float (default: 0.0)
        If unit has got this close, consider path behavior complete.
    sensitivity : int (default: 5)
        Path precision.
    smoothing : bool (default: False)
        Smooth out the path.
    sense_danger : bool (default: False)
        Check for dangers, if none are present pathing query is skipped.
    danger_distance : float (default: 20.0)
        If sense_danger=True, how far to check for dangers?
    danger_threshold : float (default: 5.0)
        Influence at which a danger is respected.
    prevent_duplicate : bool (default: True)
        Try to prevent spamming action.
    """

    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

group : Unit The group of units we want to control. group_tags: Point2 The group unit tags. group_position : Point2 The position where this group is situated. target : Union[Point2, Unit] Target for the group. grid : np.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 : Unit
        The group of units we want to control.
    group_tags: Point2
        The group unit tags.
    group_position : Point2
        The position where this group is situated.
    target : Union[Point2, Unit]
        Target for the group.
    grid : np.ndarray
        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.

        Parameters
        ----------
        ai

        Returns
        -------

        """
        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

calculate_retreat_position(ai)

Search 8 directions for somewhere to retreat to.

Parameters

ai

Returns
Source code in src/ares/behaviors/combat/group/stutter_group_back.py
def calculate_retreat_position(self, ai: "AresBot") -> Point2:
    """
    Search 8 directions for somewhere to retreat to.

    Parameters
    ----------
    ai

    Returns
    -------

    """
    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

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 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 : 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 present.
    enemies : Union[Units, list[Unit]]
        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

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: 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
    """

    ability: AbilityId
    group: list[Unit]
    group_tags: set[int]
    target: Union[Point2, Unit]
    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