From 69940374e13d27faae30c13fe6e6e0396b21000c Mon Sep 17 00:00:00 2001 From: BadMagic100 Date: Thu, 27 Feb 2025 08:12:35 -0800 Subject: [PATCH] Core: Only consider requested exits during ER placement and speculative sweep #4684 --- entrance_rando.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/entrance_rando.py b/entrance_rando.py index b6e64002..67ca6567 100644 --- a/entrance_rando.py +++ b/entrance_rando.py @@ -157,17 +157,16 @@ class ERPlacementState: def placed_regions(self) -> set[Region]: return self.collection_state.reachable_regions[self.world.player] - def find_placeable_exits(self, check_validity: bool) -> list[Entrance]: + def find_placeable_exits(self, check_validity: bool, usable_exits: list[Entrance]) -> list[Entrance]: if check_validity: blocked_connections = self.collection_state.blocked_connections[self.world.player] - blocked_connections = sorted(blocked_connections, key=lambda x: x.name) - placeable_randomized_exits = [connection for connection in blocked_connections - if not connection.connected_region - and connection.is_valid_source_transition(self)] + placeable_randomized_exits = [ex for ex in usable_exits + if not ex.connected_region + and ex in blocked_connections + and ex.is_valid_source_transition(self)] else: # this is on a beaten minimal attempt, so any exit anywhere is fair game - placeable_randomized_exits = [ex for region in self.world.multiworld.get_regions(self.world.player) - for ex in region.exits if not ex.connected_region] + placeable_randomized_exits = [ex for ex in usable_exits if not ex.connected_region] self.world.random.shuffle(placeable_randomized_exits) return placeable_randomized_exits @@ -181,7 +180,8 @@ class ERPlacementState: self.placements.append(source_exit) self.pairings.append((source_exit.name, target_entrance.name)) - def test_speculative_connection(self, source_exit: Entrance, target_entrance: Entrance) -> bool: + def test_speculative_connection(self, source_exit: Entrance, target_entrance: Entrance, + usable_exits: list[Entrance]) -> bool: copied_state = self.collection_state.copy() # simulated connection. A real connection is unsafe because the region graph is shallow-copied and would # propagate back to the real multiworld. @@ -198,6 +198,9 @@ class ERPlacementState: # ignore the source exit, and, if coupled, the reverse exit. They're not actually new if _exit.name == source_exit.name or (self.coupled and _exit.name == target_entrance.name): continue + # make sure we are only paying attention to usable exits + if _exit not in usable_exits: + continue # technically this should be is_valid_source_transition, but that may rely on side effects from # on_connect, which have not happened here (because we didn't do a real connection, and if we did, we would # not want them to persist). can_reach is a close enough approximation most of the time. @@ -339,7 +342,7 @@ def randomize_entrances( 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) + placeable_exits = er_state.find_placeable_exits(perform_validity_check, exits) for source_exit in placeable_exits: target_groups = target_group_lookup[source_exit.randomization_group] for target_entrance in entrance_lookup.get_targets(target_groups, dead_end, preserve_group_order): @@ -355,7 +358,7 @@ def randomize_entrances( and len(placeable_exits) == 1) if exit_requirement_satisfied and source_exit.can_connect_to(target_entrance, dead_end, er_state): if (needs_speculative_sweep - and not er_state.test_speculative_connection(source_exit, target_entrance)): + and not er_state.test_speculative_connection(source_exit, target_entrance, exits)): continue do_placement(source_exit, target_entrance) return True