Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pettingzoo/env_registry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def _maybe_register_parallel(

_sisl_envs = [
("multiwalker_v9", "multiwalker"),
("pursuit_v5", "pursuit"),
("pursuit_v6", "pursuit"),
]

for _id, _base in _sisl_envs:
Expand Down
4 changes: 2 additions & 2 deletions pettingzoo/sisl/all_modules.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pettingzoo.sisl import multiwalker_v9, pursuit_v5
from pettingzoo.sisl import multiwalker_v9, pursuit_v6

sisl_environments = {
"sisl/multiwalker_v9": multiwalker_v9,
"sisl/pursuit_v5": pursuit_v5,
"sisl/pursuit_v6": pursuit_v6,
}
6 changes: 3 additions & 3 deletions pettingzoo/sisl/pursuit/manual_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ def available_agents(self):


if __name__ == "__main__":
from pettingzoo.sisl import pursuit_v5
from pettingzoo.sisl import pursuit_v6

clock = pygame.time.Clock()

env = pursuit_v5.env()
env = pursuit_v6.env()
env.reset()

manual_policy = pursuit_v5.ManualPolicy(env)
manual_policy = pursuit_v6.ManualPolicy(env)

for agent in env.agent_iter():
clock.tick(env.metadata["render_fps"])
Expand Down
24 changes: 20 additions & 4 deletions pettingzoo/sisl/pursuit/pursuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

This environment is part of the <a href='..'>SISL environments</a>. Please read that page first for general information.

| Creation | `make("aec", "sisl/pursuit-v5")` |
| Creation | `make("aec", "sisl/pursuit-v6")` |
|----------------------|--------------------------------------------------------|
| Actions | Discrete |
| Parallel API | Yes |
Expand All @@ -32,6 +32,18 @@

The state takes the full form of `(y_size, x_size, 3)`, with the same three channels as the observations, but covering the whole map instead of the `obs_range` box around each agent.

### Center obstacle

The `center_box_size` argument controls the size of the centered obstacle in
grid cells. Pass a `(width, height)` tuple to set an explicit size, for example
`center_box_size=(4, 2)`. Passing `(0, 0)` removes the obstacle entirely, while
the default `None` preserves the obstacle dimensions used by earlier versions
of Pursuit.

Both dimensions must be non-negative integers, cannot exceed the corresponding
map dimension, and cannot cover the entire map. This argument was introduced in
`pursuit_v6`; code using `pursuit_v5` must update its import to use it.

### Manual Control

Select different pursuers with 'J' and 'K'. The selected pursuer can be moved with the arrow keys.
Expand All @@ -42,9 +54,10 @@
```python
from pettingzoo import make

make("aec", "sisl/pursuit-v5", max_cycles=500, x_size=16, y_size=16, shared_reward=True,
make("aec", "sisl/pursuit-v6", max_cycles=500, x_size=16, y_size=16, shared_reward=True,
n_evaders=30, n_pursuers=8, obs_range=7, n_catch=2, freeze_evaders=False, tag_reward=0.01,
catch_reward=5.0, urgency_reward=-0.1, surround=True, constraint_window=1.0)
catch_reward=5.0, urgency_reward=-0.1, surround=True, constraint_window=1.0,
center_box_size=None)
```

`x_size, y_size`: Size of environment world space
Expand All @@ -71,11 +84,14 @@

`constraint_window`: Size of box (from center, in proportional units) which agents can randomly spawn into the environment world. Default is 1.0, which means they can spawn anywhere on the map. A value of 0 means all agents spawn in the center.

`center_box_size`: Optional `(width, height)` of the center obstacle in grid cells. The default `None` preserves the original proportional obstacle size. Use `(0, 0)` for a map without a center obstacle.

`max_cycles`: After max_cycles steps all agents will return done


### Version History

* v6: Add `center_box_size` to control or remove the center obstacle
* v5: Add state() and state space support (1.27.0)
* v4: Change the reward sharing, fix a collection bug, add agent counts to the rendering (1.14.0)
* v3: Observation space bug fixed (1.5.0)
Expand Down Expand Up @@ -110,7 +126,7 @@ def env(**kwargs):
class raw_env(AECEnv, EzPickle):
metadata = {
"render_modes": ["human", "rgb_array"],
"name": "pursuit_v5",
"name": "pursuit_v6",
"is_parallelizable": True,
"render_fps": 5,
"has_manual_policy": True,
Expand Down
6 changes: 5 additions & 1 deletion pettingzoo/sisl/pursuit/pursuit_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(
surround: bool = True,
render_mode=None,
constraint_window: float = 1.0,
center_box_size: tuple[int, int] | None = None,
):
"""In evade pursuit a set of pursuers must 'tag' a set of evaders.

Expand All @@ -57,10 +58,13 @@ def __init__(
urgency_reward: reward added in each step
surround: toggles surround condition for evader removal
constraint_window: window in which agents can randomly spawn
center_box_size: width and height of the center obstacle in grid cells
"""
self.x_size = x_size
self.y_size = y_size
self.map_matrix = two_d_maps.rectangle_map(self.x_size, self.y_size)
self.map_matrix = two_d_maps.rectangle_map(
self.x_size, self.y_size, center_box_size=center_box_size
)
self.max_cycles = max_cycles
self._seed()

Expand Down
64 changes: 60 additions & 4 deletions pettingzoo/sisl/pursuit/test_pursuit.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import numpy as np
import pytest

from pettingzoo.sisl import pursuit_v5
from pettingzoo.sisl import pursuit_v6
from pettingzoo.sisl.pursuit.utils import two_d_maps


def test_state_matches_model_state():
# use a non-square map so axis ordering mistakes are caught
env = pursuit_v5.env(x_size=8, y_size=19, max_cycles=40)
env = pursuit_v6.env(x_size=8, y_size=19, max_cycles=40)
env.reset(seed=42)
base_env = env.unwrapped.env
for agent in env.agent_iter(env.num_agents * 4):
Expand All @@ -22,7 +24,7 @@ def test_state_matches_model_state():


def test_observations_are_crops_of_state():
env = pursuit_v5.env(max_cycles=40)
env = pursuit_v6.env(max_cycles=40)
env.reset(seed=0)
base_env = env.unwrapped.env
for agent in env.agent_iter(env.num_agents * 4):
Expand All @@ -40,7 +42,61 @@ def test_observations_are_crops_of_state():


def test_parallel_state():
par_env = pursuit_v5.parallel_env(max_cycles=40)
par_env = pursuit_v6.parallel_env(max_cycles=40)
par_env.reset(seed=42)
state = par_env.state()
assert par_env.state_space.contains(state)


def test_center_box_size():
env = pursuit_v6.env(
x_size=8,
y_size=10,
n_evaders=1,
n_pursuers=1,
center_box_size=(4, 2),
)
expected_map = np.zeros((8, 10), dtype=np.int32)
expected_map[2:6, 4:6] = -1
assert np.array_equal(env.unwrapped.env.map_matrix, expected_map)


def test_zero_center_box_size_removes_obstacle():
env = pursuit_v6.env(
x_size=8,
y_size=10,
n_evaders=1,
n_pursuers=1,
center_box_size=(0, 0),
)
assert np.count_nonzero(env.unwrapped.env.map_matrix) == 0


def test_default_center_box_size_is_unchanged():
env = pursuit_v6.env(n_evaders=1, n_pursuers=1)
assert np.array_equal(
env.unwrapped.env.map_matrix,
two_d_maps.rectangle_map(env.unwrapped.env.x_size, env.unwrapped.env.y_size),
)


@pytest.mark.parametrize(
("center_box_size", "error"),
[
([2, 2], TypeError),
((2,), TypeError),
((2.0, 2), TypeError),
((-1, 2), ValueError),
((9, 2), ValueError),
((8, 10), ValueError),
],
)
def test_invalid_center_box_size(center_box_size, error):
with pytest.raises(error):
pursuit_v6.env(
x_size=8,
y_size=10,
n_evaders=1,
n_pursuers=1,
center_box_size=center_box_size,
)
36 changes: 32 additions & 4 deletions pettingzoo/sisl/pursuit/utils/two_d_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,41 @@
from scipy.ndimage import zoom


def rectangle_map(xs, ys, xb=0.3, yb=0.2):
"""Returns a 2D 'map' with a rectangle building centered in the middle.
def rectangle_map(xs, ys, xb=0.3, yb=0.2, center_box_size=None):
"""Returns a 2D map with a rectangle building centered in the middle.

Map is a 2D numpy array
xb and yb are buffers for each dim representing the raio of the map to leave open on each side
``center_box_size`` is an optional ``(width, height)`` tuple measured in
grid cells. When omitted, the historical proportional sizing controlled by
``xb`` and ``yb`` is preserved.
"""
rmap = np.zeros((xs, ys), dtype=np.int32)

if center_box_size is not None:
if (
not isinstance(center_box_size, tuple)
or len(center_box_size) != 2
or any(
not isinstance(size, int) or isinstance(size, bool)
for size in center_box_size
)
):
raise TypeError("center_box_size must be a tuple of two integers or None")

box_width, box_height = center_box_size
if box_width < 0 or box_height < 0:
raise ValueError("center_box_size dimensions must be non-negative")
if box_width > xs or box_height > ys:
raise ValueError(
"center_box_size dimensions cannot exceed the map dimensions"
)
if box_width == xs and box_height == ys:
raise ValueError("center_box_size must leave at least one free grid cell")

x_start = (xs - box_width) // 2
y_start = (ys - box_height) // 2
rmap[x_start : x_start + box_width, y_start : y_start + box_height] = -1
return rmap

for i in range(xs):
for j in range(ys):
# are we in the rectnagle in x dim?
Expand Down
File renamed without changes.
23 changes: 14 additions & 9 deletions test/all_parameter_combs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
texas_holdem_v4,
tictactoe_v3,
)
from pettingzoo.sisl import multiwalker_v9, pursuit_v5
from pettingzoo.sisl import multiwalker_v9, pursuit_v6
from pettingzoo.test import max_cycles_test, parallel_api_test
from pettingzoo.test.api_test import api_test
from pettingzoo.test.render_test import render_test
Expand Down Expand Up @@ -230,17 +230,22 @@
multiwalker_v9,
{"terminate_on_fall": False, "remove_on_fall": False, "max_cycles": 50},
],
["sisl/pursuit_v5", pursuit_v5, {"max_cycles": 50}],
["sisl/pursuit_v5", pursuit_v5, {"x_size": 8, "y_size": 19, "max_cycles": 50}],
["sisl/pursuit_v5", pursuit_v5, {"shared_reward": True, "max_cycles": 50}],
["sisl/pursuit_v6", pursuit_v6, {"max_cycles": 50}],
["sisl/pursuit_v6", pursuit_v6, {"x_size": 8, "y_size": 19, "max_cycles": 50}],
["sisl/pursuit_v6", pursuit_v6, {"shared_reward": True, "max_cycles": 50}],
[
"sisl/pursuit_v5",
pursuit_v5,
"sisl/pursuit_v6",
pursuit_v6,
{"n_evaders": 5, "n_pursuers": 16, "max_cycles": 50},
],
["sisl/pursuit_v5", pursuit_v5, {"obs_range": 15, "max_cycles": 50}],
["sisl/pursuit_v5", pursuit_v5, {"n_catch": 3, "max_cycles": 50}],
["sisl/pursuit_v5", pursuit_v5, {"freeze_evaders": True, "max_cycles": 50}],
["sisl/pursuit_v6", pursuit_v6, {"obs_range": 15, "max_cycles": 50}],
["sisl/pursuit_v6", pursuit_v6, {"n_catch": 3, "max_cycles": 50}],
["sisl/pursuit_v6", pursuit_v6, {"freeze_evaders": True, "max_cycles": 50}],
[
"sisl/pursuit_v6",
pursuit_v6,
{"center_box_size": (4, 2), "max_cycles": 50},
],
]


Expand Down
4 changes: 2 additions & 2 deletions test/pygame_init_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
texas_holdem_v4,
tictactoe_v3,
)
from pettingzoo.sisl import multiwalker_v9, pursuit_v5
from pettingzoo.sisl import multiwalker_v9, pursuit_v6

pygame_envs = [
cooperative_pong_v6,
Expand All @@ -32,7 +32,7 @@
texas_holdem_v4,
tictactoe_v3,
multiwalker_v9,
pursuit_v5,
pursuit_v6,
]


Expand Down