multi-agent-rlenv 3.3.0__tar.gz → 3.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/.github/workflows/ci.yaml +4 -4
  2. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/.github/workflows/docs.yaml +1 -1
  3. multi_agent_rlenv-3.3.0/README.md → multi_agent_rlenv-3.3.1/PKG-INFO +59 -2
  4. multi_agent_rlenv-3.3.0/PKG-INFO → multi_agent_rlenv-3.3.1/README.md +24 -17
  5. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/pyproject.toml +21 -1
  6. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/__init__.py +1 -1
  7. multi_agent_rlenv-3.3.1/src/marlenv/adapters/__init__.py +42 -0
  8. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/adapters/overcooked_adapter.py +43 -13
  9. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/env_builder.py +26 -49
  10. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/env_pool.py +0 -1
  11. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/observation.py +6 -1
  12. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_adapters.py +56 -24
  13. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_serialization.py +7 -1
  14. multi_agent_rlenv-3.3.0/src/marlenv/adapters/__init__.py +0 -32
  15. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/.gitignore +0 -0
  16. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/LICENSE +0 -0
  17. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/adapters/gym_adapter.py +0 -0
  18. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/adapters/pettingzoo_adapter.py +0 -0
  19. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/adapters/pymarl_adapter.py +0 -0
  20. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/adapters/smac_adapter.py +0 -0
  21. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/exceptions.py +0 -0
  22. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/mock_env.py +0 -0
  23. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/__init__.py +0 -0
  24. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/env.py +0 -0
  25. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/episode.py +0 -0
  26. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/spaces.py +0 -0
  27. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/state.py +0 -0
  28. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/step.py +0 -0
  29. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/models/transition.py +0 -0
  30. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/py.typed +0 -0
  31. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/__init__.py +0 -0
  32. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/agent_id_wrapper.py +0 -0
  33. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/available_actions_mask.py +0 -0
  34. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/available_actions_wrapper.py +0 -0
  35. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/blind_wrapper.py +0 -0
  36. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/centralised.py +0 -0
  37. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/delayed_rewards.py +0 -0
  38. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/last_action_wrapper.py +0 -0
  39. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/paddings.py +0 -0
  40. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/penalty_wrapper.py +0 -0
  41. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/rlenv_wrapper.py +0 -0
  42. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/time_limit.py +0 -0
  43. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/src/marlenv/wrappers/video_recorder.py +0 -0
  44. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/__init__.py +0 -0
  45. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_episode.py +0 -0
  46. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_models.py +0 -0
  47. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_pool.py +0 -0
  48. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_spaces.py +0 -0
  49. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/test_wrappers.py +0 -0
  50. {multi_agent_rlenv-3.3.0 → multi_agent_rlenv-3.3.1}/tests/utils.py +0 -0
@@ -21,8 +21,8 @@ jobs:
21
21
  matrix:
22
22
  os:
23
23
  - ubuntu-latest
24
- - windows-latest
25
24
  - macOS-latest
25
+ - windows-latest
26
26
  target:
27
27
  - x86_64
28
28
  - aarch64
@@ -43,16 +43,16 @@ jobs:
43
43
  - name: Install uv
44
44
  uses: yezz123/setup-uv@v4
45
45
  with:
46
- uv-version: 0.5.6
46
+ uv-version: 0.6.4
47
47
  - name: Install dependencies and run pytest
48
48
  run: |
49
- uv sync
49
+ uv sync --extra overcooked --extra gym --extra pettingzoo
50
50
  uv run pytest
51
51
 
52
52
  build:
53
53
  name: 📦 Build package
54
54
  if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
55
- needs: test
55
+ needs: [test]
56
56
  runs-on: ubuntu-latest
57
57
  steps:
58
58
  - uses: actions/checkout@v4
@@ -4,7 +4,7 @@ name: Deploy static content to Pages
4
4
  on:
5
5
  # Runs on pushes targeting the default branch
6
6
  push:
7
- branches: ["main"]
7
+ branches: ["main", "master"]
8
8
  # Only deploy the documentation on new version tags
9
9
  tags:
10
10
  - 'v[0-9]+.[0-9]+.[0-9]+'
@@ -1,8 +1,65 @@
1
- # `marlenv` - A unified interface for muti-agent reinforcement learning
1
+ Metadata-Version: 2.4
2
+ Name: multi-agent-rlenv
3
+ Version: 3.3.1
4
+ Summary: A strongly typed Multi-Agent Reinforcement Learning framework
5
+ Project-URL: repository, https://github.com/yamoling/multi-agent-rlenv
6
+ Author-email: Yannick Molinghen <yannick.molinghen@ulb.be>
7
+ License-File: LICENSE
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: <4,>=3.10
11
+ Requires-Dist: numpy>=2.0.0
12
+ Requires-Dist: opencv-python>=4.0
13
+ Requires-Dist: typing-extensions>=4.0
14
+ Provides-Extra: all
15
+ Requires-Dist: gymnasium>0.29.1; extra == 'all'
16
+ Requires-Dist: overcooked-ai; extra == 'all'
17
+ Requires-Dist: pettingzoo>=1.20; extra == 'all'
18
+ Requires-Dist: pymunk>=6.0; extra == 'all'
19
+ Requires-Dist: pysc2; extra == 'all'
20
+ Requires-Dist: scipy>=1.10; extra == 'all'
21
+ Requires-Dist: smac; extra == 'all'
22
+ Provides-Extra: gym
23
+ Requires-Dist: gymnasium>=0.29.1; extra == 'gym'
24
+ Provides-Extra: overcooked
25
+ Requires-Dist: overcooked-ai>=1.1.0; extra == 'overcooked'
26
+ Requires-Dist: scipy>=1.10; extra == 'overcooked'
27
+ Provides-Extra: pettingzoo
28
+ Requires-Dist: pettingzoo>=1.20; extra == 'pettingzoo'
29
+ Requires-Dist: pymunk>=6.0; extra == 'pettingzoo'
30
+ Requires-Dist: scipy>=1.10; extra == 'pettingzoo'
31
+ Provides-Extra: smac
32
+ Requires-Dist: pysc2; extra == 'smac'
33
+ Requires-Dist: smac; extra == 'smac'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # `marlenv` - A unified framework for muti-agent reinforcement learning
37
+ **Documentation: [https://yamoling.github.io/multi-agent-rlenv](https://yamoling.github.io/multi-agent-rlenv)**
38
+
2
39
  The objective of `marlenv` is to provide a common (typed) interface for many different reinforcement learning environments.
3
40
 
4
41
  As such, `marlenv` provides high level abstractions of RL concepts such as `Observation`s or `Transition`s that are commonly represented as mere (confusing) lists or tuples.
5
42
 
43
+ ## Installation
44
+ Install with you preferred package manager (`uv`, `pip`, `poetry`, ...):
45
+ ```bash
46
+ $ pip install marlenv[all] # Enable all features
47
+ $ pip install marlenv # Basic installation
48
+ ```
49
+
50
+ There are multiple optional dependencies if you want to support specific libraries and environments. Available options are:
51
+ - `smac` for StarCraft II environments
52
+ - `gym` for OpenAI Gym environments
53
+ - `pettingzoo` for PettingZoo environments
54
+ - `overcooked` for Overcooked environments
55
+
56
+ Install them with:
57
+ ```bash
58
+ $ pip install marlenv[smac] # Install SMAC
59
+ $ pip install marlenv[gym,smac] # Install Gym & smac support
60
+ ```
61
+
62
+
6
63
  ## Using `marlenv` with existing libraries
7
64
  `marlenv` unifies multiple popular libraries under a single interface. Namely, `marlenv` supports `smac`, `gymnasium` and `pettingzoo`.
8
65
 
@@ -32,7 +89,7 @@ from marlenv import RLEnv, DiscreteActionSpace, Observation
32
89
  N_AGENTS = 3
33
90
  N_ACTIONS = 5
34
91
 
35
- class CustomEnv(RLEnv[DiscreteActionSpace]):
92
+ class CustomEnv(MARLEnv[DiscreteActionSpace]):
36
93
  def __init__(self, width: int, height: int):
37
94
  super().__init__(
38
95
  action_space=DiscreteActionSpace(N_AGENTS, N_ACTIONS),
@@ -1,23 +1,30 @@
1
- Metadata-Version: 2.4
2
- Name: multi-agent-rlenv
3
- Version: 3.3.0
4
- Summary: A strongly typed Multi-Agent Reinforcement Learning framework
5
- Project-URL: repository, https://github.com/yamoling/multi-agent-rlenv
6
- Author-email: Yannick Molinghen <yannick.molinghen@ulb.be>
7
- License-File: LICENSE
8
- Classifier: Operating System :: OS Independent
9
- Classifier: Programming Language :: Python :: 3
10
- Requires-Python: <4,>=3.10
11
- Requires-Dist: gymnasium>=0.29.1
12
- Requires-Dist: numpy>=2.0.0
13
- Requires-Dist: opencv-python>=4.10.0.84
14
- Description-Content-Type: text/markdown
15
-
16
- # `marlenv` - A unified interface for muti-agent reinforcement learning
1
+ # `marlenv` - A unified framework for muti-agent reinforcement learning
2
+ **Documentation: [https://yamoling.github.io/multi-agent-rlenv](https://yamoling.github.io/multi-agent-rlenv)**
3
+
17
4
  The objective of `marlenv` is to provide a common (typed) interface for many different reinforcement learning environments.
18
5
 
19
6
  As such, `marlenv` provides high level abstractions of RL concepts such as `Observation`s or `Transition`s that are commonly represented as mere (confusing) lists or tuples.
20
7
 
8
+ ## Installation
9
+ Install with you preferred package manager (`uv`, `pip`, `poetry`, ...):
10
+ ```bash
11
+ $ pip install marlenv[all] # Enable all features
12
+ $ pip install marlenv # Basic installation
13
+ ```
14
+
15
+ There are multiple optional dependencies if you want to support specific libraries and environments. Available options are:
16
+ - `smac` for StarCraft II environments
17
+ - `gym` for OpenAI Gym environments
18
+ - `pettingzoo` for PettingZoo environments
19
+ - `overcooked` for Overcooked environments
20
+
21
+ Install them with:
22
+ ```bash
23
+ $ pip install marlenv[smac] # Install SMAC
24
+ $ pip install marlenv[gym,smac] # Install Gym & smac support
25
+ ```
26
+
27
+
21
28
  ## Using `marlenv` with existing libraries
22
29
  `marlenv` unifies multiple popular libraries under a single interface. Namely, `marlenv` supports `smac`, `gymnasium` and `pettingzoo`.
23
30
 
@@ -47,7 +54,7 @@ from marlenv import RLEnv, DiscreteActionSpace, Observation
47
54
  N_AGENTS = 3
48
55
  N_ACTIONS = 5
49
56
 
50
- class CustomEnv(RLEnv[DiscreteActionSpace]):
57
+ class CustomEnv(MARLEnv[DiscreteActionSpace]):
51
58
  def __init__(self, width: int, height: int):
52
59
  super().__init__(
53
60
  action_space=DiscreteActionSpace(N_AGENTS, N_ACTIONS),
@@ -7,13 +7,28 @@ authors = [
7
7
  ]
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10, <4"
10
- dependencies = ["numpy>=2.0.0", "opencv-python>=4.10.0.84", "gymnasium>=0.29.1"]
11
10
  urls = { "repository" = "https://github.com/yamoling/multi-agent-rlenv" }
12
11
  classifiers = [
13
12
  "Programming Language :: Python :: 3",
14
13
  "Operating System :: OS Independent",
15
14
  ]
16
15
 
16
+ dependencies = ["numpy>=2.0.0", "opencv-python>=4.0", "typing_extensions>=4.0"]
17
+
18
+ [project.optional-dependencies]
19
+ gym = ["gymnasium>=0.29.1"]
20
+ smac = ["smac", "pysc2"]
21
+ pettingzoo = ["pettingzoo>=1.20", "pymunk>=6.0", "scipy>=1.10"]
22
+ overcooked = ["overcooked-ai>=1.1.0", "scipy>=1.10"]
23
+ all = [
24
+ "gymnasium>0.29.1",
25
+ "pettingzoo>=1.20",
26
+ "overcooked-ai",
27
+ "smac",
28
+ "pysc2",
29
+ "pymunk>=6.0",
30
+ "scipy>=1.10",
31
+ ]
17
32
 
18
33
  [build-system]
19
34
  requires = ["hatchling"]
@@ -35,5 +50,10 @@ pythonpath = "src"
35
50
  # Ignore deprecation warnings caused by SMAC
36
51
  filterwarnings = "ignore::DeprecationWarning"
37
52
 
53
+ [tool.uv.sources]
54
+ smac = { git = "https://github.com/oxwhirl/smac.git" }
55
+ pysc2 = { git = "https://github.com/google-deepmind/pysc2.git" }
56
+
57
+
38
58
  [dependency-groups]
39
59
  dev = ["orjson>=3.10.12", "pdoc>=15.0.1", "pytest>=8.3.2"]
@@ -62,7 +62,7 @@ print(env.extras_shape) # (1, )
62
62
  If you want to create a new environment, you can simply create a class that inherits from `MARLEnv`. If you want to create a wrapper around an existing `MARLEnv`, you probably want to subclass `RLEnvWrapper` which implements a default behaviour for every method.
63
63
  """
64
64
 
65
- __version__ = "3.3.0"
65
+ __version__ = "3.3.1"
66
66
 
67
67
  from . import models
68
68
  from . import wrappers
@@ -0,0 +1,42 @@
1
+ from importlib.util import find_spec
2
+ from .pymarl_adapter import PymarlAdapter
3
+
4
+ HAS_GYM = False
5
+ if find_spec("gymnasium") is not None:
6
+ from .gym_adapter import Gym
7
+
8
+ HAS_GYM = True
9
+
10
+ HAS_PETTINGZOO = False
11
+ if find_spec("pettingzoo") is not None:
12
+ from .pettingzoo_adapter import PettingZoo
13
+
14
+ HAS_PETTINGZOO = True
15
+
16
+ HAS_SMAC = False
17
+ if find_spec("smac") is not None:
18
+ from .smac_adapter import SMAC
19
+
20
+ HAS_SMAC = True
21
+
22
+ HAS_OVERCOOKED = False
23
+ if find_spec("overcooked_ai_py.mdp") is not None:
24
+ import numpy
25
+
26
+ # Overcooked assumes a version of numpy <2.0 where np.Inf is available.
27
+ setattr(numpy, "Inf", numpy.inf)
28
+ from .overcooked_adapter import Overcooked
29
+
30
+ HAS_OVERCOOKED = True
31
+
32
+ __all__ = [
33
+ "PymarlAdapter",
34
+ "Gym",
35
+ "PettingZoo",
36
+ "SMAC",
37
+ "Overcooked",
38
+ "HAS_GYM",
39
+ "HAS_PETTINGZOO",
40
+ "HAS_SMAC",
41
+ "HAS_OVERCOOKED",
42
+ ]
@@ -1,41 +1,71 @@
1
- import numpy as np
2
- import pygame
3
- import cv2
4
1
  import sys
5
- from marlenv.models import MARLEnv, State, Observation, Step, DiscreteActionSpace
2
+ from dataclasses import dataclass
6
3
  from typing import Literal, Sequence
4
+
5
+ import cv2
6
+ import numpy as np
7
7
  import numpy.typing as npt
8
- from overcooked_ai_py.mdp.overcooked_mdp import OvercookedGridworld, Action
8
+ import pygame
9
+ from marlenv.models import ContinuousSpace, DiscreteActionSpace, MARLEnv, Observation, State, Step
10
+
9
11
  from overcooked_ai_py.mdp.overcooked_env import OvercookedEnv
12
+ from overcooked_ai_py.mdp.overcooked_mdp import Action, OvercookedGridworld, OvercookedState
10
13
  from overcooked_ai_py.visualization.state_visualizer import StateVisualizer
11
- from dataclasses import dataclass
12
14
 
13
15
 
14
16
  @dataclass
15
17
  class Overcooked(MARLEnv[Sequence[int] | npt.NDArray, DiscreteActionSpace]):
18
+ horizon: int
19
+
16
20
  def __init__(self, oenv: OvercookedEnv):
17
21
  self._oenv = oenv
18
22
  assert isinstance(oenv.mdp, OvercookedGridworld)
19
23
  self._mdp = oenv.mdp
20
24
  self.visualizer = StateVisualizer()
25
+ shape = tuple(int(s) for s in self._mdp.get_lossless_state_encoding_shape())
26
+ shape = (shape[2], shape[0], shape[1])
21
27
  super().__init__(
22
- action_space=DiscreteActionSpace(n_agents=self._mdp.num_players, n_actions=Action.NUM_ACTIONS),
23
- observation_shape=(1,),
24
- state_shape=(1,),
28
+ action_space=DiscreteActionSpace(
29
+ n_agents=self._mdp.num_players,
30
+ n_actions=Action.NUM_ACTIONS,
31
+ action_names=[Action.ACTION_TO_CHAR[a] for a in Action.ALL_ACTIONS],
32
+ ),
33
+ observation_shape=shape,
34
+ extras_shape=(1,),
35
+ extras_meanings=["timestep"],
36
+ state_shape=shape,
37
+ state_extra_shape=(1,),
38
+ reward_space=ContinuousSpace.from_shape(1),
25
39
  )
40
+ self.horizon = int(self._oenv.horizon)
41
+
42
+ @property
43
+ def state(self) -> OvercookedState:
44
+ """Current state of the environment"""
45
+ return self._oenv.state
46
+
47
+ def set_state(self, state: State):
48
+ raise NotImplementedError("Not yet implemented")
49
+
50
+ @property
51
+ def time_step(self):
52
+ return self.state.timestep
26
53
 
27
54
  def _state_data(self):
28
- state = self._oenv.state
29
- state = np.array(self._mdp.lossless_state_encoding(state))
55
+ state = np.array(self._mdp.lossless_state_encoding(self.state))
30
56
  # Use axes (agents, channels, height, width) instead of (agents, height, width, channels)
31
57
  state = np.transpose(state, (0, 3, 1, 2))
32
58
  return state
33
59
 
34
60
  def get_state(self):
35
- return State(self._state_data())
61
+ return State(self._state_data()[0], np.array([self.time_step / self.horizon]))
36
62
 
37
63
  def get_observation(self) -> Observation:
38
- return Observation(self._state_data(), self.available_actions())
64
+ return Observation(
65
+ data=self._state_data(),
66
+ available_actions=self.available_actions(),
67
+ extras=np.array([[self.time_step / self.horizon]] * self.n_agents),
68
+ )
39
69
 
40
70
  def available_actions(self):
41
71
  available_actions = np.full((self.n_agents, self.n_actions), False)
@@ -1,32 +1,27 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Generic, Literal, Optional, TypeVar, overload
3
-
4
3
  import numpy as np
5
4
  import numpy.typing as npt
6
5
 
7
6
  from . import wrappers
7
+ from marlenv import adapters
8
8
  from .models import ActionSpace, MARLEnv
9
- from .adapters import PettingZoo
10
9
 
11
10
  A = TypeVar("A")
12
11
  AS = TypeVar("AS", bound=ActionSpace)
13
12
 
14
- try:
13
+ if adapters.HAS_PETTINGZOO:
14
+ from .adapters import PettingZoo
15
15
  from pettingzoo import ParallelEnv
16
16
 
17
17
  @overload
18
- def make(
19
- env: ParallelEnv,
20
- ) -> PettingZoo: ...
21
-
22
- HAS_PETTINGZOO = True
23
- except ImportError:
24
- HAS_PETTINGZOO = False
18
+ def make(env: ParallelEnv) -> PettingZoo: ...
25
19
 
26
20
 
27
- try:
28
- from gymnasium import Env
21
+ if adapters.HAS_GYM:
29
22
  from .adapters import Gym
23
+ from gymnasium import Env
24
+ import gymnasium
30
25
 
31
26
  @overload
32
27
  def make(env: Env) -> Gym: ...
@@ -37,25 +32,21 @@ try:
37
32
  Make an RLEnv from the `gymnasium` registry (e.g: "CartPole-v1").
38
33
  """
39
34
 
40
- HAS_GYM = True
41
- except ImportError:
42
- HAS_GYM = False
43
35
 
44
- try:
45
- from smac.env import StarCraft2Env
36
+ if adapters.HAS_SMAC:
46
37
  from .adapters import SMAC
38
+ from smac.env import StarCraft2Env
47
39
 
48
40
  @overload
49
41
  def make(env: StarCraft2Env) -> SMAC: ...
50
42
 
51
- HAS_SMAC = True
52
- except ImportError:
53
- HAS_SMAC = False
54
43
 
44
+ if adapters.HAS_OVERCOOKED:
45
+ from .adapters import Overcooked
46
+ from overcooked_ai_py.mdp.overcooked_env import OvercookedEnv
55
47
 
56
- @overload
57
- def make(env: MARLEnv[A, AS]) -> MARLEnv[A, AS]:
58
- """Why would you do this ?"""
48
+ @overload
49
+ def make(env: OvercookedEnv) -> Overcooked: ...
59
50
 
60
51
 
61
52
  def make(env, **kwargs):
@@ -64,32 +55,18 @@ def make(env, **kwargs):
64
55
  case MARLEnv():
65
56
  return env
66
57
  case str(env_id):
67
- try:
68
- import gymnasium
69
- except ImportError:
70
- raise ImportError("Gymnasium is not installed !")
71
- from marlenv.adapters import Gym
72
-
73
- gym_env = gymnasium.make(env_id, render_mode="rgb_array", **kwargs)
74
- return Gym(gym_env)
75
-
76
- try:
77
- from marlenv.adapters import PettingZoo
78
-
79
- if isinstance(env, ParallelEnv):
80
- return PettingZoo(env)
81
- except ImportError:
82
- pass
83
- try:
84
- from smac.env import StarCraft2Env
85
-
86
- from marlenv.adapters import SMAC
87
-
88
- if isinstance(env, StarCraft2Env):
89
- return SMAC(env)
90
- except ImportError:
91
- pass
92
-
58
+ if adapters.HAS_GYM:
59
+ gym_env = gymnasium.make(env_id, render_mode="rgb_array", **kwargs)
60
+ return Gym(gym_env)
61
+
62
+ if adapters.HAS_PETTINGZOO and isinstance(env, ParallelEnv):
63
+ return PettingZoo(env) # type: ignore
64
+ if adapters.HAS_SMAC and isinstance(env, StarCraft2Env):
65
+ return SMAC(env)
66
+ if adapters.HAS_OVERCOOKED and isinstance(env, OvercookedEnv):
67
+ return Overcooked(env) # type: ignore
68
+ if adapters.HAS_GYM and isinstance(env, Env):
69
+ return Gym(env)
93
70
  raise ValueError(f"Unknown environment type: {type(env)}")
94
71
 
95
72
 
@@ -1,6 +1,5 @@
1
1
  from typing import Sequence
2
2
  from dataclasses import dataclass
3
- import numpy as np
4
3
  import numpy.typing as npt
5
4
  from typing_extensions import TypeVar
6
5
  import random
@@ -58,9 +58,14 @@ class Observation:
58
58
  available_actions=self.available_actions[agent_id],
59
59
  )
60
60
 
61
+ @property
62
+ def shape(self) -> tuple[int, ...]:
63
+ """The individual shape of the observation data"""
64
+ return self.data[0].shape
65
+
61
66
  @property
62
67
  def extras_shape(self) -> tuple[int, ...]:
63
- """The shape of the observation extras"""
68
+ """The individual shape of the observation extras"""
64
69
  return self.extras[0].shape
65
70
 
66
71
  def __hash__(self):
@@ -1,31 +1,18 @@
1
- try:
2
- import gymnasium
3
-
4
- skip_gym = False
5
- except ImportError:
6
- skip_gym = True
7
-
8
- try:
9
- import pettingzoo
10
-
11
- skip_pettingzoo = False
12
- except ImportError:
13
- skip_pettingzoo = True
14
-
15
- try:
16
- import smac
17
-
18
- skip_smac = False
19
- except ImportError:
20
- skip_smac = True
21
-
1
+ from importlib.util import find_spec
22
2
 
23
3
  import numpy as np
24
4
  import pytest
25
5
 
26
6
  import marlenv
27
- from marlenv import DiscreteActionSpace, DiscreteMockEnv, MARLEnv, Observation, State, ContinuousActionSpace
28
- from marlenv.adapters import SMAC, PymarlAdapter
7
+ from marlenv import ContinuousActionSpace, DiscreteActionSpace, DiscreteMockEnv, MARLEnv, Observation, State
8
+ from marlenv.adapters import PymarlAdapter
9
+
10
+ skip_gym = find_spec("gymnasium") is None
11
+ skip_pettingzoo = find_spec("pettingzoo") is None
12
+ skip_smac = find_spec("smac") is None
13
+ # Check for "overcooked_ai_py.mdp" specifically because after uninstalling, the package
14
+ # can still be found because of some remaining .pkl file.
15
+ skip_overcooked = find_spec("overcooked_ai_py.mdp") is None
29
16
 
30
17
 
31
18
  @pytest.mark.skipif(skip_gym, reason="Gymnasium is not installed")
@@ -113,7 +100,10 @@ def test_pettingzoo_adapter_continuous_action():
113
100
  assert isinstance(env.action_space, marlenv.ContinuousActionSpace)
114
101
 
115
102
 
116
- def _check_env_3m(env: SMAC):
103
+ def _check_env_3m(env):
104
+ from marlenv.adapters import SMAC
105
+
106
+ assert isinstance(env, SMAC)
117
107
  obs = env.reset()
118
108
  assert isinstance(obs, Observation)
119
109
  assert env.n_agents == 3
@@ -147,6 +137,48 @@ def test_smac_render():
147
137
  env.render()
148
138
 
149
139
 
140
+ @pytest.mark.skipif(skip_overcooked, reason="Overcooked is not installed")
141
+ def test_overcooked_attributes():
142
+ from overcooked_ai_py.mdp.overcooked_mdp import Action
143
+
144
+ from marlenv.adapters import Overcooked
145
+
146
+ env = Overcooked.from_layout("simple_o")
147
+ height, width = env._mdp.shape
148
+ assert env.n_agents == 2
149
+ assert env.n_actions == Action.NUM_ACTIONS
150
+ assert env.observation_shape == (26, height, width)
151
+ assert env.reward_space.shape == (1,)
152
+ assert env.extras_shape == (1,)
153
+ assert not env.is_multi_objective
154
+
155
+
156
+ @pytest.mark.skipif(skip_overcooked, reason="Overcooked is not installed")
157
+ def test_overcooked_obs_state():
158
+ from marlenv.adapters import Overcooked
159
+
160
+ HORIZON = 100
161
+ env = Overcooked.from_layout("coordination_ring", horizon=HORIZON)
162
+ height, width = env._mdp.shape
163
+ obs, state = env.reset()
164
+ for i in range(HORIZON):
165
+ assert obs.shape == (26, height, width)
166
+ assert obs.extras_shape == (1,)
167
+ assert state.shape == (26, height, width)
168
+ assert state.extras_shape == (1,)
169
+
170
+ assert np.all(obs.extras == i / HORIZON)
171
+ assert np.all(state.extras == i / HORIZON)
172
+
173
+ step = env.random_step()
174
+ obs = step.obs
175
+ state = step.state
176
+ if i < HORIZON - 1:
177
+ assert not step.done
178
+ else:
179
+ assert step.done
180
+
181
+
150
182
  def test_pymarl():
151
183
  LIMIT = 20
152
184
  N_AGENTS = 2
@@ -1,10 +1,15 @@
1
1
  import pickle
2
- import marlenv
2
+ from importlib.util import find_spec
3
+
3
4
  import numpy as np
4
5
  import orjson
6
+ import pytest
5
7
 
8
+ import marlenv
6
9
  from marlenv import DiscreteMockEnv
7
10
 
11
+ skip_gym = find_spec("gymnasium") is None
12
+
8
13
 
9
14
  def test_registry():
10
15
  env = DiscreteMockEnv(4)
@@ -17,6 +22,7 @@ def test_registry():
17
22
  assert restored_env.n_actions == env.n_actions
18
23
 
19
24
 
25
+ @pytest.mark.skipif(skip_gym, reason="Gymnasium is not installed")
20
26
  def test_registry_gym():
21
27
  env = marlenv.make("CartPole-v1")
22
28
  restored_env = pickle.loads(pickle.dumps(env))
@@ -1,32 +0,0 @@
1
- from .pymarl_adapter import PymarlAdapter
2
- from typing import Any
3
-
4
- __all__ = ["PymarlAdapter"]
5
- try:
6
- from .gym_adapter import Gym
7
-
8
- __all__.append("Gym")
9
- except ImportError:
10
- Gym = Any
11
-
12
- try:
13
- from .pettingzoo_adapter import PettingZoo
14
-
15
- __all__.append("PettingZoo")
16
- except ImportError:
17
- PettingZoo = Any
18
-
19
- try:
20
- from .smac_adapter import SMAC
21
-
22
- __all__.append("SMAC")
23
- except ImportError:
24
- SMAC = Any
25
-
26
-
27
- try:
28
- from .overcooked_adapter import Overcooked
29
-
30
- __all__.append("Overcooked")
31
- except ImportError:
32
- Overcooked = Any