Skip to content

games: add pursuit-evasion game (2-player zero-sum, continuous 2D space) - #1553

Open
aeyjeyaryan wants to merge 1 commit into
google-deepmind:masterfrom
aeyjeyaryan:add-pursuit-evasion-game
Open

games: add pursuit-evasion game (2-player zero-sum, continuous 2D space)#1553
aeyjeyaryan wants to merge 1 commit into
google-deepmind:masterfrom
aeyjeyaryan:add-pursuit-evasion-game

Conversation

@aeyjeyaryan

Copy link
Copy Markdown

This PR adds a new Python game, python_pursuit_evasion, to address issue #843 (Call for New Games).

Game overview

A 2-player zero-sum pursuit-evasion game in bounded 2D continuous space:

  • Player 0 (Pursuer): chooses a move direction each turn from 9 discrete actions (8 compass directions + stay). Movement is unit-length in any direction.
  • Player 1 (Evader): moves automatically according to a parameterized strategy. Has exactly one no-op legal action.
  • Capture condition: Euclidean distance < capture_radius (default 1.0).
  • Reward: +1 for pursuer if capture occurs within max_steps rounds, −1 otherwise (zero-sum).
  • Game parameters: grid_size (default 10.0), max_steps (default 50), capture_radius (default 1.0), evader_strategy (0–3).

Evader strategies

ID Name Behavior
0 Random Moves in a uniformly random direction each turn
1 Constant-velocity Moves East every turn
2 Zigzag Alternates between NE and SE
3 Adaptive Moves directly away from the pursuer (unit vector of evader − pursuer)

Implementation details

  • Follows the standard OpenSpiel Python game pattern (pyspiel.Game, pyspiel.State, observer class, registration).
  • Observation/information-state tensor: 5-element normalized vector [pursuer_x/grid_size, pursuer_y/grid_size, evader_x/grid_size, evader_y/grid_size, step/max_steps].
  • Sequential turn structure: pursuer moves → evader moves → capture check → repeat.

Tests

12 tests covering API conformance (pyspiel.random_sim_test), all 4 evader strategies, both terminal conditions, legal action counts, zero-sum property, observation tensor shape/range, and deterministic replay.

Additional context

This game accompanies a research paper comparing NEAT and PPO under non-stationary opponent strategies (IEEE Access, under submission). The adaptive evader strategy shifts the evasion policy during an episode, creating a non-stationary environment where population-based methods (NEAT) outperform gradient-based methods (PPO). The game is designed for use with OpenSpiel's Python algorithms and can serve as a benchmark for multi-agent RL under strategy distribution shifts.

@google-cla

google-cla Bot commented Jun 9, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@aeyjeyaryan
aeyjeyaryan force-pushed the add-pursuit-evasion-game branch from 14c084c to 861319b Compare June 12, 2026 13:43
Comment thread open_spiel/python/games/pursuit_evasion.py
@lanctot lanctot added the imported This PR has been imported and awaiting internal review. Please avoid any more local changes, thanks! label Jul 11, 2026
@aeyjeyaryan
aeyjeyaryan force-pushed the add-pursuit-evasion-game branch from 861319b to e91503a Compare July 11, 2026 16:05
@lanctot

lanctot commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

@aeyjeyaryan could you add the game to the games list? (in docs/games.md)?

@aeyjeyaryan
aeyjeyaryan force-pushed the add-pursuit-evasion-game branch 2 times, most recently from 29af2e0 to f4f282d Compare July 11, 2026 16:58
@lanctot
lanctot requested review from dhennes and lanctot July 13, 2026 10:35

@lanctot lanctot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments from internal review thanks to @dhennes

"""Pursuit-evasion game in 2D continuous space.

A 2-player zero-sum game where a pursuer (Player 0) attempts to capture an
evader (Player 1) within a bounded 2D grid. The evader follows one of four

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the game takes place in a continuous space and the players don't snap to discrete cells, consider changing "grid" to "box" or "space" to avoid confusion.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly elsewhere in the file (grid_size_ should be renamed)

class PursuitEvasionGame(pyspiel.Game):
"""2-player zero-sum pursuit-evasion game in continuous 2D space.

The pursuer (Player 0) takes discrete directional actions each turn. The

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding the opponent's strategy into the game state and hiding stochasticity behind a dummy action breaks OpenSpiel's tree-search assumptions.

Because _EVADER_RANDOM uses self._rng, applying action 0 is stochastic. In OpenSpiel, stochastic transitions should be explicitly modeled as chance nodes so search algorithms can branch correctly. Furthermore, without a custom clone() method, duplicating this state in a search tree will share the same RNG object or fail to copy it correctly.

If this is meant to be a 2-player game, consider exposing the 9 actions to Player 1 and moving these hardcoded strategies (random, zigzag, etc.) into external pyspiel.Bot implementations. If it's meant to be a 1-player environment against a fixed behavior, Player 1's turn should be formally implemented as a chance node.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, Marc! I've addressed both points in the most recent push.

@lanctot lanctot removed the imported This PR has been imported and awaiting internal review. Please avoid any more local changes, thanks! label Jul 13, 2026
@aeyjeyaryan
aeyjeyaryan force-pushed the add-pursuit-evasion-game branch 2 times, most recently from d909843 to 7ec847b Compare July 13, 2026 15:34
self._is_chance = False
self._is_terminal = False
self.pursuer_reward = 0.0
self._rng = np.random.RandomState(game.seed)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be no random number generator in the state or the game. All randomness should be controlled externally and change events handelled by chance nodes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to no longer be used, right? Please remove.

each pursuer action the evader moves via a chance node whose probability
distribution is determined by the ``evader_strategy`` parameter. The game
ends when the pursuer captures the evader (distance < capture_radius) or
after ``max_steps`` rounds.

@lanctot lanctot Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this.

First: why make this a single-player game instead of a two-player game?

Second: in the case where it is a single player game, then bots should not be acting at chance nodes (i.e. see the test where several different types of evaders are tested). Chance nodes have fixed distributions: you should only sample from their (categorical) distribution.

I think the better choice here is keep the evader bots and make it proper two-player game. Then use the bots when it's the evader's turn.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clear direction, addressed all three points in the recent push!

@aeyjeyaryan
aeyjeyaryan force-pushed the add-pursuit-evasion-game branch from 7ec847b to e8bd22c Compare July 14, 2026 05:09
@aeyjeyaryan
aeyjeyaryan requested a review from lanctot July 23, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants