The Problem
Running a Game Boy Advance title under full programmatic control is difficult because the emulator exposes only raw input/output, while the game logic lives in memory addresses that change across versions. Developers need a reliable bridge to read state, a decision engine that can act in real time, and a way to monitor progress without digging into the emulator each frame.
What This Does
The repository stitches three components together:
mgba/scripts/FireRedBridgeSocketServer.luaruns inside mGBA and streams memory reads / input commands over a TCP socket.firered_mgba_bridge.py(FastAPI) wraps that socket in a REST API. It imports thefirered_bridge/package – e.g.firered_bridge/memory/reader.py,firered_bridge/game_state.py,firered_bridge/ui/dialog.py– to translate raw bytes into high‑level game objects (player party, map, fog‑of‑war).server/index.jshosts the AI loop (server/src/core/gameLoop.js) and a WebSocket hub (server/src/core/socketHub.js). It pulls the latest state from the Python bridge, builds an OpenAI prompt (server/src/ai/promptBuilder.js), receives the LLM’s action, and posts the corresponding input back through the bridge.
A minimal front‑end (frontend/app.js + frontend/index.html) connects to the WebSocket and displays logs, a minimap, and inventory.
How It Is Wired
- Entry point – start the Node server with
node server/index.js. It creates an HTTP server, enables CORS, and launches the WebSocket hub. - Game loop –
server/src/core/gameLoop.js(13 outgoing imports) repeatedly callsserver/src/core/openaiClient.jsto send the current snapshot (built fromserver/src/services/pythonService.js→ HTTP GET/stateon the FastAPI bridge). - Bridge request –
pythonService.jscontactshttp://127.0.0.1:8000/state(FastAPI). The FastAPI endpoint infirered_mgba_bridge.pyinvokesfirered_bridge/game_state.py, which aggregates data from:firered_bridge/memory/reader.py(reads raw bytes via the socket client infirered_bridge/memory/mgba.py),firered_bridge/constants/addresses.py(hub: 19 modules import it, 1 module imports it), *firered_bridge/ui/dialog.py(high cognitive load, 652 lines) for UI‑related state. The import graph shows 60 internal modules with 175 edges and no cycles, so the data path is linear. - Decision –
promptBuilder.jsformats the snapshot,openaiClient.jscalls the OpenAI API, and the response is parsed back into a concrete controller command. - Action –
pythonService.jsPOSTs the command to the FastAPI bridge, which forwards it to the Lua socket viafirered_bridge/memory/control.py. - Dashboard – The WebSocket hub pushes the same snapshot and the AI’s chosen action to
frontend/app.js, which renders the UI.
The widest blast radius belongs to firered_bridge/constants/addresses.py (19 dependents) and server/src/config.js (11 dependents). Changing those files can affect most of the codebase.
How To Use It
# 1. Clone (use the exact URL)
git clone https://github.com/moses-y/gpt-play-pokemon-firered
cd gpt-play-pokemon-firered
# 2. Install Python deps
python -m pip install -r requirements.txt
# 3. Install Node deps
cd server && npm install && cd ..
# 4. Copy environment templates
cp .env.example .env
cp server/.env.example server/.env
# Edit server/.env → set OPENAI_API_KEY
# Edit .env if you need to change MGBA socket ports
# 5. Start the emulator with the Lua script
# (open mGBA → load FireRed ROM → Tools → Scripting → load mgba/scripts/FireRedBridgeSocketServer.lua)
# 6. Run the Python bridge
uvicorn firered_mgba_bridge:app --host 127.0.0.1 --port 8000
# 7. Launch the AI server
node server/index.js
Open frontend/index.html in a browser; it will connect to ws://localhost:9885 and display live status.
Real‑World Use
A QA team can spin up the stack on a CI runner, point it at a known ROM, and let the LLM explore the game automatically. The dashboard provides a visual log of routes taken, items collected, and failures, enabling rapid regression testing of ROM hacks or AI‑driven speed‑run strategies.
Code Health & Issues
- HIGH – No test suite – 61 source files, no test files.
- HIGH – Missing lockfile –
server/package.jsonhas no accompanyingpackage-lock.jsonoryarn.lock. - HIGH – No CI pipeline – No
.github/workflowsor other CI config. - HIGH – Wildcard CORS –
server/index.jsusescors()with default (allow any origin) while credentials are enabled. - MEDIUM – Dependabot not enabled – Two manifest files, no update bot config.
- LOW – Missing convention files – No
.editorconfig,.gitattributes, or formatter config.
Additional static findings (not in the health audit) include oversized modules (addresses.py, snapshot.py, dialog.py > 600 lines), broad except: blocks, deep nesting (up to 6 levels), duplicated code blocks across player and world modules, and a file opened without a context manager (firered_bridge/ui/menus.py).
The Bottom Line
The repo delivers a functional end‑to‑end pipeline that lets an LLM control FireRed in real time, with a clear separation between emulator bridge, AI loop, and UI. However, the codebase suffers from high cognitive load, no automated testing or CI, and security‑related CORS settings. It is suitable for experimentation or research but requires refactoring and process improvements before production or extensive community use.