Ajouter un backend de jeu à fmri-gym

VérifiéSûr

Procédure complète pour intégrer un nouveau moteur de jeu (backend) dans fmri-gym : adaptateur, package gym, config de curriculum, extra pyproject, tests et pièges de rejeu.

Spar Skills Guide Bot
DeveloppementAvancé
02/11/2026
Claude CodeCursorWindsurfCopilotCodex
#fmri#gymnasium#game-engine#adapter#reproducibility

Recommandé pour

Synthèse automatique

Guide la procédure d'ajout d'un nouveau moteur de jeu (backend) ou d'un adaptateur Gymnasium au projet fmri-gym, du code d'adaptation jusqu'aux tests et à la configuration.

Points forts

  • Checklist ordonnée et numérotée couvrant adaptateur, config, extras pyproject, tests et workflows CI
  • Distinction claire entre logique du jeu (env Gym) et adaptateur, avec des modèles de fichiers existants à imiter
  • Met l'accent sur la reproduction (replay) comme critère d'acceptation réel, pas seulement la jouabilité
  • Signale des pièges concrets comme les noms de touches pygame ou l'absence de valeurs par défaut dans les phases de jeu

Limites

  • Très spécifique au dépôt fmri-gym et à ses conventions internes, peu réutilisable ailleurs
  • Le texte fourni s'interrompt en pleine phrase sur la gestion des espaces d'action
  • Suppose une familiarité avec Gymnasium, uv, pytest et les workflows GitHub Actions
Quand l'utiliser

À utiliser lorsqu'on doit ajouter, porter ou connecter un jeu à fmri-gym et qu'on veut suivre toutes les étapes sans en oublier.

Quand l'éviter

Inutile pour un projet qui n'utilise pas fmri-gym ou qui ne cherche pas à intégrer un environnement de jeu.

Analyse de sécurité

Sûr
Score qualité86/100

The skill is pure developer documentation for adding a game-engine backend: it describes file layout, hooks, config conventions and reproducibility pitfalls, and its only commands are local test/lint/play invocations (`pytest`, `uv run`, `ruff`). There is no destructive command, no obfuscated payload, no credential or token handling, and no instruction to disable safety checks. The single supply-chain surface is the pinned git dependency on a maintainer fork, which is disclosed in-line and used for a stated determinism reason rather than concealment.

Points d'attention
  • •Step 4 introduces a pinned third-party dependency installed directly from a GitHub fork (`crafter @ git+https://github.com/chengfanbrain/crafter.git@deterministic`); this is a legitimate determinism fix but pulls code from a non-upstream personal repository, so provenance should be confirmed before wide use.
Comment cette analyse est faite

Exemples

Add a new backend
Add a new backend for the game NetHack to fmri-gym: create the adapter, the curriculum config, the pyproject extra and the tests row, then run the backend test suite.
Check for an existing engine
Does an existing fmri-gym backend like ale or gym already play this ROM out of the box, or do I need to write a new adapter?
Fix replay failure
My new backend passes playable and recorded checks but fails the replays test — what should I look at in the curriculum keys map?

name: add-backend description: Add a new game engine (backend) to fmri-gym, or wrap a game that has no Gymnasium env: the adapter, the gym/ package, the config, the pyproject extra, the tests row, and the traps that make a backend look fine and replay wrong. Use when asked to add, port or wire up a game.

Adding a backend

Read AGENTS.md first: rule 1 (the game logic lives in the gym env, not the adapter) decides where every line below goes.

The checklist

  1. Check that no existing backend already plays the game, possibly out of the box: ale takes any Atari ROM, retro any libretro core, gym any registered Gymnasium env, vizdoom any Doom scenario. If one does, all you need is a config.
  2. fmri_gym/adapters/<BACKEND>.py: module docstring (what the engine is, why it was chosen, what its observation/action spaces look like, what is not supported), then a subclass of EnvAdapter overriding only the hooks you actually need. Everything else is inherited; do not re-state the defaults. adapters/vizdoom.py, baba.py and minihack.py are the models; stay under about 150 lines, one file.
  3. fmri_gym/adapters/__init__.py: one if backend == ...: branch with a lazy import.
  4. configs/curricula/<BACKEND>/<GAME>.json: a runnable curriculum, and configs/curricula/<BACKEND>/common/README.md with any install step that is not a plain pip install and what the action indices in its keys mean. Comments in the JSON ("## ..." keys) are for what is particular to one file, and few. Lay it out like the others: one ## comment per line, the message's text one screen line per line, fixations as one-liners, and the game block on a few lines with keys (and menu) inline, not one key per line. A game with several levels gets one curriculum per level, whose game phases __include a shared configs/curricula/<BACKEND>/common/<GAME>.json (the keys, the instructions, the settings) and state only the level and its title (README, "Shared phases").
  5. pyproject.toml: one extra under [project.optional-dependencies] (and a mention in dbp / all if it belongs there), then uv lock. A game without a Gymnasium env gets one first, as its own small package under gym/<GAME>/ (pyproject.toml, README.md, <game>_gym/{__init__,env}.py, a gym.register id), listed in [tool.uv.sources]; the extra installs that package. It wraps the game, it does not contain it.
  6. tests/backends.py: a Backend row (the extra, the module that proves it is installed, one or two short smoke configs, the gym/ dirs it runs on, its external/ checkout, a ci reason if a hosted Linux runner cannot play it), and a line in .github/workflows/tests.yml's matrix unless it has one. test_configs fails until the row exists.
  7. README.md: only if the backend needs setup beyond pip install (a repo checkout, a binary, an env var).

Then play it, through the suite and by hand:

uv run pytest --backend <BACKEND>
uv run fmri-play --subject sub-test --dummy-trigger --ses 1 --run 1 --rig configs/rigs/example/laptop.json \
    --curriculum configs/curricula/<BACKEND>/<GAME>.json
uv run ruff check fmri_gym fmri_play.py agents tests

The suite's four checks (playable, recorded, replays, runs) are the acceptance test; the testing skill says how to read a failure. A backend that passes everything but replays is not done: reproducibility is the product.

Things that are easy to get wrong

  • Key names are pygame names, upper-cased, without K_ ("LEFT", "SPACE", "Z"). Add unlisted keys to _PYGAME_KEY_NAMES in keys.py rather than mapping keycodes yourself; validate_curriculum refuses a config that names a key outside that table.
  • Every game phase writes its whole keys map; there is no default. A MultiBinary env takes button indices (held keys combine), anything else takes the action itself and a real-time phase also needs the "" entry, the action for no key held. One class per action space in adapters/keymap.py. Don't add a per-backend default or a merge step: the map depends on the site's input device, not on the engine.
  • Copy observations you keep. Several envs reuse their observation buffers, so capture must .copy() anything it stores (see minihack.py). The replay check is what found nethack logging each frame's variables one frame late: a view of a reused buffer, pickled after the loop had moved on.
  • render() is not always free of side effects. An engine that draws from the same RNG its dynamics use spends a draw on every render, so one extra call shifts everything after it and the run silently stops matching its own log. Return the frame step already produced rather than rendering again; crafter's night noise is the live example, and it is why gym/crafter caches the observation and hands that back.
  • Reproducibility is the product. A block must be replayable from episode_seeds + actions. If the env has no savestate of its own, wrap it in replay_state_gym.ReplayState (gym/replay_state/: the seed and the actions since, restored by replay) and make sure reset(seed=...) really determines the episode. Test it rather than assume it: uv run python -m tests.verify_replay --curriculum <config> records a block with the random policy, replays it in a second process under another PYTHONHASHSEED and compares every frame, every sound chunk and every savestate anchor bit for bit. A second process because the usual culprit is hash order, which is fixed for the life of one interpreter and would let a replay inside the recording process pass. Crafter passes every surface check (a seed argument, a seeded RandomState, deterministic worldgen) and still diverges, because one creature list is built from a Python set and the despawn pick therefore follows object id(), which moves with PYTHONHASHSEED. Sorting that list is the whole fix, and it lives in a fork the env package pins (gym/crafter/pyproject.toml: crafter @ git+https://github.com/chengfanbrain/crafter.git@deterministic), not in the adapter: when the bug is in the engine, fix the engine and pin the build, or the rig starts owning game behaviour.
  • Sound is part of the record. An engine that makes sound returns this step's PCM from sound() (the contract is in EnvAdapter.sound; retro.py is a two-line example); the loop plays it and logs it, and the replay check compares it chunk for chunk. ViZDoom's first episode after init() sounded different from one after new_episode(), which is why vizdoom_gym begins every episode the same way.
  • Turn-based games (grid worlds, puzzles) need "is_turn_based": true in the config.
  • Slow real-time games (a grid world that must keep ticking, so is_turn_based is out) need "has_latched_keys": true: the default real-time path polls held keys, and at a few frames per second a tap that begins and ends between two frames is never seen.

Du fichier au premier essai

Le téléchargement contient exactement le contenu affiché sur cette fiche. Cette copie peut différer de la dernière version du dépôt source.

Consulter la source et ses fichiers annexes

Ce téléchargement contient uniquement SKILL.md. Les scripts, références, modèles et dépendances éventuels ne sont pas inclus. Récupérez les fichiers nécessaires depuis la source en conservant leur arborescence avant de poursuivre.

1. Télécharger le fichier

Télécharger SKILL.md

Depuis la racine d’un projet de test, enregistrez le fichier sous .skill-downloads/add-a-game-backend-to-fmri-gym-mvg6txds/SKILL.md, ou utilisez la commande suivante. Ce dossier sert à examiner la skill avant son installation.

Commande pour macOS/Linux ou WSL : un shell POSIX et curl sont nécessaires. Sous Windows sans WSL, utilisez le bouton de téléchargement et créez le dossier manuellement.

mkdir -p '.skill-downloads/add-a-game-backend-to-fmri-gym-mvg6txds' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/add-a-game-backend-to-fmri-gym-mvg6txds/download' --output '.skill-downloads/add-a-game-backend-to-fmri-gym-mvg6txds/SKILL.md.part' && mv '.skill-downloads/add-a-game-backend-to-fmri-gym-mvg6txds/SKILL.md.part' '.skill-downloads/add-a-game-backend-to-fmri-gym-mvg6txds/SKILL.md'

2. Vérifier les prérequis

Lisez SKILL.md avant de l’utiliser. Si le fichier cite des scripts ou services externes, examinez-les et préparez les accès nécessaires dans votre environnement de test.

Aucune section de prérequis reconnue dans ce fichier. Cela ne signifie pas qu’il est autonome : les bibliothèques, outils et accès peuvent être cités ailleurs dans le contenu ou dans le dépôt source.

Les plateformes de la fiche sont des indications du catalogue. Les chemins ci-dessous suivent la documentation des outils ; ils ne prouvent pas que cette skill y a été exécutée. Aucune version minimale de ces outils n’est attestée pour cette fiche.

3. Charger la skill

Le fichier ne fournit pas un en-tête name/description exploitable pour cette procédure native. Le téléchargement reste disponible pour lecture ; utilisez le mode manuel ci-dessous.

Mode manuel ou autre outil : joignez SKILL.md à une conversation, ou ouvrez-le dans un agent capable de lire les fichiers. Ajoutez les ressources annexes nécessaires. Il n’existe pas de dossier universel reconnu par tous les outils.

4. Faire un premier essai

Utilisez une entrée courte et fictive : un endpoint d’API, un petit document ou un brief d’interface. Dans une nouvelle conversation, joignez le fichier ou donnez son chemin à l’agent, puis adaptez cette demande :

Lis .skill-downloads/add-a-game-backend-to-fmri-gym-mvg6txds/SKILL.md et applique ses instructions à cet exemple : [décrire une entrée de test]. Indique les fichiers, dépendances ou accès qui manquent. Produis le résultat demandé par la skill sans inventer de données ni exécuter de script non examiné.

Résultat attendu : une sortie correspondant aux instructions de la skill, ou une liste précise des prérequis manquants. Une simple confirmation de lecture ne valide pas le premier usage. Si une ressource externe est indisponible, ce parcours reste à tester.

Skills similaires