gale.ai.tactical module

This file contains support for tactical/strategic decisions: an InfluenceMap tracks, over a grid, how much presence each side has across an area (propagated outward from wherever influence was added, decaying with distance), so an agent can query “who controls this spot” or “which of these candidate positions is safest/most contested” instead of only reacting to what’s immediately visible. It is deliberately generic about what “influence” means – add whatever sources make sense for your game (unit positions, alarms, resources) and score positions with best_position’s score function.

Author: Alejandro Mujica (aledrums@gmail.com)

class gale.ai.tactical.InfluenceMap(width, height, cell_size=40)[source]

Bases: object

A grid over a rectangular world area, each cell accumulating a signed influence value per team: positive for one team, negative for another, so value_at reads as “how strongly, and by whom, is this position controlled”.

Usage example:

influence_map = InfluenceMap(width=800, height=600, cell_size=40) for enemy in enemies:

influence_map.add_influence(enemy.position, strength=1.0, radius=150, team=”enemy”)

for ally in allies:

influence_map.add_influence(ally.position, strength=1.0, radius=150, team=”ally”)

influence_map.propagate() influence_map.dominance_at(candidate_position) # > 0: ally-favored, < 0: enemy-favored

Parameters:
  • width (float)

  • height (float)

  • cell_size (float)

add_influence(position, strength, radius, team='default')[source]

Add a source of influence centered on position, falling off linearly to 0 at radius. Call propagate() after adding every source for a round to get a smoothed result.

Parameters:
  • position (Vector2) – World position the influence radiates from.

  • strength (float) – Influence value at position itself.

  • radius (float) – Distance at and beyond which this source no longer contributes.

  • team (str) – Which team’s influence this source adds to. The default value is “default”.

Return type:

None

propagate(iterations=1, decay=0.5)[source]

Blur each team’s influence into neighboring cells, so influence spreads a little beyond exactly where it was added.

Parameters:
  • iterations (int) – Number of blur passes to perform. The default value is 1.

  • decay (float) – Fraction of a cell’s influence that spreads into each direct neighbor per pass. The default value is 0.5.

Return type:

None

value_at(position, team='default')[source]
Parameters:
  • position (Vector2) – The world position to query.

  • team (str) – Which team’s influence to read. The default value is “default”.

Returns:

The accumulated influence of team at position’s cell, or 0 if none has reached it.

Return type:

float

dominance_at(position, team_a='ally', team_b='enemy')[source]
Parameters:
  • position (Vector2) – The world position to query.

  • team_a (str) – The team whose dominance reads as positive. The default value is “ally”.

  • team_b (str) – The team whose dominance reads as negative. The default value is “enemy”.

Returns:

team_a’s influence at position minus team_b’s.

Return type:

float

clear()[source]

Remove every source of influence added so far.

Return type:

None

gale.ai.tactical.best_position(candidates, score)[source]

Pick whichever of candidates scores highest – a generic tactical waypoint scorer, meant to be paired with any scoring function you like, including one that reads from an InfluenceMap (e.g. “prefer positions dominated by my team but still within some distance of the enemy”, or a cover system, or line-of-sight checks).

Parameters:
  • candidates (Sequence[Vector2]) – The positions to choose among.

  • score (Callable[[Vector2], float]) – Callable(position) -> float ranking a candidate; higher is better.

Returns:

The highest-scoring candidate, or None if candidates is empty.

Return type:

Vector2 | None