Skip to content

Improve mixed ER generation with multiple pools#33

Merged
fenhl merged 6 commits intofenhl:dev-fenhlfrom
tanjo3:fenhl-improve-mixed-pools-shuffling
Feb 27, 2026
Merged

Improve mixed ER generation with multiple pools#33
fenhl merged 6 commits intofenhl:dev-fenhlfrom
tanjo3:fenhl-improve-mixed-pools-shuffling

Conversation

@tanjo3
Copy link
Copy Markdown

@tanjo3 tanjo3 commented Feb 23, 2026

When multiple entrance pools are shuffled together with mixed pools, seed generation frequently fails. The root cause is assume_entrance_pool(). When all pools are assumed simultaneously, assume_entrance_pool() creates assumed exits on the Root region for every entrance across all pools. During per-entrance validation, Search explores from Root and sees all these assumed exits, leading it to consider every region reachable, regardless of its actual placement. This means bad placements get accepted, and the seed only fails later during the final fill, triggering a full retry.

The fix in this PR is to un-assume all entrance pools, then assume and shuffle one pool at a time, so Search only sees the assumed exits for the current pool. By skipping the per-pool validate_world() and confirm_replacement() calls, we keep placements tentative. Only after all pools have been shuffled is this validation performed. If there's a failure, the shuffle is rolled back, entrance pools are un-assumed, and the process is repeated.

Additionally, currently, boss room savewarps and blue warps when set to "Dungeon Entrance" remain connected to their vanilla targets during shuffling, providing false reachability paths. This PR also disconnects these connections before entrance shuffling, improving the generation success rate.

Finally, this PR fixes a couple of bugs that caused issues with multiworld generation. The world variable was shadowed in a few places, leading to references to the wrong world. Similarly, the placed_one_way_entrances variable was overwritten for each world, causing the final validation check across all worlds to use only the entrances of the last world shuffled.

Testing

I looked into this because we wanted to run a multiworld seed with some crazier entrance randomization, but couldn't get a seed to generate successfully. We used the MW S5 preset as a base, then added Full Interior and Boss entrance shuffle to a single pool, and Dungeon + GC entrance to a separate pool. The following tests are run on v9.0.14-3. I've also added before/after results on the Mixed Pools S4 preset and this preset, but as a 3-player multiworld. Each test uses 20 seeds, with a max of 10 retries per seed.

MW S5 Variant: WACSPSMAWCBDDSVLDNNACUXJ22A6AJACAAASABWEFAAEYGASHFSFAAJA2ETSAAFAEEALFSVCRLAASAJADSB4SHAE2BRCEH7KYCADJBLUK7NBAAAAAAANS8WBTPAHDACEGJNQYA6B43BCALEJGUBB

Success rate Avg retries/seed Avg time/seed
Before 0/20 10 106.8s
2c40c7b 20/20 1 24.2s
9795d15 20/20 1 28.3s

Mixed Pools S4: EACSQSKAWCBDDSSJENNACWXJ22A6AWAEAAASABX9LXLMDA2DCAGXMAASASKCBBALAJJAWKBHF8WAAABSAGADWBRAJSVA3KRUXA2ALSUNLHMAAAAAAAADWHFJED2ZASSBCDARCSDAEAWJSNEDC

Success rate Avg retries/seed Avg time/seed
Before 4/20 8.7 85.8s
2c40c7b 17/20 5 82.4s
9795d15 20/20 1.2 18.1s

Mixed Pools S4 MW: WACSQSKAWCBDDSSJENNACWXJ22A6AWAEAAASABX9LXLMDA2DCAGXMAASASKCBBALAJJAWKBHF8WAAABSAGADWBRAJSVA3KRUXA2ALSUNLHMAAAAAAAADWHFJED2ZASSBCDARCSDAEAWJSNEDC

Success rate Avg retries/seed Avg time/seed
Before 0/20 10 273.8s
2c40c7b 1/20 9.7 366.3s
9795d15 19/20 3.4 388.5s

@tanjo3
Copy link
Copy Markdown
Author

tanjo3 commented Feb 23, 2026

I ran another test on the second preset (Mixed Pools S4), where the shuffle would also go through the shuffle_pools_mixed() code path rather than shuffle_pools_sequentially(). I had 18/20 seeds succeed, with an average retry rate of 3.9 and an average time of 60.6s (though, to be fair, when creating the tables, I had 3 tests running at once). So, it seems like you could also replace the current shuffle algorithm (shuffle_pools_sequentially()) with shuffle_pools_mixed() in all cases. I left them as two cases in case it interferes with the current algorithm.

@fenhl
Copy link
Copy Markdown
Owner

fenhl commented Feb 24, 2026

This looks like a huge improvement, thank you for looking into this!

Additionally, currently, boss room savewarps remain connected to their vanilla targets during shuffling, providing false reachability paths. This PR also disconnects boss room savewarps before entrance shuffling, improving generation success rate.

Good catch, but looking at your changes, I'm not seeing where they're reconnected after shuffling. Is that done implicitly somewhere?

So, it seems like you could also replace the current shuffle algorithm (shuffle_pools_sequentially()) with shuffle_pools_mixed() in all cases.

The numbers are close enough that I'll want to confirm with a larger sample size, I can set up an ootrstats run for that. If it ends up being an improvement, it should definitely be used for all cases to simplify the code.

@fenhl
Copy link
Copy Markdown
Owner

fenhl commented Feb 24, 2026

Good catch, but looking at your changes, I'm not seeing where they're reconnected after shuffling. Is that done implicitly somewhere?

Looking at the existing code again, savewarps are reconnected but only if the source of the savewarp is the target of a shuffled entrance. So the remaining savewarps will have to be reconnected as well (probably easiest to just add a pass to reconnect them all vanilla before the existing code that updates them).

@tanjo3
Copy link
Copy Markdown
Author

tanjo3 commented Feb 24, 2026

Thanks for catching that with the boss room savewarps. I've added a reconnect pass as you suggested. I also found that blue warps, when shuffled, have the same issue, so I've made a change to disconnect (they get reconnected later when blue warp targets are determined). There were also a couple of bugs related to multiworld generation. With these changes, I got 20/20 successes on the first two settings, and 19/20 on the third. Since you have the tooling for a bigger test, I'd definitely recommend it, since my focus was really on just these three settings. I've update the PR with the new changes and results.

@fenhl
Copy link
Copy Markdown
Owner

fenhl commented Feb 26, 2026

While attempting to collect more data, I found a seed that takes much longer than the rest to generate (possible infinite loop?): commit f4ac36d, with the Mixed Pools S4 settings string (EACSQSKAWCBDDSSJENNACWXJ22A6AWAEAAASABX9LXLMDA2DCAGXMAASASKCBBALAJJAWKBHF8WAAABSAGADWBRAJSVA3KRUXA2ALSUNLHMAAAAAAAADWHFJED2ZASSBCDARCSDAEAWJSNEDC) and seed ootrstats459. I haven't checked if this still happens on commit 10d24f8.

@fenhl
Copy link
Copy Markdown
Owner

fenhl commented Feb 26, 2026

Sorry, forgot to say, the following settings.json needs to be used in addition to the settings string to repro:

{
    "check_version": true,
    "create_spoiler": true,
    "create_cosmetics_log": true,
    "create_patch_file": true,
    "create_uncompressed_rom": true,
    "create_compressed_rom": false,
    "salt_seed": false
}

It seems to be getting stuck in rom patching.

@fenhl
Copy link
Copy Markdown
Owner

fenhl commented Feb 26, 2026

This turned out to be a bug in World.bigocto_location and I've pushed a fix to dev-fenhl. Could you rebase this PR please?

@tanjo3 tanjo3 force-pushed the fenhl-improve-mixed-pools-shuffling branch from 10d24f8 to 9795d15 Compare February 26, 2026 06:52
@fenhl
Copy link
Copy Markdown
Owner

fenhl commented Feb 27, 2026

Here's my results. Metric is average CPU instructions until success (lower is better) with a sample size of 512.

MW S5 Variant Mixed Pools S4 Mixed Pools S4 MW
baseline 3.364e14 5.207e12
second fix 5.276e11 5.452e11 7.562e12
second fix, unconditional 5.276e11 5.599e11 7.716e12
detailed stats

Formula used to calculate the metric:

success_rate = num_successes / (num_successes + num_failures)
average_instructions_success = total_instructions_success / num_successes
average_instructions_failure = total_instructions_failure / num_failures
average_failure_count = (1 - success_rate) / success_rate
average_instructions = average_failure_count * average_instructions_failure + average_instructions_success

MW S5 Variant

baseline (ff2602f)

success rate: 2/512 (0.39%)
average instructions (success): 937711020943 (9.377e11)
average instructions (failure): 1315560958923 (1.316e12)
average total instructions until success: 336405755546308 (3.364e14)

second fix (9795d15)

success rate: 512/512 (100.00%)
average instructions (success): 527633133198 (5.276e11)
average instructions (failure): N/A
average total instructions until success: 527633133198 (5.276e11)

second fix, unconditional (30c65d8)

success rate: 512/512 (100.00%)
average instructions (success): 527601101580 (5.276e11)
average instructions (failure): N/A
average total instructions until success: 527601101580 (5.276e11)

Mixed Pools S4

baseline (ff2602f)

success rate: 135/512 (26.37%)
average instructions (success): 872710367137 (8.727e11)
average instructions (failure): 1551910735386 (1.552e12)
average total instructions until success: 5206564791140.867 (5.207e12)

second fix (9795d15)

success rate: 512/512 (100.00%)
average instructions (success): 545240069877 (5.452e11)
average instructions (failure): N/A
average total instructions until success: 545240069877 (5.452e11)

second fix, unconditional (30c65d8)

success rate: 512/512 (100.00%)
average instructions (success): 559861107616 (5.599e11)
average instructions (failure): N/A
average total instructions until success: 559861107616 (5.599e11)

Mixed Pools S4 MW

baseline (ff2602f)

No successful seeds, so average instruction count is infinite

second fix (9795d15)

success rate: 489/512 (95.51%)
average instructions (success): 6672287028061 (6.672e12)
average instructions (failure): 18923166423580 (1.892e13)
average total instructions until success: 7562333710560.673 (7.562e12)

second fix, unconditional (30c65d8)

success rate: 492/512 (96.09%)
average instructions (success): 6939395092006 (6.939e12)
average instructions (failure): 19105555927277 (1.911e13)
average total instructions until success: 7716043706935.959 (7.716e12)

Based on these numbers, the sequential code path seems to be about 2% faster than the mixed code path for the settings where it's used. I'd say that makes the sequential code path worth keeping.

@fenhl fenhl merged commit 492aa73 into fenhl:dev-fenhl Feb 27, 2026
3 checks passed
@tanjo3 tanjo3 deleted the fenhl-improve-mixed-pools-shuffling branch February 27, 2026 16:32
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