← Engineering log
Games Knowledge Graph Autonomous Agents eLearning

JRPG KGraph Parser & Automated Playthrough, Part 2: Rebuilding Final Fantasy (1990 NES) from Coneria to Defeating Garland

In Part 1 we introduced robos-jrpg-parser and the beginner bootcamp on Ember Quest and Quest: Warrior Dragon. In Part 2 we walk through our separate, full-scale build of the 1990 North American NES release of Final Fantasy: harvesting 82 reference files into a private folder, closing the 5 gameplay blockers from the 6502 disassembly (including the critical-hit index bug and indoor ceiling palette swap), building the 4-hero Godot 4.3 engine with 2-step NES shop interiors, passing 7 isolated headless Cucumber suites, and clearing all 12 Chapter 1 milestones to defeat Garland in the RobOS Game Playthrough viewer.

In Part 1 of this series (and the companion Part 1 eLearning course), we introduced packages/robos-jrpg-parser on a compact practice game (Ember Quest) and a single-hero Godot 4 bootcamp (Quest: Warrior Dragon). In parallel, we ran the exact same pipeline against a much larger target in a separate private workspace: the 1990 North American NES release of Final Fantasy.

Today we are publishing Part 2 of the Interactive eLearning Course (/elearning/how-to-recreate-classic-jrpg-using-kgraph-parsing-with-automated-playthrough-part-2/) alongside this technical deep dive, walking through every engineering hurdle we hit taking the 4 Light Warriors from the title screen through Coneria's shops, Coneria Castle, and the Temple of Fiends to defeat Garland—and documenting exactly where the build left off at the North Bridge gateway to Chapter 2.

RobOS Game Playthrough desktop viewer showing all 12 completed Final Fantasy Chapter 1 milestones, the Garland boss victory screen, and Checkpoint #12 state
The RobOS Game Playthrough desktop app (packages/game-playthrough --game-dir ~/Downloads/ff1/ff1-game) after completing all 12 Chapter 1 milestones in 100.9 seconds with 0 deaths: Garland (106 HP) is defeated, Princess Sara is rescued, and the North Bridge is built.

1. Harvesting 82 Mixed Reference Files & Quarantining GBA/PS1 Remakes

Scaling from a 12-file demo to a real 1990 cartridge begins with reusable acquisition. Rather than hardcoding URLs for one game, packages/robos-jrpg-parser/tools/harvest_jrpg_assets.py reads a saved VGMaps NES index page, matches all 27 FinalFantasy-*.png atlas sheets, queues 14 StrategyWiki guide pages, and writes both ~/Downloads/ff1/sources-manifest.json and ~/Downloads/ff1/download-receipt.json (recording SHA-256 hashes and PNG IHDR dimensions).

Inspecting download-receipt.json exposed two immediate physical facts:

  • Exact Overworld vs. Cropped Town Sheets: While FinalFantasy-World(Unmarked).png is 4096×4096 pixels (an exact 256×256 grid of 16×16 tiles), VGMaps town and dungeon PNGs such as FinalFantasy-Coneria.png (987×544) and FinalFantasy-TempleOfFiends(Present).png (805×594) are cropped reference sheets where human mappers trimmed repeating outer border trees and composited multiple floors onto one canvas.
  • Remake & Balance-Mod Contamination: Real community folders mix editions. Running jrpg-parser.js scan ~/Downloads/ff1 --edition "NES (USA)" classified 61 authentic NES files as usable, skipped 12 saved-page _files/ directories, and quarantined 9 non-NES files—including GBA Dawn of Souls screenshots, PS1 Origins battle sheets, and a saved forum thread (shop.html, "Let's Play Dawn of Souls FF1: Mod of Balance!") whose rebalanced 50 G spell prices would otherwise overwrite the 100 G 1990 NES magic tables.
Terminal output inspecting download-receipt.json showing 4096x4096 Overworld vs cropped 987x544 Coneria PNG dimensions
Inspecting PNG IHDR dimensions in download-receipt.json before slicing a single tile.
Terminal output of jrpg-parser.js scan quarantining GBA Dawn of Souls, PS1 Origins, and modded forum files
jrpg-parser.js scan quarantines GBA, PS1, PSP, and modded files (usable: false) so remake numbers never enter the NES graph.

2. The Verbatim Quote Gate & Why Fan Guides Left 5 Engine Blockers

During jrpg-parser.js parse, structured HTML and Markdown tables are parsed deterministically by lib/charts.js, while unstructured walkthrough prose is extracted through lib/extract.js behind a hard verbatim quote gate: if the quote field returned by the AI agent is not literally present in the local file on disk, the record is dropped. In our FF1 run, that gate caught and dropped 4 unverified records where the model attempted to supply remake facts from pretraining memory, and parsed.json surfaced 5 cross-source conflicts (such as normalizing a prose walkthrough's GBA Mythril Sword alias to the 1990 NES Silver Sword).

Even more importantly, running jrpg-parser.js coverage --out ~/kgraphs/final-fantasy-nes after scraping the web guides and the 84-page Nintendo manual (CLV-P-NAAAE.pdf) showed why prose guides alone cannot run a game engine. Guides record what players see in menus (monster HP/XP/Gold, spell names and prices), leaving five engine blockers: missing enemy combat stats (ATK, ACC, Hits, CRT, DEF, EVA, MDEF), missing 8-slot encounter formation tables, missing 50-level class stat/XP curves, missing numeric spell Effectivity (Power E) and Accuracy bytes, and missing 64×64 walkable tile grids with exact door and chest coordinates.

Parser output showing deterministic table counts and 4 unverified prose records dropped by the verbatim quote gate
Deterministic table extraction paired with the verbatim quote gate in lib/extract.js.
Completeness audit matrix showing the 5 gameplay blockers left after scraping web guides
The initial coverage audit turns "we're not done" into a specific 5-item engineering checklist.

3. Closing the Gaps from the 6502 Disassembly (Including 3 Famous 1990 NES Quirks)

To close all five blockers with 100% hardware accuracy, we cross-referenced Disch's commented 6502 disassembly (~/Downloads/ff1/nes_disassembly/) with AstralEsper's NES Game Mechanics Guide and wrote build_ff1_resources.py + update_reference_guide.py. That decoded all 128 enemies, 64 spells, 12 classes across 50 levels, 84 encounter zones, 256 treasure chests, and 61 RLE-compressed 64×64 map grids into ~/Downloads/ff1/nes_data/ and map_grids/, and appended structured Markdown tables to ff1_nes_agent_reference_guide.md.

Reading the 6502 assembly also preserved three famous 1990 NES quirks in our engine:

  1. The Weapon Critical Hit Index Bug: In LoadOneCharacterIBStats, a pointer bug loads the weapon's 1-based inventory index (1..40) instead of its critical byte, giving the Wooden Nunchuck (ID 1) a 1/201 crit rate and the Masamune (ID 40) a 40/201 (~19.9%) crit rate.
  2. The Level-Up HP & Strong-Level Bitmask: Every level-up grants floor(VIT / 4) + 1 HP, plus 20..25 bonus HP on levels flagged in the class's 49-level strong-HP bitmask table.
  3. The INT Magic Bug: Caster INT never scales magic in the 1990 NES release; Level 1 CURE (E=16) heals 16..32 HP and Level 1 FIRE (E=10, ACC=24) rolls 10..20 (doubling to 20..40 when 0..255 < 148 + Spell_Acc - Target_MDEF).
Terminal output inspecting IMP, GARLAND, CURE, and FIRE combat stats and the 3 preserved 1990 NES quirks
Exact combat profiles for IMP (8 HP) and GARLAND (106 HP, 15 ATK, 10 DEF) alongside numeric spell power bytes.
Re-running jrpg-parser.js parse producing 1,206 validated OSLC JSON-LD nodes with 0 blockers
Re-running jrpg-parser.js parse produces 1,206 validated OSLC JSON-LD nodes across 13 RDF types with zero blockers.

4. 64×64 Walkable Grids, the Tree Spawn Bug, and the Indoor Ceiling Palette Swap

When we first booted our Godot 4.3 project (~/Downloads/ff1/ff1-game) and ran our opening Gherkin scenario, every step passed—yet inspecting the recorded video frames exposed two classic visual bugs that unit tests never see:

  • The South Gate Border Tree at (15, 22): Checking row y=21 of map_00_coneria.json ([14, 14, 16, 14, 14]) showed that Coneria's south entrance is a single brick column wide at x=16 (tileId=16, matching lut_EntrTele_X = 16) flanked by unwalkable border trees at x=15 and x=17 (tileId=14). Moving spawn_tile from (15, 22) to Vector2i(16, 21) placed the party squarely on the brick street.
  • Outside-Room Ceiling Palette vs. Inside-Room Chest Palette: In lut_SMPalettes ($A000), Outside-Room Palette 0 (+0x00) is [0x0F, 0x30, 0x30, 0x30] (solid white) so the NES PPU can hide unentered rooms behind a white ceiling, while Inside-Room Palette 0 (+0x20) is [0x0F, 0x10, 0x17, 0x0F] (black, silver, and wood brown). Swapping in Inside-Room Palette 0 and removing a debug ColorRect overlay turned flat teal squares back into crisp silver-and-brown treasure chests.
Inspecting map_00_coneria.json around row 21 showing the walkable brick corridor at x=16 and shop door triggers
Decompressed map_00_coneria.json showing the 1-tile brick corridor at x=16 and Coneria's shop door coordinates.
Comparison of Outside-Room Palette 0 (+0x00 white ceiling) vs Inside-Room Palette 0 (+0x20 silver and wood brown treasure chests)
Swapping Outside-Room Palette 0 (+0x00) for Inside-Room Palette 0 (+0x20) restores the indoor floor and silver-and-brown chests.

5. Clean-Room Art & the Authentic 2-Step NES Shop Interior

Using jrpg-generate-art (packages/robos-lib/ai-image.js + nesify.py), we generated 50 clean-room NES-quantized sprites, backdrops, and maps and ran jrpg-parser.js publish to export ~/kgraphs/final-fantasy-nes-public (1,184 fact nodes + 50 clean-room images, zero source filenames or quotes).

Next came the most important interactive fix of Chapter 1. When we first let the AI player loose in Coneria to buy gear before fighting Garland, it walked onto the Coneria Weapon Shop door at (11, 10) and got stuck in an infinite loop. To fix both the visual presentation and the state machine, we built the authentic NES 2-step Shop interior inside FF1MapScene.gd for all six shop types (WEAPON, ARMOR, WHITE, BLACK, INN, ITEM):

  • Stepping onto a shop door opens the royal-blue NES shop layout with the top-left shop banner, the center shopkeeper counter, the item/spell price list on the right, the greeting dialogue box, the live Gold box (400 G), and the white pointing-hand glove cursor positioned on Buy (shop_cursor = 0).
  • Pressing ui_accept on Buy equips the class-appropriate upgrades (Rapier on ZEST, Hammer on ROBO, Chain Mail at the Armor Shop, CURE/RUSE at White Magic, FIRE/LIT at Black Magic), deducts Gold live, sets the milestone flag, and automatically advances the pointing-hand cursor to Exit (shop_cursor = 1).
  • Pressing ui_accept on Exit closes the shop overlay and steps the party one tile south off the doorway trigger so it never re-enters the shop in a loop.
Clean-room Coneria Weapon Shop interior on entry with 400 G and the pointing-hand cursor on Buy
Step 0: Entering the Coneria Weapon Shop at (11, 10) places the white pointing-hand cursor on Buy (index 0) with 400 G.
Clean-room Coneria Weapon Shop after buying Rapier and Hammer, showing 380 G and the pointing-hand cursor on Exit
Step 1: Pressing ui_accept buys and equips Rapier & Hammer, updates Gold to 380 G, and moves the cursor to Exit (index 1).

6. Seven Isolated Headless Cucumber Suites → 12-Milestone Stepped Playthrough

Rather than discovering mechanic bugs midway through a full-game run, we built and passed 7 headless Cucumber suites in ~/Downloads/ff1/ff1-game/tests/e2e/features/:

  • 01_start_new_game_and_create_party.feature — TitleScreen → PartyCreate (ZEST, SAIL, ROBO, MAGE) → Coneria (16, 21).
  • 02_smoke_maps_and_transitions.feature — Town, Overworld, Castle 1F/2F, and Temple of Fiends transitions.
  • 03_npc_dialogues.feature — Facing the Coneria town guard and the King of Coneria and dismissing dialogues cleanly.
  • 04_shops_and_inn.feature — All 6 Coneria shop and Inn scenarios (46 steps passed in 11.3s).
  • 05_treasure_chests.feature — Looting Heal Potion (3,3), Cabin (2,3), and Leather Cap (4,14) in the Temple of Fiends with open-chest persistence.
  • 06_random_encounters_and_boss.feature — Overworld IMP encounter and the GARLAND boss fight (106 HP) at (19, 5).
  • 07_checkpoint_kill_garland_1.feature — Checkpoint-chained Act I verification across Milestones 1..6, 7..8, and 9..12 (21 steps passed in 72.1s).
Terminal output of behave running 04_shops_and_inn.feature passing all 6 scenarios and 46 steps in 11.3 seconds
04_shops_and_inn.feature verifies Weapon, Armor, White Magic, Black Magic, Item, and Inn interiors headlessly in 11.3 seconds.
Garland boss battle victory screen paired with 07_checkpoint_kill_garland_1.feature passing
Defeating Garland (106 HP) in the Temple of Fiends at the end of 07_checkpoint_kill_garland_1.feature.

7. Where We Left Off & The Gateway to Chapter 2

With all 7 isolated suites green, running python3 tests/e2e/play_full_game.py --fresh --stepped --time-scale 3.0 clears all 12 Chapter 1 milestones in 100.9s with 0 deaths, writing 12 per-milestone MP4 videos, 12 JSON save checkpoints, and 12 PNG screenshots into tests/e2e/reports/playthrough/ for inspection in the RobOS Game Playthrough viewer.

Terminal output of play_full_game.py --fresh --stepped completing all 12 milestones in 100.9s
Stepped playthrough summary: all 12/12 Act I milestones completed in 100.9 seconds with 0 deaths.
Replaying Milestone #3 from Checkpoint #2 in 8.4 seconds and inspecting Checkpoint #12 JSON
Replaying Milestone #3 alone (--stepped --only 3) in 8.4 seconds and inspecting checkpoints/12-defeat-garland-1.json.

Where the FF1 project sits right now: At the end of Milestone #12 (checkpoints/12-defeat-garland-1.json), the Light Warriors have defeated Garland in the Temple of Fiends, rescued Princess Sara, and unlocked the North Bridge (bridge_built: true). Because our 1,206-node Knowledge Graph in ~/kgraphs/final-fantasy-nes and the 61 decompressed 64×64 map grids in ~/Downloads/ff1/map_grids/ already contain every town, dungeon, monster, spell, shop, and treasure chest in the 1990 NES cartridge, Chapter 2 picks up directly at Checkpoint #12: crossing the North Bridge to Pravoka, defeating Bikke's Pirates to win the Ship, sailing to Elfland, looting the Crown from the Marsh Cave, and defeating Astos.