Skip to content

Commit 59856c1

Browse files
committed
Modernize MAgent2 for Python 3.10-3.12 (v0.4.0)
- Fix c_lib.py: replace hardcoded venv path with automatic library discovery - Fix CMakeLists.txt: separate MSVC/GCC flags, use CMAKE_CXX_STANDARD - Fix setup.py: remove deprecated self.debug, unify cross-platform cmake build - Fix Range.h: sizeof(bool) -> sizeof(int) in copy constructor (data corruption bug) - Fix C++ headers: tgmath.h -> cmath, stdlib.h -> cstdlib, assert.h -> cassert - Fix magent_env.py: update deprecated seeding.np_random() API - Update pyproject.toml: Python >=3.10, remove numpy <2.0 upper bound - Update CI: Python 3.10-3.12 matrix, upgrade actions to v4/v5 - Update pre-commit: pyupgrade --py310-plus - Update README and docs with modern usage examples - Add battle_demo.py and battle_video.py examples - Bump version to 0.4.0 Tested with Python 3.11, numpy 2.3, gymnasium 1.2.3, pettingzoo 1.25, pygame 2.6.1 Fixes Farama-Foundation#63, Farama-Foundation#59, Farama-Foundation#57, Farama-Foundation#54, Farama-Foundation#53
1 parent 7bd5fc5 commit 59856c1

16 files changed

Lines changed: 454 additions & 121 deletions

File tree

.github/workflows/build-publish.yml

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,60 +20,62 @@ jobs:
2020
strategy:
2121
matrix:
2222
include:
23-
- os: ubuntu-22.04
24-
python: 38
25-
platform: manylinux_x86_64
26-
- os: ubuntu-22.04
27-
python: 39
28-
platform: manylinux_x86_64
2923
- os: ubuntu-22.04
3024
python: 310
3125
platform: manylinux_x86_64
3226
- os: ubuntu-22.04
3327
python: 311
3428
platform: manylinux_x86_64
29+
- os: ubuntu-22.04
30+
python: 312
31+
platform: manylinux_x86_64
3532

3633
steps:
3734
- uses: actions/checkout@v4
3835
- name: Build wheels
39-
uses: pypa/cibuildwheel@v2.19.2
36+
uses: pypa/cibuildwheel@v2.22.0
4037
env:
4138
CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform }}
4239
CIBW_ARCHS: auto
4340
CIBW_BUILD_VERBOSITY: 1
4441

4542
- name: Store wheels
46-
uses: actions/upload-artifact@v3
43+
uses: actions/upload-artifact@v4
4744
with:
45+
name: wheels-linux-${{ matrix.python }}
4846
path: ./wheelhouse/*.whl
4947

5048
build-src:
5149
runs-on: ubuntu-latest
5250
steps:
5351
- uses: actions/checkout@v4
52+
- name: Set up Python
53+
uses: actions/setup-python@v5
54+
with:
55+
python-version: "3.12"
5456
- name: Build src
5557
run: |
56-
python -m pip install --upgrade pip
57-
pip install wheel
58-
python setup.py sdist
58+
python -m pip install --upgrade pip build
59+
python -m build --sdist
5960
- name: Store src
60-
uses: actions/upload-artifact@v3
61+
uses: actions/upload-artifact@v4
6162
with:
63+
name: sdist
6264
path: dist/*.tar.gz
6365

6466
build-wheels-ms:
6567
runs-on: ${{ matrix.os }}
6668
strategy:
6769
matrix:
68-
python-version: [ '3.8', '3.9', '3.10', '3.11' ]
70+
python-version: [ '3.10', '3.11', '3.12' ]
6971
include:
7072
- os: windows-latest
7173

7274
steps:
7375
- uses: actions/checkout@v4
7476

7577
- name: Set up Python
76-
uses: actions/setup-python@v3
78+
uses: actions/setup-python@v5
7779
with:
7880
python-version: ${{ matrix.python-version }}
7981
architecture: x64
@@ -87,8 +89,9 @@ jobs:
8789
pip install wheel cmake-build-extension
8890
pip wheel . --no-deps -w wheelhouse/
8991
90-
- uses: actions/upload-artifact@v3
92+
- uses: actions/upload-artifact@v4
9193
with:
94+
name: wheels-windows-${{ matrix.python-version }}
9295
path: ./wheelhouse/*.whl
9396

9497
publish:
@@ -100,10 +103,10 @@ jobs:
100103
if: github.event_name == 'release' && github.event.action == 'published'
101104
steps:
102105
- name: Download dists
103-
uses: actions/download-artifact@v3
106+
uses: actions/download-artifact@v4
104107
with:
105-
name: artifact
106108
path: dist
109+
merge-multiple: true
107110
- name: Publish
108111
uses: pypa/gh-action-pypi-publish@release/v1
109112
with:

.gitignore

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,13 @@ docs/environments/*.md
2222
Parallel_Demo.py
2323

2424
# Environments
25-
.venv
25+
.venv
26+
27+
# Build artifacts
28+
*.pyd
29+
*.egg-info/
30+
_frames/
31+
.pytest_cache/
32+
33+
# Demo outputs
34+
battle_video.mp4

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ repos:
3838
rev: v3.3.2
3939
hooks:
4040
- id: pyupgrade
41-
args: ["--py37-plus"]
41+
args: ["--py310-plus"]
4242
- repo: https://github.com/PyCQA/isort
4343
rev: 5.12.0
4444
hooks:

CMakeLists.txt

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@ project(magent)
44

55
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
66

7+
# Use CMake's standard mechanism instead of -std=c++11
8+
set(CMAKE_CXX_STANDARD 11)
9+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
10+
711
file(GLOB autopilot_sources src/*.cc src/gridworld/*.cc src/utility/*.cc)
812
set(LIB_SRC_FILES ${autopilot_sources})
913

10-
11-
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -std=c++11 -O3")
12-
IF (WIN32)
13-
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0 /wd4711 /wd4710")
14-
ELSE()
15-
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable -Wno-reorder -Wno-sign-compare -Wno-missing-braces")
16-
ENDIF()
17-
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG")
14+
if(MSVC)
15+
add_compile_options(/W0 /wd4711 /wd4710)
16+
add_compile_definitions(DEBUG)
17+
else()
18+
add_compile_options(-g -Wall -O3
19+
-Wno-unused-variable -Wno-reorder -Wno-sign-compare -Wno-missing-braces)
20+
add_compile_definitions(DEBUG)
21+
endif()
1822

1923
# runtime library
2024
add_library(magent SHARED ${LIB_SRC_FILES})

README.md

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,98 @@ MAgent2 is a library for the creation of environments where large numbers of pix
1111
MAgent2 is a maintained fork of the original [MAgent](https://github.com/geek-ai/MAgent) codebase. It contains some [reference environments](https://github.com/Farama-Foundation/MAgent2/tree/main/magent2/environments) implemented using the [PettingZoo](https://github.com/Farama-Foundation/PettingZoo) API. These environments used to be included in PettingZoo itself, but have been moved here to exist independently. They are being regularly maintained and will receive bug fixes, support new versions of Python, etc. Development used to take place at [github.com/Farama-Foundation/MAgent](https://github.com/Farama-Foundation/MAgent) but was moved to [github.com/Farama-Foundation/MAgent2](https://github.com/Farama-Foundation/MAgent2) so that the distinction from the original MAgent library is clear to users.
1212

1313
## Installation
14-
Install using pip: `pip install magent2`. See [docs](https://magent2.farama.org/) for usage information.
1514

15+
Install using pip:
16+
```bash
17+
pip install magent2
18+
```
19+
20+
See [docs](https://magent2.farama.org/) for usage information.
21+
22+
### Build from source
23+
24+
MAgent2 includes a C++ native extension that requires CMake to build:
25+
26+
```bash
27+
# Prerequisites: Python 3.10+, CMake, C++ compiler (GCC/Clang on Linux, MSVC on Windows)
28+
git clone https://github.com/Farama-Foundation/MAgent2.git
29+
cd MAgent2
30+
pip install -e .
31+
```
1632

1733
## Requirements
18-
MAgent2 supports Linux and macOS and Python 3.8+.
34+
35+
- Python 3.10+
36+
- Linux or Windows
37+
- Dependencies (installed automatically):
38+
- `numpy >= 1.21.0`
39+
- `pygame >= 2.1.0`
40+
- `pettingzoo >= 1.23.1` (which pulls in `gymnasium`)
41+
42+
### Tested with
43+
44+
| Package | Version |
45+
|---------|---------|
46+
| Python | 3.11 |
47+
| numpy | 2.3.x |
48+
| gymnasium | 1.2.3 |
49+
| pettingzoo | 1.25.0 |
50+
| pygame | 2.6.1 |
51+
52+
## Quick Start
53+
54+
### AEC API (agents take turns)
55+
56+
```python
57+
from magent2.environments import battle_v4
58+
59+
env = battle_v4.env(map_size=45, max_cycles=300)
60+
env.reset()
61+
62+
for agent in env.agent_iter():
63+
observation, reward, termination, truncation, info = env.last()
64+
if termination or truncation:
65+
action = None
66+
else:
67+
action = env.action_space(agent).sample() # random policy
68+
env.step(action)
69+
70+
env.close()
71+
```
72+
73+
### Parallel API (all agents act simultaneously, faster)
74+
75+
```python
76+
from magent2.environments.battle.battle import parallel_env
77+
78+
env = parallel_env(map_size=45, max_cycles=300)
79+
observations, infos = env.reset()
80+
81+
while env.agents:
82+
actions = {agent: env.action_space(agent).sample() for agent in env.agents}
83+
observations, rewards, terminations, truncations, infos = env.step(actions)
84+
85+
env.close()
86+
```
87+
88+
### Generate a battle video (requires ffmpeg)
89+
90+
```python
91+
python battle_video.py
92+
```
93+
94+
This renders each frame as a grid image (red = red team, blue = blue team) and encodes them into an MP4 video using ffmpeg. See `battle_video.py` for the full implementation.
95+
96+
## Available Environments
97+
98+
| Environment | Agents | Description |
99+
|-------------|--------|-------------|
100+
| `battle_v4` | 162 | Two teams battle on a 45x45 grid |
101+
| `adversarial_pursuit_v4` | 75 | Predators chase prey agents |
102+
| `battlefield_v5` | 162 | Large battlefield with obstacles |
103+
| `combined_arms_v6` | 162 | Two unit types per team (melee + ranged) |
104+
| `gather_v5` | 495 | Agents gather food while avoiding predators |
105+
| `tiger_deer_v4` | 121 | Tigers hunt deer |
19106

20107
## References
21108
```

battle_demo.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""MAgent2 Battle 环境演示脚本
2+
3+
两队智能体(红队 red vs 蓝队 blue)在 45x45 网格上进行团战。
4+
使用随机策略运行,输出每步的存活数量和最终结果。
5+
"""
6+
7+
from magent2.environments import battle_v4
8+
9+
10+
def run_battle_demo():
11+
# 创建环境 (AEC 模式,即逐个智能体轮流行动)
12+
env = battle_v4.env(map_size=45, max_cycles=300)
13+
env.reset()
14+
15+
red_alive = 0
16+
blue_alive = 0
17+
step_count = 0
18+
19+
# 统计初始数量
20+
for agent in env.agents:
21+
if agent.startswith("red"):
22+
red_alive += 1
23+
else:
24+
blue_alive += 1
25+
print(f"=== MAgent2 Battle Demo ===")
26+
print(f"Initial: red={red_alive}, blue={blue_alive}, total={red_alive + blue_alive}")
27+
print()
28+
29+
# 主循环:逐个智能体行动
30+
for agent in env.agent_iter():
31+
observation, reward, termination, truncation, info = env.last()
32+
33+
if termination or truncation:
34+
action = None # 已死亡或截断的智能体不行动
35+
else:
36+
# 随机选择一个合法动作
37+
action = env.action_space(agent).sample()
38+
39+
env.step(action)
40+
step_count += 1
41+
42+
# 每 1000 步打印一次状态
43+
if step_count % 1000 == 0:
44+
red = sum(1 for a in env.agents if a.startswith("red"))
45+
blue = sum(1 for a in env.agents if a.startswith("blue"))
46+
print(f" step {step_count:>6d}: red={red:>3d}, blue={blue:>3d}")
47+
48+
# 最终结果
49+
red_final = sum(1 for a in env.agents if a.startswith("red"))
50+
blue_final = sum(1 for a in env.agents if a.startswith("blue"))
51+
print()
52+
print(f"=== Battle Finished ===")
53+
print(f"Total steps: {step_count}")
54+
print(f"Survivors: red={red_final}, blue={blue_final}")
55+
if red_final > blue_final:
56+
print("Result: Red team wins!")
57+
elif blue_final > red_final:
58+
print("Result: Blue team wins!")
59+
else:
60+
print("Result: Draw!")
61+
62+
env.close()
63+
64+
65+
if __name__ == "__main__":
66+
run_battle_demo()

0 commit comments

Comments
 (0)