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.lua runs 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 the firered_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.js hosts 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

  1. Entry point – start the Node server with node server/index.js. It creates an HTTP server, enables CORS, and launches the WebSocket hub.
  2. Game loop – server/src/core/gameLoop.js (13 outgoing imports) repeatedly calls server/src/core/openaiClient.js to send the current snapshot (built from server/src/services/pythonService.js → HTTP GET /state on the FastAPI bridge).
  3. Bridge request – pythonService.js contacts http://127.0.0.1:8000/state (FastAPI). The FastAPI endpoint in firered_mgba_bridge.py invokes firered_bridge/game_state.py, which aggregates data from: firered_bridge/memory/reader.py (reads raw bytes via the socket client in firered_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.
  4. Decision – promptBuilder.js formats the snapshot, openaiClient.js calls the OpenAI API, and the response is parsed back into a concrete controller command.
  5. Action – pythonService.js POSTs the command to the FastAPI bridge, which forwards it to the Lua socket via firered_bridge/memory/control.py.
  6. 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.json has no accompanying package-lock.json or yarn.lock.
  • HIGH – No CI pipeline – No .github/workflows or other CI config.
  • HIGH – Wildcard CORS – server/index.js uses cors() 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.