diff --git a/BaseClasses.py b/BaseClasses.py index 71d728fd8c7b..5d6e9ade371f 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -634,7 +634,7 @@ def __init__(self, parent: MultiWorld): for item in items: self.collect(item, True) - def update_reachable_regions(self, player: int): + def update_reachable_regions(self, player: int, allow_partial_entrances: bool = False): self.stale[player] = False rrp = self.reachable_regions[player] bc = self.blocked_connections[player] @@ -654,7 +654,11 @@ def update_reachable_regions(self, player: int): if new_region in rrp: bc.remove(connection) elif connection.can_reach(self): - assert new_region, f"tried to search through an Entrance \"{connection}\" with no Region" + if not allow_partial_entrances: + assert new_region, f"tried to search through an Entrance \"{connection}\" with no Region" + else: + if not new_region: + break rrp.add(new_region) bc.remove(connection) bc.update(new_region.exits) @@ -793,8 +797,8 @@ def __init__(self, player: int, name: str = '', parent: Region = None, self.er_group = er_group self.er_type = er_type - def can_reach(self, state: CollectionState) -> bool: - if self.parent_region.can_reach(state) and self.access_rule(state): + def can_reach(self, state: CollectionState, allow_partial_entrances: bool = False) -> bool: + if self.parent_region.can_reach(state, allow_partial_entrances) and self.access_rule(state): if not self.hide_path and not self in state.path: state.path[self] = (self.name, state.path.get(self.parent_region, (self.parent_region.name, None))) return True @@ -924,9 +928,9 @@ def set_exits(self, new): exits = property(get_exits, set_exits) - def can_reach(self, state: CollectionState) -> bool: + def can_reach(self, state: CollectionState, allow_partial_entrances: bool = False) -> bool: if state.stale[self.player]: - state.update_reachable_regions(self.player) + state.update_reachable_regions(self.player, allow_partial_entrances) return self in state.reachable_regions[self.player] @property diff --git a/EntranceRando.py b/EntranceRando.py index a3386f9b63b8..611851af4b05 100644 --- a/EntranceRando.py +++ b/EntranceRando.py @@ -45,7 +45,6 @@ def _is_dead_end(entrance: Entrance): Checks whether a entrance is an unconditional dead end, that is, no matter what you have, it will never lead to new randomizable exits. """ - # obviously if this is an unpaired exit, then leads to unpaired exits! if not entrance.connected_region: return False @@ -120,11 +119,11 @@ def place(self, start: Union[Region, Entrance]) -> None: starting_entrance_name = None if isinstance(start, Entrance): starting_entrance_name = start.name - q.put(start.parent_region) + q.put(start.connected_region) else: q.put(start) - while q: + while not q.empty(): region = q.get() if region in self.placed_regions: continue @@ -145,8 +144,8 @@ def place(self, start: Union[Region, Entrance]) -> None: self._pending_exits.add(exit) elif exit.connected_region not in self.placed_regions: # traverse unseen static connections - if exit.can_reach(self.collection_state): - q.put(exit) + if exit.can_reach(self.collection_state, True): + q.put(exit.connected_region) else: self._pending_exits.add(exit) @@ -157,7 +156,7 @@ def sweep_pending_exits(self) -> None: """ no_longer_pending_exits = [] for exit in self._pending_exits: - if exit.connected_region and exit.can_reach(self.collection_state): + if exit.connected_region and exit.can_reach(self.collection_state, True): # this is an unrandomized entrance, so place it and propagate self.place(exit.connected_region) no_longer_pending_exits.append(exit) @@ -255,7 +254,7 @@ def randomize_entrances( # todo - this doesn't prioritize placing new rooms like the original did; # that's problematic because early loops would lead to failures # this is needed to reduce bias; otherwise newer exits are prioritized - rng.shuffle(state._placeable_exits) + # rng.shuffle(state._placeable_exits) source_exit = state._placeable_exits.pop() target_groups = get_target_groups(source_exit.er_group) @@ -286,6 +285,7 @@ def randomize_entrances( # none of the existing targets can pair to the existing sources. Since dead ends will never add new sources # this means the current targets can never be paired (in most cases) # todo - investigate ways to prevent this case + return state # this short circuts the exception for testing purposes in order to see how far ER got. raise Exception("Unable to place all non-dead-end entrances with available source exits") # anything we couldn't place before might be placeable now