From 1bcff2c92ac9ae11e6ba50f5a5e8e48c56fc7bb5 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 7 Jul 2026 00:14:35 -0500 Subject: [PATCH 1/3] added guard to not waste time on generations that can't be made beatable --- Fill.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Fill.py b/Fill.py index 7bd575662708..c9efc811241f 100644 --- a/Fill.py +++ b/Fill.py @@ -257,7 +257,7 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati def remaining_fill(multiworld: MultiWorld, locations: typing.List[Location], itempool: typing.List[Item], - name: str = "Remaining", + name: str = "Remaining", move_unplaceable_to_start_inventory: bool = False, check_location_can_fill: bool = False) -> None: unplaced_items: typing.List[Item] = [] @@ -494,6 +494,18 @@ def distribute_items_restrictive(multiworld: MultiWorld, f"{[(item.location, item) for item in multiworld.itempool if item.location is not None]}" ) + # cheap quick guard against unreachable goals to avoid wasting fill and progression balancing time + # collect all items and sweep pre_fill advancements because fill doesn't catch when they're unreachable + guard_state = multiworld.state.copy() + for item in multiworld.itempool: + if item.advancement: + guard_state.collect(item, True) + pre_fill_advancements = [location for location in multiworld.get_locations() if location.item is not None and location.item.advancement] + guard_state.sweep_for_advancements(locations=pre_fill_advancements) + unreachable_goals = [player for player in multiworld.player_ids if not multiworld.has_beaten_game(guard_state, player)] + if unreachable_goals: + raise FillError(f"Cannot reach goal for players with all advancements collected: {unreachable_goals}") + fill_locations = sorted(multiworld.get_unfilled_locations()) multiworld.random.shuffle(fill_locations) # get items to distribute @@ -885,7 +897,7 @@ def item_percentage(player: int, num: int) -> float: items_to_replace.sort() multiworld.random.shuffle(items_to_replace) - # Start swapping items. Since we swap into earlier spheres, no need for accessibility checks. + # Start swapping items. Since we swap into earlier spheres, no need for accessibility checks. while replacement_locations and items_to_replace: old_location = items_to_replace.pop() for i, new_location in enumerate(replacement_locations): From c0e18521d52cc02d9d5b8be4a96711261396f701 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 8 Jul 2026 01:45:26 -0500 Subject: [PATCH 2/3] style --- Fill.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Fill.py b/Fill.py index c9efc811241f..5bf57a506222 100644 --- a/Fill.py +++ b/Fill.py @@ -500,9 +500,15 @@ def distribute_items_restrictive(multiworld: MultiWorld, for item in multiworld.itempool: if item.advancement: guard_state.collect(item, True) - pre_fill_advancements = [location for location in multiworld.get_locations() if location.item is not None and location.item.advancement] + pre_fill_advancements = [ + location for location in multiworld.get_locations() + if location.item is not None and location.item.advancement + ] guard_state.sweep_for_advancements(locations=pre_fill_advancements) - unreachable_goals = [player for player in multiworld.player_ids if not multiworld.has_beaten_game(guard_state, player)] + unreachable_goals = [ + player for player in multiworld.player_ids + if not multiworld.has_beaten_game(guard_state, player) + ] if unreachable_goals: raise FillError(f"Cannot reach goal for players with all advancements collected: {unreachable_goals}") From da6e3d8048b7c40cc6730e3aa11a148de41d853e Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 30 Jul 2026 23:16:23 -0500 Subject: [PATCH 3/3] demoted guard to warning and added special handling for sm maxDiff --- Fill.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Fill.py b/Fill.py index 5bf57a506222..0d2bd17024f4 100644 --- a/Fill.py +++ b/Fill.py @@ -497,6 +497,14 @@ def distribute_items_restrictive(multiworld: MultiWorld, # cheap quick guard against unreachable goals to avoid wasting fill and progression balancing time # collect all items and sweep pre_fill advancements because fill doesn't catch when they're unreachable guard_state = multiworld.state.copy() + # super metroid can fail this check because of maxdiff, which it corrects in post fill, so we set maxdiff to max in + # our state to prevent a false warning. + smbm = getattr(guard_state, "smbm", None) + if smbm is not None: + for p in multiworld.player_ids: + if multiworld.game[p] == "Super Metroid" and p in smbm: + smbm[p].maxDiff = 8675309 + for item in multiworld.itempool: if item.advancement: guard_state.collect(item, True) @@ -506,11 +514,11 @@ def distribute_items_restrictive(multiworld: MultiWorld, ] guard_state.sweep_for_advancements(locations=pre_fill_advancements) unreachable_goals = [ - player for player in multiworld.player_ids + multiworld.player_name[player] for player in multiworld.player_ids if not multiworld.has_beaten_game(guard_state, player) ] if unreachable_goals: - raise FillError(f"Cannot reach goal for players with all advancements collected: {unreachable_goals}") + logging.warning(f"Prefill check failed for the following players. Generation may fail: {unreachable_goals}") fill_locations = sorted(multiworld.get_unfilled_locations()) multiworld.random.shuffle(fill_locations)