Chris Shinnimin

Final Fantasy Bot Project Journal

Final Fantasy Bot Project Journal

A fun personal project to learn LLMs and React, and rekindle my love of a favourite childhood game.

September 3, 2025

Prev | Project Journal Entries | Github | Next

Preparing to Read and Write NES Memory

One of the first requirements of the bot app will be having the ability to read and write from the NES memory dynamically. The easiest way to accomplish this will be to use the built in ability of the FCEUX emulator to use LUA scripts to manipulate the RAM. We will eventually also need to catalog the memory addresses of interest that we will want to read and write to. Since an LLM will need to understand what each memory address does, I have a JSON catalog with descriptions of what each address controls in mind.

Use of RAMdisk

The FFBot will use a RAMdisk to provide a location for the LUA scripts to write NES memory for the bot app to read and consume, as well as provide a location for the bot app to drop executable LUA scripts when it wants to write to NES memory. Since the main LUA daemon will need to execute at least once a second (possibly more frequently) to create a real-time communication between the bot and the NES, RAMdisk will be a faster approach than doing so many reads and writes on a physical disk.

LUA JSON Requirement

I envision a JSON catalog on the RAMdisk that maps NES RAM addresses to verbose descriptions of what each memory location controls, so that the LLM will be able to better understand how to manipulate the RAM based on requests it gets. The LUA scripts will also dump the real-time memory values in JSON format for the LLM to consume. This means LUA will need to interact with JSON, which it cannot natively. After doing some research I settled on using the lunajson library. I updated the README with instructions to install.

An Easy Way to Launch

I also decided to create a bash script that will contain configuration and set up the environment for the user. The script is so far responsible for:

  1. Storing configuration, such as the location of the lunajson library.
  2. Exporting environment variables that will be needed by LUA and the bot app.
  3. Mounting (after first unmounting an old copy) the RAMdisk.
  4. Setting up any initial files required by the RAMdisk.
  5. Launching the NES emulator.

Version 1.0 of the LUA Daemon

I've also created the first version of the LUA daemon. So far only capable of writing to the NES memory (not yet capable of dumping the NES RAM). The first version of the LUA daemon looks like this:

local RAMDISK_DIR = os.getenv("RAMDISK_DIR")
local LUA_PACKAGE_DIR = os.getenv("LUA_PACKAGE_DIR")
local EXECUTION_CADENCE = 30 -- NES / FCEUX runs at about 60 frames per second

local frame_count = 0

package.path = package.path .. ";" .. LUA_PACKAGE_DIR .. "?.lua"
local json = require("lunajson")

while true do
    if (frame_count < EXECUTION_CADENCE) then
        frame_count = frame_count + 1
    else
        frame_count = 0

        local result = dofile(RAMDISK_DIR .. "execute.lua")

        if (result) then
            print("Executed a script")
            io.open(RAMDISK_DIR .. "execute.lua","w"):close() -- clear contents of execute.lua
        end
    end

    emu.frameadvance() -- crucial for allowing the emulator to advance a frame
end

So far the daemon successfully loads the lunajson package (which it does not yet use, that is yet to come), and implements a main execution loop. I've learned that the NES runs at approximately 60 frames per second (FPS). I created an EXECUTION_CADENCE constant to specify how many frames should execute before the daemon actually does something itself. It's currently set to 30, meaning the daemon will actually "do it's thing" twice per second. What it currently does is check the contents of a file called execute.lua on the RAMdisk. In the future, the LLM / FFBot app will be able to place lua script contents into this file to update NES RAM.

Demo of Today's Accomplishments

Prev | Project Journal Entries | Github | Next