Skip to content

[FRLG RNG] Check free party slots in applicable RNG programs - #1881

Open
dayfine wants to merge 15 commits into
PokemonAutomation:mainfrom
dayfine:upstream/frlg-free-party-slots
Open

dayfine wants to merge 15 commits into
PokemonAutomation:mainfrom
dayfine:upstream/frlg-free-party-slots

Conversation

@dayfine

@dayfine dayfine commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

The documentation pages of the RNG programs document the number of free party slots needed. Programs might fail after catching / receiving when there is no free slot, as the pokemon got sent to box.

This PR actually checks for those requirements at program startup, by introducing a general pre-startup hook for programs.

Testing

On a Switch1 with a full party (when catching Mewtwo), Static RNG on this build stopped with that message 5 seconds after starting, before any of its own setup; before, it ran five resets (about 30 minutes) and stopped with the seed error.

The RNG programs' documentation lists how many free party slots each one
needs. Check that at program startup through a new pre-startup hook,
start_program_game_checks(), instead of failing after a catch or gift
goes to the box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@theastrogoth

Copy link
Copy Markdown
Contributor

I'm backtracking on the navigation behavior tests like you'd added in #1663 -- testing is usually done by running the program on a Switch to observe its behavior, and my impression is that getting the logic wrong for the navigation means you'll probably write tests that get it wrong, too, making them less than useful.

Without the need for the tests, the additions here can probably be reduced to only a few files:

  • no need to add the general pre-startup hook. Maybe the check can just be added before the main loop in each RNG program (after the black border check).
  • there shouldn't be any need to modify detect_last_occupied_party_slot
  • the check itself can be written to PokemonFRLG_RngNavigation.*, and it can accept the number free slots needed as an argument. When called by each program, it should just need one line.

Feel free to push back if I'm missing anything important.

dayfine and others added 3 commits November 11, 2026 17:28
…heck

Each case runs check_party_free_slots() with the real slot detector on a
party menu screenshot, replacing the scripted PartyCheck cases and the
separate LastOccupiedPartySlot test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
…tion#1896

Upstream removed scripted navigation behavior tests (PokemonAutomation#1896). Follow it:
take its Navigation and Tests files, drop PartyCheckActions, the image
overload of detect_last_occupied_party_slot and the party check tests,
and run the check directly in start_program_game_checks().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor Author

@theastrogoth

I'm backtracking on the navigation behavior tests like you'd added in #1663 -- testing is usually done by running the program on a Switch to observe its behavior, and my impression is that getting the logic wrong for the navigation means you'll probably write tests that get it wrong, too, making them less than useful.

  • there shouldn't be any need to modify detect_last_occupied_party_slot

Removed the tests here.

detect_last_occupied_party_slot could use some tests, and its overload enabled that. But I left them out to keep this change slim.

The tests here are indeed not the most interesting (as it's mostly just value-mapping), but there are potential cases where the tests could be more useful (when state is more complicated / to guard against corner cases).

Without the need for the tests, the additions here can probably be reduced to only a few files:

  • no need to add the general pre-startup hook. Maybe the check can just be added before the main loop in each RNG program (after the black border check).
  • the check itself can be written to PokemonFRLG_RngNavigation.*, and it can accept the number free slots needed as an argument. When called by each program, it should just need one line.

One thing I figured in most programs is that they do have some preconditions, but there is programmatically checks for them; then they just fail either silently or loudly but only after spending quite some times running up to the failure point. So I think a general pre-start program hook could be valuable / useful for other programs.

e.g. just picking a random program like: https://pokemonautomation.github.io/Programs/PokemonFRLG/PrizeCornerReset.html

Game Settings:
Text Speed: Fast
Button Mode: NOT L=A
Frame: Type 1

This is most common where we can check / correct the settings

Other Setup:
Have 5 Pokemon in your party.
Have enought coins to afford your target.

So this is similar in checking party free slot (though it's a reset program, not a RNG program). (but it can be tricky to abstract well what this really is -- like a ShinyHuntingProgram(Mixin))?

In that regard we don't necessary need a base class here in order to share, though that's relatively straightforward. I've done similar things at work just recently using template policies, but that's more involved and likely not necessary right now.

@theastrogoth

Copy link
Copy Markdown
Contributor

My comments are mostly motivated by 1) handling things similarly to other checks at the start of the program, like the black border check, for easier readability, and 2) my only really having "ownership" over FRLG code. I'm just an imposter who has stumbled into helping maintain the FRLG stuff, so I try to minimize changes to anything at a higher level.

If you'd like to go forward with the changes to NintendoSwitch_SingleSwitchProgram, I'd recommend splitting that into a separate PR or two to make it easier for someone smarter than me to review and to incorporate some other common checks when a program is started (like the border check).

dayfine and others added 2 commits November 11, 2026 18:29
…itchProgram hook

Drop the start_program_game_checks() hook from SingleSwitchProgramInstance.
Each RNG program calls check_free_party_slots() first in program().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

Sure. Will leave out NintendoSwitch_SingleSwitchProgram later when we have more examples to demonstrate its value.

I added a run_precheck locally now on the base class to run the common prechecks: party free slots and block border.

@dayfine

dayfine commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

@dayfine
dayfine force-pushed the upstream/frlg-free-party-slots branch 2 times, most recently from 2e779a7 to 5d1fc80 Compare November 12, 2026 05:12
@theastrogoth

theastrogoth commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Would you be willing to test out a few more of the programs with these changes to make sure the new check works as intended in each? Especially the Egg RNG and Wild RNG Safari Zone cases which needed tweaking.

@dayfine

dayfine commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

@theastrogoth yes let me run a couple. Haven't really ran Egg RNG and Wild RNG so far

dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
…ty slots check) into frlg/stage2-rng

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 13, 2026
@dayfine

dayfine commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

I ran 6 setup-only tests and 1 real RNG program run to hit

  • Gift RNG positive / negative (not enough free slots vs. enough free slots)
  • Wild RNG positive / negative
  • Egg ENG positive / negative (with pickup_calibration vs. held_prep)
  • 1 real safari Wild RNG, with 5000 advances target, non-shiny Parasect

Full report written by agent below:

# Case Setup Expect Result
1 Gift + party 4 border check → "Checking for 1 free party slot(s)..." → "Party has enough free slots." → continues PASS 20:59
2 Gift − party 6 (withdraw 2 at Cinnabar Center PC) UserSetupError "Your party is full..." before any reset PASS 21:23
3 Wild + party 4 same as 1, program continues PASS 21:17
4 Wild − party 6 UserSetupError "Your party is full..." PASS 21:23
5 Egg + party 4, STARTING_POINT pickup_calibration (needs 2) "Checking for 2 free party slot(s)..." → enough PASS 21:15
6 Egg − party 4, STARTING_POINT held_prep (needs 3) UserSetupError "Your party has 4 ... at least 3 party slots free" PASS 21:14
7 Wild real run (Safari) user OK 21:2x: save moved to Safari entrance logo (Fuchsia) after reset; Super Repel used; WildRng: safari_zone_entrance, grass, seed 70FE, Help/mono hit / miss reported, no crash PASS 22:34 (Target Hit, 70fe/5000, Lax Parasect Lv30)

Findings (for #1881 / WildRng; suggestions, not blockers)

# Finding Evidence Suggested change
F1 Grass/surf/Safari WildRng uses the last party slot's first field move as Sweet Scent; with any other move it fails late ("WildRng(): Failed to trigger battle") after a full reset. Case 7 try 3, 21:47:43; error-report video shows SUMMARY/SWITCH on Eevee run_precheck already opens the party: also check the last slot has Sweet Scent for those encounter types, and throw UserSetupError up front.
F2 Starting the program while the game is still on the previous run's post-catch summary screens fails the party check ("Failed to open party menu" x4), then the program stops, instead of resetting first. 22:23:41 start → 22:24:04–22:24:55 four "Failed to open party menu" Run the precheck after the first reset / from a known screen, or reset when the start menu cannot be opened.
F3 (minor) Catch fanfare not detected even on the successful catch ("Catch fanfare not detected. Error Coefficient = 1", 22:34:40); summary read still confirmed the catch. Case 7 hit Check the fanfare detector against Safari catches.
F4 (minor) Stats page HP read as "81281" (current/max run together). 22:34:50 Harmless for the hit check; worth a look in the stats reader.

Log lines

Setup note: lab-B's EggRng/WildRng/StaticRng/RoamingLegendaryRng panels had no controller/video bound; copied CFR's ConsoleSetup (21:12).

1 Gift +

2026-11-13 20:59:56.932329 - [Program]: <b>Starting Program: PokemonFRLG:GiftRng</b>
2026-11-13 20:59:56.935583 - [Console 0]: Going to home to check for black border.
2026-11-13 20:59:59.760779 - [Console 0]: Checking for 1 free party slot(s)...
2026-11-13 21:00:04.314803 - [Console 0]: Party has enough free slots.
2026-11-13 21:00:04.318526 - [Program]: Seed delay: 52641ms, button mode: Help, column: Mono / Help / A
2026-11-13 21:00:04.318845 - [Console 0]: Seed delay: 51858ms
2026-11-13 21:01:06.299778 - [Program]: Stopping program...
2026-11-13 21:03:54.007817 - [Console 0]: Stopping Camera...

2 Gift − (party 6: withdrew Magikarp + Rattata at Cinnabar Center, unsaved)

2026-11-13 21:23:11.707147 - [Program]: <b>Starting Program: PokemonFRLG:GiftRng</b>
2026-11-13 21:23:11.707974 - [Console 0]: Going to home to check for black border.
2026-11-13 21:23:14.576795 - [Console 0]: Checking for 1 free party slot(s)...
2026-11-13 21:23:18.237768 - [Console 0]: UserSetupError: Your party is full. Leave a party slot free so the program can check the Pokémon it catches or receives.

3 Wild +

2026-11-13 21:17:10.807621 - [Program]: <b>Starting Program: PokemonFRLG:WildRng</b>
2026-11-13 21:17:10.810614 - [Console 0]: Going to home to check for black border.
2026-11-13 21:17:13.580034 - [Console 0]: Checking for 1 free party slot(s)...
2026-11-13 21:17:18.337895 - [Console 0]: Party has enough free slots.
2026-11-13 21:17:18.347239 - [Program]: Seed delay: 31338ms, button mode: Help, column: Mono / Help / A
2026-11-13 21:17:18.349530 - [Console 0]: Seed delay: 30493ms
2026-11-13 21:17:21.890330 - [Program]: Stopping program...

4 Wild − (party 6)

2026-11-13 21:23:39.291624 - [Program]: <b>Starting Program: PokemonFRLG:WildRng</b>
2026-11-13 21:23:39.291966 - [Console 0]: Going to home to check for black border.
2026-11-13 21:23:42.159309 - [Console 0]: Checking for 1 free party slot(s)...
2026-11-13 21:23:45.866370 - [Console 0]: UserSetupError: Your party is full. Leave a party slot free so the program can check the Pokémon it catches or receives.

5 Egg + (pickup_frame)

2026-11-13 21:15:12.592849 - [Program]: <b>Starting Program: PokemonFRLG:EggRng</b>
2026-11-13 21:15:12.604936 - [Console 0]: Going to home to check for black border.
2026-11-13 21:15:15.426687 - [Console 0]: Checking for 2 free party slot(s)...
2026-11-13 21:15:19.284859 - [Console 0]: Party has enough free slots.
2026-11-13 21:15:19.298477 - [Console 0]: Seed delay: 30493ms
2026-11-13 21:15:19.298837 - [Console 0]: Seed delay: 30493ms
2026-11-13 21:15:22.639999 - [Program]: Stopping program...

6 Egg − (held_prep)

2026-11-13 21:14:44.487169 - [Program]: <b>Starting Program: PokemonFRLG:EggRng</b>
2026-11-13 21:14:44.488947 - [Console 0]: Going to home to check for black border.
2026-11-13 21:14:47.309199 - [Console 0]: Checking for 3 free party slot(s)...
2026-11-13 21:14:51.026578 - [Console 0]: UserSetupError: Your party has 4 Pokémon. Leave at least 3 party slots free for the Pokémon the program catches or receives.

7 Wild real run (Safari Zone center, grass)

Setup (21:24–21:32): soft reset (party back to 4) → Fly Fuchsia → walk to Safari entrance (route from pokesim route CLI) → stand on the Poké Ball logo → Super Repel (no Max Repel in bag; 5→4) → SAVE (overwrote the Cinnabar story save, user OK). Money ₽16,946, TID 15287.
Try 1, ADVANCES 3000 — refused before any reset (correct guard):

21:33:44 Checking for 1 free party slot(s)...
21:33:50 Party has enough free slots.
21:33:50 Target Species: rhyhorn, Level 25
21:33:50 OperationFailedExceptionWithScreenshot: Safari Zone Center: the in-game delay (20400ms) cannot be less than 30370ms. ... this target needs at least 4000 in-game advances

Try 2, ADVANCES 5000 — running:

21:34:24 Party has enough free slots.
21:34:24 Target Species: parasect, Level 30
21:34:24 Total time: 70892ms

After: restore the story save — reset, walk out, Fly Cinnabar, stand outside the lab door facing up, save.
Try 2 (21:34) FAILED on lab setup, not the program: after reset every launch went user-select → HOME ("Detected Home screen, but no black screen. Trying again from the beginning...") until "rng_start_game_and_return_home(): Failed to start game and return to Home within 5 minutes" (21:39:33). Cause: the lab app had been relaunched straight onto a panel, so FRLG Game Settings (HANDLE_ONLINE_LICENSE_CHECK=true) were never loaded (known lab issue). Fix: relaunched the lab app on the FRLG Game Settings panel (build aab36d1), relaunched the game to the logo.
Try 3 (21:46) — running:

21:46:02 Party has enough free slots.
21:46:02 Target Species: parasect

Try 3 (21:46) FAILED on my setup: launch with the license check worked (paid ₽500, walked into the center grass), then "WildRng(): Failed to trigger battle" (21:47:43). The error-report video shows the program opening the party and the LAST slot's first field move — Eevee, no Sweet Scent — landing in SUMMARY/SWITCH. The doc says grass needs a Sweet Scent user in the last slot; I missed it. Finding for #1881/WildRng: the precheck already opens the party, so it could also check the last slot has Sweet Scent (grass/surf) and fail with a setup message instead of "Failed to trigger battle" after a full reset.
Fix: soft reset to the logo save, swapped Oddish (Sweet Scent) into slot 4 (party now Blastoise, Eevee, Pidgey, Oddish), back on the logo facing up, saved again. Restore the story order (Oddish slot 2, Eevee slot 4) when putting the Cinnabar save back.
Try 4 (21:55) — running:

21:55:03 Party has enough free slots.
21:55:03 Target Species: parasect

Case 7 result: PASS (target hit), 2026-11-13 22:34

Run restarted by the user at 22:25 (Max Resets 40; calibration carried over from tries 4: seed cal 0.083 f, CSF 0.5, in-game 1.83).

22:25:58 Party has enough free slots.
22:28:12 Failed catch.            (reset 1)
22:30:03 Failed catch.            (reset 2)
22:34:49 String Match Result: "PARASECT" ... Lv "30" ... "LAx nature." -> (Lax)
22:34:50 Hits from previous 1 attempt(s):  70fe / 5000
22:34:50 Target Hit!
22:34:50 Program finished normally!

@dayfine

dayfine commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Various screenshots

Party full failure
截圖 2026-11-13 下午9 25 33

Hit Parasect
截圖 2026-11-13 下午10 35 55

dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 14, 2026
…s, hook done)

Fork-only for now for the app seams and the mGBA work, kept small enough to
upstream later. Preconditions are a list of named check objects declared with
the program (prechecks()/run_prechecks(), generalizing FRLG RNG's run_precheck
from upstream PokemonAutomation#1881); they land in Slice 4 and
the server first refuses runs on them in Slice 5. The require-profile
PreToolUse hook is done, so Enforcement describes it instead of planning it.
The open questions are listed with a recommended answer each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@theastrogoth

theastrogoth commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Since it seems like you might plan to expand the program start checks later on, I'd like an option to disable them. I'm torn on it being in FRLG Game Settings in a new "RNG Settings" section alongside the license check, or as a per-program settings checkbox. Common usage for me includes noticing a mistake in my entered settings by watching the "Target Details" box, and then restarting the program after fixing my settings, which could get frustrating if each restart needs to go through a bunch of checks.

Exhaustively checking all in-game setup would eventually need to look for Teachy TV in the top bag slot (when used), Rare Candy and Max Repel positions (and possibly quantities), Fishing Rod or Bike registration, Sweet Scent, in-game location (visible on the Save dialog), game version (from the title screen, maybe?), text speed, button mode, and audio settings as well -- I'm fine with not handling any of these, mostly because they involve OCR, which can be finicky and can be annoying to acquire test images for in each language.

Regarding Claude's findings:

  • F1: if pursued, detecting field moves needs dictionaries for each language, which were recently added to the FRLG resources, and is being worked on already by thewhitewolfking.
  • F2: this might be mitigated by a per-program toggle that can disable the new checks
  • F4: the stats reader has comments explaining this behavior and how it is handled

"Run Start Checks" in the FRLG Game Settings, in a new RNG Settings section with
the license check. Off skips the party check; the black border check always runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

Since it seems like you might plan to expand the program start checks later on

Not anytime soon :)

Added the option, defaulting to true, and tested with Egg RNG when having 4 party members with held_prep (requires 3 free slots) with the option on and off.

截圖 2026-11-14 下午1 00 27

dayfine added a commit to dayfine/Arduino-Source that referenced this pull request Nov 14, 2026
Moves the seven open questions to settled with their recommended answers,
writes the nine answers from the 2026-11-14 implementation review into the
resources, lease, tool and slice text, corrects the legacy Lab/state fields
and what 7c44e72 does, and records Slices 0-3 as built (#478, #481, #482,
#483) and Slice 4 as approved on PokemonAutomation#1881's branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 2 commits November 14, 2026 13:35
A Precheck has a stable name, a one-line "needs" and a policy (REQUIRED or
OPTIONAL). run_prechecks() runs a list in order, leaves out the OPTIONAL
ones when asked, returns one result per check (name, passed/failed/skipped,
message) and throws UserSetupError with the check's message at the first
failure.

FRLG RNG's run_precheck() now runs rng_prechecks(n): black_border
(REQUIRED), then free_party_slots(n) (OPTIONAL, only when n > 0). The
"Run Start Checks" box in the FRLG Game Settings skips the OPTIONAL checks,
as it skipped the party check before. Messages are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
…de health

OPTIONAL is a macro in the Windows headers, so the policy values are
ALWAYS_RUNS and SKIPPABLE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 2 commits November 14, 2026 15:54
@theastrogoth

Copy link
Copy Markdown
Contributor

This seems to have veered back into touching code that is shared with many unrelated programs. I'm still not convinced we need the extra layers of abstraction and shared classes to handle something that boils down to "do this first when the program starts". I'm also not sure that the new tests are needed or particularly helpful.

I also asked Claude to give this a look, in case I'm missing something, but it basically came up with a more longwinded version of my comment from a few days ago (which probably could have been more clear, apologies). Snippets from its response below, but TLDR we can avoid the new base classes and precheck framework for now:


The base class (RngProgramInstance)

  • All it adds is one pure virtual, free_party_slots_needed(), and a run_precheck() helper. Each program still has to
    call run_precheck() itself at the top of program(), so the base class doesn't enforce anything. A free function
    would do the same job.
  • Its public prechecks() method exists only so the tests can list the checks.
  • Each program now has to declare its slot count in its header as an override. That's harder to find than passing a
    number at the call site.

The precheck framework (NintendoSwitch_Prechecks.*)

  • It brings a lot of machinery for two checks: a Precheck class with subclasses, two status enums, and an overload
    that takes a std::function. run_prechecks() also returns a list of results, and nothing reads it.
  • It's new framework code in shared, non-FRLG space, which other contributors would then be expected to follow, and
    only FRLG uses it.

What I'd suggest instead

The useful part, checking for free party slots, could be one free function in the RNG folder, e.g. rng_start_checks(env, context, free_slots_needed). It would:

  1. if free_slots_needed > 0 and the setting is on, open the party menu, count the slots, and throw UserSetupError with the existing message;
  2. close the menu.

Each program would add rng_start_checks(env, context, 1) after its home_black_border_check(...) line. That's about 40 lines in total, with no base class, no framework and no new test files.


If you want to add more checks later on, you could pass rng_start_checks some kind of PokemonFRLG_RngPrechecks struct that has all of the necessary info, but that can wait until more checks have been implemented.

This reverts commit a7e19ea, reversing
changes made to eb27dfc.
@dayfine

dayfine commented Nov 15, 2026

Copy link
Copy Markdown
Contributor Author

@theastrogoth sorry that should not have landed here right now. Reverted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants