# gym-super-mario-bros: Gymnasium environments for the NES Mario games

> gym-super-mario-bros is a Gymnasium environment for Super Mario Bros., Super Mario Bros. 2 including the Lost Levels and the USA release, and Super Mario Bros. 3, running on the nes-py emulator with task metadata, action lists and a command line player. It targets researchers who need reproducible Mario evaluation, and it requires Python 3.13 or newer.

**Kautenja/gym-super-mario-bros** — Gymnasium environments for NES Mario games, with task metadata, reward instrumentation, and reproducible evaluation interfaces.

- Repository: https://github.com/Kautenja/gym-super-mario-bros
- Stars: 903 · Forks: 173
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/kautenja-gym-super-mario-bros

## Four NES Mario games behind one Gymnasium registration

gym-super-mario-bros is a Gymnasium environment for Super Mario Bros., Super Mario Bros. 2 covering both the Lost Levels and the USA release, and Super Mario Bros. 3 on the Nintendo Entertainment System, using the nes-py emulator as the underlying system.

The library's stated alignment is with Gymnasium's modern semantics: reset, step, render-mode and truncation. That positioning is the whole point of the package, because the older Mario environments in the reinforcement learning ecosystem predate the current API and return a different tuple from step. This one returns the five element result, state, reward, terminated, truncated and info, and it separates termination from truncation, which is the distinction that matters when a time limit ends an episode rather than the game doing so.

The environment identifiers are simple. There are four whole game environments, SuperMarioBros-v0 for the first game, SuperMarioBros2-v0, SuperMarioBros2USA-v0 and SuperMarioBros3-v0, all using the standard ROM. On top of that there is a per-stage template, SuperMarioBros-<world>-<stage>-v<version>, where the world is a number from 1 to 8, the stage a number from 1 to 4, and the version is 0 for the standard ROM. A single stage such as 4-2 becomes SuperMarioBros-4-2-v0.

The difference between the two families is the number of attempts. The whole game environments allow three attempts, the lives, to get through the 32 stages. The individual stage environments allow a single attempt at a single stage, which is the variant most people use for curriculum learning where the agent should be able to retry a level rather than lose a life.

Who this is for is researchers and developers doing reinforcement learning on side-scrolling platformers. The package classifiers list the intended audience as developers and science or research, and the topic classifiers cover games and entertainment for side-scrolling arcade games and scientific and engineering artificial intelligence.

## 256 actions by default, and three lists that cut it down

The action space is where a Mario environment either helps you or makes you write the same code twice.

By default, gym_super_mario_bros environments use the full NES action space of 256 discrete actions. That is the raw button combination space, and it is a large space to explore and an awkward one to reason about, because most of the combinations are equivalent or useless.

The library therefore ships a module, gym_super_mario_bros.actions, with three action lists named RIGHT_ONLY, SIMPLE_MOVEMENT and COMPLEX_MOVEMENT. They are meant to be used with the nes_py.wrappers.JoypadSpace wrapper, and the README points at gym_super_mario_bros/actions.py for a breakdown of which actions are legal in each list.

That design has a subtlety worth knowing. The action list is not passed to gym.make as a parameter. Instead, the environment is created and then wrapped, so the environment is built first and the JoypadSpace wrapper constrains the action space afterwards:

```python
import gymnasium as gym
from nes_py.wrappers import JoypadSpace
import gym_super_mario_bros
from gym_super_mario_bros.actions import SIMPLE_MOVEMENT

env = gym.make('SuperMarioBros-v0', render_mode='human')
env = JoypadSpace(env, SIMPLE_MOVEMENT)
```

Because the wrapper sits outside the environment, an agent sees only the reduced action set while the emulator still receives whatever the wrapper maps it to. That is the right separation, and it also means the same wrapper technique works for other NES environments from the same author.

The README also flags a detail that catches people out: you must import gym_super_mario_bros before trying to make an environment, because Gymnasium environments are registered at runtime. There is no entry in an entry point registry that gets discovered automatically, so an import that is missing produces an environment id that does not exist rather than a clear error.

For convenience there is also a make function in the package, and the README says it is just an alias to gymnasium.make for convenience after the package is imported.

## Task metadata you can query without booting a ROM

The feature that most clearly separates this package from a bare environment wrapper is the task metadata API.

gym_super_mario_bros exposes lightweight task metadata for curriculum, conditioning and evaluation-matrix code without constructing ROM-backed environments. The functions are:

```python
import gym_super_mario_bros

tasks = gym_super_mario_bros.all_tasks(single_stage=True)
env_ids = gym_super_mario_bros.task_ids(game_family='smb1')
task = gym_super_mario_bros.task_for_env_id('SuperMarioBros-4-2-v0')
smb3_stages = gym_super_mario_bros.smb3_stage_matrix()
```

The point of that design is that enumerating the environment space does not require an emulator, a ROM or a reset. all_tasks with single_stage set returns the per-stage task list, task_ids filters by game family, task_for_env_id resolves one identifier to its task object, and smb3_stage_matrix returns the SMB3 stage catalog.

What a MarioTask carries is specified in the README: the environment ID, the canonical task ID, the game family, the ROM mode, the world and stage, a public world label, split flags, and alias metadata for separator-free SMB1 identifiers such as SuperMarioBros1-1-v0. Split flags matter for anyone building train and test partitions from the same task list, since they let you split on the canonical identity rather than on the string form of the environment id.

The SMB3 stage matrix is a special case worth understanding. The README says it catalogs the numbered vanilla courses separately from the smaller set of validated single-stage reset entry points. In other words, the number of stages in the game is not the number of environments that can actually be reset into, and the matrix records both sets so that code enumerating tasks does not assume a one to one mapping.

The practical value shows up in evaluation code. If you want a table of results per world and stage, or a per-family aggregate, you need the task structure without paying the cost of constructing environments, and that is what these helpers give you.

## Termination, truncation and a very large TimeLimit

The episode length handling in this package is unusual, and it is documented in detail because it changes how you should write your training loop.

Registered environments use Gymnasium's TimeLimit wrapper with max_episode_steps set to 9999999. The README explains why: that preserves the historical cap while allowing the game logic to end normal episodes with terminated=True. In other words, the wrapper is effectively out of the way, and the episode ends when the game ends, which is a termination rather than a truncation.

If you want a time limit, the supported way is to pass a shorter max_episode_steps to gymnasium.make yourself, and that path returns truncated=True. The distinction is the one that matters for bootstrapping: a terminated episode has genuinely ended, while a truncated one is cut off and its value estimate should be treated differently. With the default of nearly ten million steps, a Mario run reaches its own conclusion, and any truncation you see is one you asked for.

A second note in the README is about performance rather than semantics: remove calls to render in training code for a nontrivial speedup. Rendering a NES frame in a window for every step of a training run dominates the step time, and the CLI exposes the same idea with a no-render flag.

Together these two behaviours make the environment usable for both purposes. For evaluation and reinforcement learning, the game ends the episode and the reward signal is the game's own. For playing and for quick tests, you impose your own limit through the make call, and the environment reports that honestly through truncated.

One more detail in the same area: the environments only send reward-able game-play frames to the agent, and cut-scenes, loading screens and the like are not sent. The README also warns that if a cut-scene cannot be skipped by hacking the NES's RAM, the environment will lock the Python process until the emulator is ready for the next action. That is a real behaviour to plan for when running a long random policy.

## A command line player with keyboard and random modes

The package ships a command line interface for playing environments, using either the keyboard or uniform random movement.

Both invocation styles work:

```shell
python -m gym_super_mario_bros --env <environment ID> --mode <human|random>
gym_super_mario_bros --env <environment ID> --mode <human|random>
```

The second form comes from the console script entry point declared in pyproject.toml, which maps gym_super_mario_bros to gym_super_mario_bros._app.cli:main, so installing the package puts the command on your path.

The defaults are documented in the README: by default -e is set to SuperMarioBros-v0 and -m is set to human, the --actionspace or -a flag is set to nes, and rendering is enabled. The action space selector accepts nes, right, simple or complex, which correspond to the raw 256 button combinations and the three reduced lists in the actions module.

Human keyboard play opens a graphical window, and the README gives the invocation:

```shell
gym_super_mario_bros --env SuperMarioBros-v0 --mode human --actionspace simple
```

Random play can be rendered or run headless, and the seed flag seeds the first environment reset:

```shell
gym_super_mario_bros --mode random --steps 1000 --no-render --seed 123
```

There is one validation rule worth knowing, because it is the kind of thing that produces a confusing error. Human mode requires rendering, so a combination of --mode human with --no-render is rejected. The help text is available through the same command:

```shell
python -m gym_super_mario_bros --help
```

For anyone writing evaluation code, the random mode with a fixed seed is the useful one, because it gives you a repeatable baseline to compare an agent against without writing a policy first.

## Python 3.13, a native emulator dependency and the ROM question

Installation is a single command, but the requirements are strict:

```shell
pip install gym-super-mario-bros
```

Python 3.13 or newer is required, and the supported CI targets are CPython 3.13 and 3.14. The pyproject.toml confirms this with requires-python set to >=3.13, and the classifiers list Python 3.13 and 3.14 only. That is a real constraint for a research group whose analysis environment is pinned to an older interpreter, and it applies to the whole dependency chain rather than just this package.

The second dependency is the harder one in practice. The package depends on the native nes-py emulator, and the README warns that Linux GLIBCXX_* loader errors and Windows compiler toolchain failures are usually nes-py installation and runtime issues rather than wrapper bugs, pointing at the nes-py installation notes for current compiler and runtime expectations. In other words, if your install fails with a loader error, the thing to read is the emulator's documentation, not this package's.

The declared dependencies are gymnasium>=1.0.0 and nes-py>=9.0.1, and the requirements.txt file is marked as a developer and CI bootstrap only, with the canonical runtime metadata in pyproject.toml. That file also lists nes-py, build and PyYAML.

The ROM question is the third thing to understand before you rely on this. The project metadata declares the licence as MIT AND LicenseRef-Nintendo-ROMs, and lists LICENSE, LICENSING.md and docs/licenses/Nintendo-ROMs.txt as the licence files. The package data section also includes the _roms/*.nes files in the distribution. The README's badge for the code licence points at a LICENSING.md file, so the split between the MIT code and the separately handled game images is documented, and the point of the split is that the code and the Nintendo game images are not on the same footing.

This is the part of the project where the reader has to make their own decision rather than follow documentation. The rest of the package is an ordinary Gymnasium integration, but the images of the games are not the author's to license freely, and the way you obtain them has to be lawful where you are. Read LICENSING.md and the ROM licence file before you install anything.

## Conclusion

gym-super-mario-bros fits a reinforcement learning researcher who needs a Mario environment with a modern Gymnasium API, a documented action space and machine readable task metadata for evaluation matrices, since the metadata helpers work without constructing a ROM backed environment. It is the wrong choice for a Python 3.12 or older project, since the package requires Python 3.13 or newer, and it is the wrong choice for anyone who cannot accept the ROM situation, because the licence is MIT AND LicenseRef-Nintendo-ROMs and the project distributes its own _roms files. Before you adopt it, read LICENSING.md and docs/licenses/Nintendo-ROMs.txt, decide how you will obtain the game images legally, and check the nes-py installation notes for the compiler and runtime expectations on your platform.

## FAQ

### How do I install gym-super-mario-bros?

The preferred installation is `pip install gym-super-mario-bros`. Python 3.13 or newer is required, and the supported CI targets are CPython 3.13 and 3.14.

### Why does gym.make fail with an unknown environment id?

Gymnasium environments are registered at runtime, so you must import gym_super_mario_bros before trying to make an environment. The README also notes that gym_super_mario_bros.make is an alias to gymnasium.make for convenience after that import.

### How do I limit episode length in gym-super-mario-bros?

Registered environments use the TimeLimit wrapper with max_episode_steps set to 9999999, so the game logic ends episodes with terminated=True. To impose your own limit, pass a shorter max_episode_steps to gymnasium.make, which returns truncated=True.

### What is the default action space in gym-super-mario-bros?

By default the environments use the full NES action space of 256 discrete actions. The module gym_super_mario_bros.actions provides three reduced lists, RIGHT_ONLY, SIMPLE_MOVEMENT and COMPLEX_MOVEMENT, for use with the nes_py.wrappers.JoypadSpace wrapper.

### Can I list the available tasks without creating an environment?

Yes. The README says the package exposes lightweight task metadata for curriculum, conditioning and evaluation-matrix code without constructing ROM-backed environments, through functions such as all_tasks, task_ids, task_for_env_id and smb3_stage_matrix.

## Sources

- [Issues](https://github.com/Kautenja/gym-super-mario-bros/issues)
- [Kautenja/gym-super-mario-bros on GitHub](https://github.com/Kautenja/gym-super-mario-bros)
- [License: MIT](https://github.com/Kautenja/gym-super-mario-bros/blob/master/LICENSE)
- [README](https://github.com/Kautenja/gym-super-mario-bros/blob/master/README.md)
- [Releases](https://github.com/Kautenja/gym-super-mario-bros/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kautenja-gym-super-mario-bros
