Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .github/workflows/json-schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ jobs:
- run: python -m pip install -r schemas/requirements.txt
- run: PYTHONPATH=src python -m unittest pokesim.formats.test_json_schema pokesim.story.test_native_export
- run: PYTHONPATH=src python -m pokesim.formats.json_schema --check-published
- name: Audit the full FRLG source candidate without publishing it
- name: Audit the FRLG map set's candidate maps (statuses from the manifest)
run: >-
PYTHONPATH=src python -m pokesim.maps.frlg_import_audit
data/games/frlg/maps/source-candidate --schema
data/games/frlg/maps/runtime --schema
--manifest data/maps/runtime-manifest.json
--scope data/games/frlg/maps/pokeguy-map-scope.json
--out "${{ runner.temp }}/frlg-source-audit.json"
- name: Check full candidate compact formatting
- name: Check FRLG map statuses
run: PYTHONPATH=src python -m pokesim.maps.frlg_map_set check
# Candidate map bytes are checked exactly by frlg-source-reproduction below.
- name: Check the map scope's compact formatting
run: >-
PYTHONPATH=src python -m pokesim.formats.contract_format --check
data/games/frlg/maps/source-candidate/*.json
data/games/frlg/maps/pokeguy-map-scope.json
- uses: actions/upload-artifact@v4
with:
Expand All @@ -45,4 +48,5 @@ jobs:
run: >-
PYTHONPATH=src python -m pokesim.maps.frlg_decomp_map_builder --all
--scope data/games/frlg/maps/pokeguy-map-scope.json
--item-identities --out data/games/frlg/maps/source-candidate --check
--item-identities --out data/games/frlg/maps/runtime
--manifest data/maps/runtime-manifest.json --check
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ with configurable minimum-win-rate and reliability-quota criteria.

The [AutoStory integration plan](docs/autostory-plan.md) tracks map ownership,
route planning and shared FRLG/BDSP simulation. The [imported map packs](docs/maps/runtime-maps.md)
include 53 FRLG and 22 BDSP maps, portable generators, corrections, provenance
include 152 FRLG maps (57 verified, 95 candidates; one set with per-map status) and 22 BDSP maps, portable generators, corrections, provenance
and a byte-identical export for Arduino consumers.
The [local runtime-map planner](docs/navigation/RUNTIME_ROUTES.md) now computes
and draws routes inside a pinned FRLG or BDSP map, recording its inputs and state.
Expand Down
123 changes: 123 additions & 0 deletions data/games/frlg/maps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# FRLG map set

One copy of each FRLG map, in `runtime/`. Each map's **status** lives in its
entry in [`data/maps/runtime-manifest.json`](../../../maps/runtime-manifest.json):

| Status | Meaning | Count |
|---|---|---:|
| `verified` | Reviewed content. `verified.basis` is `approved-runtime-pack` (in the approved pack before this set existed; no per-map run was recorded) or `console-run` (`run`, `date`, `checkpoint`, optional `consumer_revision`). | 57 |
| `candidate` | Generated from `pret/pokefirered@ce6406d8`, unreviewed. Bytes are owned by the generator. | 95 |

Every entry also records its `source` (pret file and commit), the same object
as the map file's `source`. The status is kept out of the map bytes, so
promoting a map changes only the manifest: map hashes stay the same.

`runtime/` is the directory the published manifest and the Arduino-Source
resource pin read (`data/games/frlg/maps/runtime/<Name>.json`). "Runtime" names
the published map format, not approval. There is no other FRLG map directory.

## Workflow

1. **Pull candidates into a run.** Bumping Arduino-Source's
`devtools/maps/map-resources.lock.json` to a newer main commit publishes every
map, candidates included, into `Packages/Resources/Pokemon/Maps/frlg`. AutoStory
parses only the maps named in its own map list, so a candidate is used once
it is added there.
2. **Promote on a console pass.** After a run walks a candidate map, record it:

```sh
PYTHONPATH=src python3 -m pokesim.maps.frlg_map_set promote Route11 \
--run <run record> --date 2026-11-10 --checkpoint 00.14 \
--consumer-revision <Arduino-Source commit>
```
3. **Publish the status.** Merge the manifest change to main and bump the
consumer lock to that commit (`manifest_sha256` changes; map bytes do not).

A map **fix** is a separate change. It edits the map, updates its manifest
hash/corrections with evidence, and needs the usual review. Do not substitute a
candidate's bytes for a verified map to make a route pass. To regenerate
candidates after a generator change, generate into an empty directory, copy only
`candidate` maps into `runtime/`, then run `frlg_map_set sync-candidates`.
That command rewrites candidate entries from the files and never touches verified ones.

## Scope

[`pokeguy-map-scope.json`](pokeguy-map-scope.json) lists the 285 catalog maps
the generator does **not** publish as candidates (425-map catalog):

| Excluded group | Maps |
|---|---:|
| Sevii, event islands and island Trainer Tower | 164 |
| Multiplayer facilities | 5 |
| Optional mainland caves, routes and legendary destinations | 13 |
| Unused interiors, cabins and upper floors | 77 |
| Mainland Pokemon Centers and elevators (scripted in AutoStory) | 26 |

**Pokemon Centers and elevators are scripted in AutoStory, not mapped.** The
Centers are mostly identical and the elevators are small, so a per-building map
costs more than a script. `IndigoPlateau_PokemonCenter_1F` stays a candidate
because it is the Elite Four lobby. Eight Center 1F maps were verified before
this decision and stay in the set with their status.

Verified maps may lie outside the scope: those 8 Centers, `CeruleanCave_1F`,
`CeruleanCave_2F`, `CeruleanCave_B1F`, `PalletTown_RivalsHouse` and
`PewterCity_Museum_1F`. Every candidate must be in scope and every in-scope
map must be present.

This is a conservative first-Champion scope, not a visited-map ledger. The ship
healing cabin is **SSAnne_1F_Room6** (pinned source heal script). Tea, Fly,
Bill, Safari Surf and Warden reward rooms are retained. LeafGreen-specific
version conditions still need review.

## Checks

```sh
PYTHONPATH=src python3 -m pokesim.maps.frlg_map_set check
PYTHONPATH=src python3 -m pokesim.maps.runtime_map_pack
PYTHONPATH=src python3 -m pokesim.maps.frlg_decomp_map_builder --all \
--scope data/games/frlg/maps/pokeguy-map-scope.json --item-identities \
--out data/games/frlg/maps/runtime --manifest data/maps/runtime-manifest.json --check
python3 -m pip install -r schemas/requirements.txt
PYTHONPATH=src python3 -m pokesim.maps.frlg_import_audit data/games/frlg/maps/runtime \
--schema --manifest data/maps/runtime-manifest.json \
--scope data/games/frlg/maps/pokeguy-map-scope.json --out /tmp/frlg-import-audit.json
```

The generator `--check` regenerates the in-scope catalog. It requires every
candidate to match byte for byte and skips verified maps (reviewed content may
legitimately differ from the generator). The pack audit allows exactly two
kinds of unresolved link:

- **Boundary**: the destination is scope-excluded and absent, so the link is a dead end, never a successful transition (59 warps, 2 connections).
- **Pending relink**: a candidate warps into a verified map that has no return point yet (31 warps). Verified maps were built while their neighbors were absent, so their warps toward candidates carry no destination, and some candidate connections are one-way. Relinking edits reviewed maps and needs approval with evidence.

Pending relinks by verified map: CeruleanCity (CeruleanCity_House1),
LavenderTown (LavenderTown_Mart, LavenderTown_VolunteerPokemonHouse,
PokemonTower_1F), Route2 (Route2_EastBuilding), Route22 (Route22_NorthEntrance),
Route5 (Route5_SouthEntrance), Route6 (Route6_NorthEntrance),
SSAnne_1F_Corridor (SSAnne_1F_Room6), VermilionCity (VermilionCity_Mart),
ViridianCity (ViridianCity_Gym). One-way connections into verified maps come from
Route21_North, Route12, Route23, Route8, SaffronCity_Connection, Route11 and
SaffronCity.

## Candidate gaps

| Gap | Count |
|---|---:|
| Cells with unsupported non-collision behavior | 0 |
| Dynamic warp destinations | 0 |
| Pickups without a pinned identity catalog entry | 79 |

84 candidates omit their elevation grid with an `elevation_omitted` reason in
`source`. Every relevant walkable cell is then default ground (3). 11 keep their
grids (bridges, stairs, multi-level terrain). Glyphs beyond the original set are
documented in [tile-legend.json](tile-legend.json): spin tiles `u d l r o`
(RocketHideout_B2F/B3F, ViridianCity_Gym), holes `H` (PokemonMansion_3F,
VictoryRoad_3F) and Strength buttons `B` (VictoryRoad_1F/2F/3F). Arduino's
native decoder needs those glyphs before those maps join its map list.

NPCs and pickups keep source event, script and optional local-ID provenance.
`source_event` is a zero-based source array index, not an in-game object ID.
Scripted geometry, flags, hidden/background interactions, coordinate triggers
and game-version differences still need review. Structural validity does not
establish story traversability.
60 changes: 33 additions & 27 deletions data/games/frlg/maps/pokeguy-map-scope.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"format": "pokesim-frlg-map-scope/1",
"source_revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"catalog_count": 425,
"purpose": "PokeGuy first-Champion candidate; conservative review scope, not an exact visited-map ledger.",
"purpose": "PokeGuy first-Champion candidate scope: the maps the generator publishes as candidates. Conservative review scope, not an exact visited-map ledger. Verified maps may lie outside it; see README.md.",
"evidence": {
"notes_commit": "11cbf77e0271e97b3639f9d5d419d0549ebec0bf",
"notes_path": "pokemon_sv/FRLG_POKEGUY_15934_VIDEO_NOTES.md",
Expand Down Expand Up @@ -205,7 +205,7 @@
},
{
"reason": "unused-interiors",
"basis": "No entry in the reviewed run; required reward rooms, traversed houses, ship healing cabin and uncertain Silph floors are retained. Alternative healing plans may need to restore Pokemon Centers.",
"basis": "No entry in the reviewed run; required reward rooms, traversed houses, ship healing cabin and uncertain Silph floors are retained.",
"maps": [
"CeladonCity_Condominiums_2F",
"CeladonCity_Condominiums_3F",
Expand All @@ -214,46 +214,29 @@
"CeladonCity_DepartmentStore_2F",
"CeladonCity_DepartmentStore_3F",
"CeladonCity_DepartmentStore_4F",
"CeladonCity_DepartmentStore_Elevator",
"CeladonCity_DepartmentStore_Roof",
"CeladonCity_GameCorner_PrizeRoom",
"CeladonCity_Hotel",
"CeladonCity_House1",
"CeladonCity_PokemonCenter_1F",
"CeladonCity_PokemonCenter_2F",
"CeladonCity_Restaurant",
"CeruleanCity_House3",
"CeruleanCity_House4",
"CeruleanCity_House5",
"CeruleanCity_PokemonCenter_1F",
"CeruleanCity_PokemonCenter_2F",
"CinnabarIsland_PokemonCenter_1F",
"CinnabarIsland_PokemonCenter_2F",
"CinnabarIsland_PokemonLab_Entrance",
"CinnabarIsland_PokemonLab_ExperimentRoom",
"CinnabarIsland_PokemonLab_Lounge",
"CinnabarIsland_PokemonLab_ResearchRoom",
"FuchsiaCity_House1",
"FuchsiaCity_House2",
"FuchsiaCity_House3",
"FuchsiaCity_PokemonCenter_1F",
"FuchsiaCity_PokemonCenter_2F",
"FuchsiaCity_SafariZone_Office",
"IndigoPlateau_PokemonCenter_2F",
"LavenderTown_House1",
"LavenderTown_House2",
"LavenderTown_PokemonCenter_1F",
"LavenderTown_PokemonCenter_2F",
"PalletTown_RivalsHouse",
"PewterCity_House1",
"PewterCity_House2",
"PewterCity_Museum_1F",
"PewterCity_Museum_2F",
"PewterCity_PokemonCenter_1F",
"PewterCity_PokemonCenter_2F",
"RocketHideout_Elevator",
"Route10_PokemonCenter_1F",
"Route10_PokemonCenter_2F",
"Route11_EastEntrance_2F",
"Route12_FishingHouse",
"Route12_NorthEntrance_2F",
Expand All @@ -263,8 +246,6 @@
"Route19_UnusedHouse",
"Route23_UnusedHouse",
"Route2_House",
"Route4_PokemonCenter_1F",
"Route4_PokemonCenter_2F",
"Route5_PokemonDayCare",
"Route6_UnusedHouse",
"SSAnne_1F_Room1",
Expand Down Expand Up @@ -297,19 +278,44 @@
"SaffronCity_Dojo",
"SaffronCity_House",
"SaffronCity_MrPsychicsHouse",
"SaffronCity_PokemonCenter_1F",
"SaffronCity_PokemonCenter_2F",
"SaffronCity_PokemonTrainerFanClub",
"SilphCo_Elevator",
"VermilionCity_House1",
"VermilionCity_House2",
"VermilionCity_House3",
"ViridianCity_House",
"ViridianCity_School"
]
},
{
"reason": "scripted-in-autostory",
"basis": "Mainland Pokemon Centers (1F/2F) and elevators are scripted in AutoStory, not mapped: the Centers are mostly identical and the elevators are small, so per-building maps cost more than a script. IndigoPlateau_PokemonCenter_1F stays mapped as the Elite Four lobby. Center maps already verified before this decision remain in the map set with their status.",
"maps": [
"CeladonCity_DepartmentStore_Elevator",
"CeladonCity_PokemonCenter_1F",
"CeladonCity_PokemonCenter_2F",
"CeruleanCity_PokemonCenter_1F",
"CeruleanCity_PokemonCenter_2F",
"CinnabarIsland_PokemonCenter_1F",
"CinnabarIsland_PokemonCenter_2F",
"FuchsiaCity_PokemonCenter_1F",
"FuchsiaCity_PokemonCenter_2F",
"IndigoPlateau_PokemonCenter_2F",
"LavenderTown_PokemonCenter_1F",
"LavenderTown_PokemonCenter_2F",
"PewterCity_PokemonCenter_1F",
"PewterCity_PokemonCenter_2F",
"RocketHideout_Elevator",
"Route10_PokemonCenter_1F",
"Route10_PokemonCenter_2F",
"Route4_PokemonCenter_1F",
"Route4_PokemonCenter_2F",
"SaffronCity_PokemonCenter_1F",
"SaffronCity_PokemonCenter_2F",
"SilphCo_Elevator",
"VermilionCity_PokemonCenter_1F",
"VermilionCity_PokemonCenter_2F",
"ViridianCity_House",
"ViridianCity_PokemonCenter_1F",
"ViridianCity_PokemonCenter_2F",
"ViridianCity_School"
"ViridianCity_PokemonCenter_2F"
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeladonCity/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable land is ground level (3); water is flat without bridges"
},
"tiles": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeladonCity_Condominiums_1F/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable tiles are ground level (3); non-ground levels appear only on unwalkable walls/obstacles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeladonCity_DepartmentStore_1F/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable tiles are ground level (3); non-ground levels appear only on unwalkable walls/obstacles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeladonCity_DepartmentStore_5F/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable tiles are ground level (3); non-ground levels appear only on unwalkable walls/obstacles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeladonCity_GameCorner/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable tiles are ground level (3); non-ground levels appear only on unwalkable walls/obstacles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeladonCity_Gym/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all reachable land is ground level (3); non-ground tiles are unreachable boundary or counter tiles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CeruleanCity_House1/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable tiles are ground level (3); non-ground levels appear only on unwalkable walls/obstacles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CinnabarIsland/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable land is ground level (3); water is flat without bridges"
},
"tiles": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CinnabarIsland_Gym/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all reachable land is ground level (3); non-ground tiles are unreachable boundary or counter tiles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/CinnabarIsland_Mart/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all reachable land is ground level (3); non-ground tiles are unreachable boundary or counter tiles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/FuchsiaCity/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all reachable land is ground level (3); non-ground tiles are unreachable boundary or counter tiles"
},
"tiles": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/FuchsiaCity_Gym/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all walkable tiles are ground level (3); non-ground levels appear only on unwalkable walls/obstacles"
},
"allow_running": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"decomp_file": "data/maps/FuchsiaCity_Mart/map.json",
"revision": "ce6406d8c2eaf46d543c8a2606de0c3cb1951982",
"date": "2026-10-31",
"generation_status": "unreviewed-full-catalog-candidate",
"elevation_omitted": "all reachable land is ground level (3); non-ground tiles are unreachable boundary or counter tiles"
},
"allow_running": false,
Expand Down
Loading
Loading