Core: Generic ER fails in stage 1 when the last available target is an indirect conditioned dead end (#4679)

* Add test that stage1 ER will not fail due to speculative sweeping an indirect conditioned dead end

* Skip speculative sweep if it's the last entrance placement

* Better implementation of needs_speculative_sweep

* pep8
This commit is contained in:
BadMagic100
2025-03-15 10:56:07 -07:00
committed by GitHub
parent a9eb70a881
commit 785569c40c
2 changed files with 61 additions and 5 deletions

View File

@@ -358,6 +358,34 @@ def randomize_entrances(
if on_connect:
on_connect(er_state, placed_exits)
def needs_speculative_sweep(dead_end: bool, require_new_exits: bool, placeable_exits: list[Entrance]) -> bool:
# speculative sweep is expensive. We currently only do it as a last resort, if we might cap off the graph
# entirely
if len(placeable_exits) > 1:
return False
# in certain stages of randomization we either expect or don't care if the search space shrinks.
# we should never speculative sweep here.
if dead_end or not require_new_exits or not perform_validity_check:
return False
# edge case - if all dead ends have pre-placed progression or indirect connections, they are pulled forward
# into the non dead end stage. In this case, and only this case, it's possible that the last connection may
# actually be placeable in stage 1. We need to skip speculative sweep in this case because we expect the graph
# to get capped off.
# check to see if we are proposing the last placement
if not coupled:
# in uncoupled, this check is easy as there will only be one target.
is_last_placement = len(entrance_lookup) == 1
else:
# a bit harder, there may be 1 or 2 targets depending on if the exit to place is one way or two way.
# if it is two way, we can safely assume that one of the targets is the logical pair of the exit.
desired_target_count = 2 if placeable_exits[0].randomization_type == EntranceType.TWO_WAY else 1
is_last_placement = len(entrance_lookup) == desired_target_count
# if it's not the last placement, we need a sweep
return not is_last_placement
def find_pairing(dead_end: bool, require_new_exits: bool) -> bool:
nonlocal perform_validity_check
placeable_exits = er_state.find_placeable_exits(perform_validity_check, exits)
@@ -371,11 +399,9 @@ def randomize_entrances(
# very last exit and check whatever exits we open up are functionally accessible.
# this requirement can be ignored on a beaten minimal, islands are no issue there.
exit_requirement_satisfied = (not perform_validity_check or not require_new_exits
or target_entrance.connected_region not in er_state.placed_regions)
needs_speculative_sweep = (not dead_end and require_new_exits and perform_validity_check
and len(placeable_exits) == 1)
or target_entrance.connected_region not in er_state.placed_regions)
if exit_requirement_satisfied and source_exit.can_connect_to(target_entrance, dead_end, er_state):
if (needs_speculative_sweep
if (needs_speculative_sweep(dead_end, require_new_exits, placeable_exits)
and not er_state.test_speculative_connection(source_exit, target_entrance, exits_set)):
continue
do_placement(source_exit, target_entrance)