Core: Generic excluded fill (#1511)
This commit is contained in:
parent
6d13dc4944
commit
6671b21a86
41
Fill.py
41
Fill.py
|
@ -23,15 +23,27 @@ def sweep_from_pool(base_state: CollectionState, itempool: typing.Sequence[Item]
|
||||||
|
|
||||||
|
|
||||||
def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations: typing.List[Location],
|
def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations: typing.List[Location],
|
||||||
itempool: typing.List[Item], single_player_placement: bool = False, lock: bool = False,
|
item_pool: typing.List[Item], single_player_placement: bool = False, lock: bool = False,
|
||||||
swap: bool = True, on_place: typing.Optional[typing.Callable[[Location], None]] = None,
|
swap: bool = True, on_place: typing.Optional[typing.Callable[[Location], None]] = None,
|
||||||
allow_partial: bool = False) -> None:
|
allow_partial: bool = False, allow_excluded: bool = False) -> None:
|
||||||
|
"""
|
||||||
|
:param world: Multiworld to be filled.
|
||||||
|
:param base_state: State assumed before fill.
|
||||||
|
:param locations: Locations to be filled with item_pool
|
||||||
|
:param item_pool: Items to fill into the locations
|
||||||
|
:param single_player_placement: if true, can speed up placement if everything belongs to a single player
|
||||||
|
:param lock: locations are set to locked as they are filled
|
||||||
|
:param swap: if true, swaps of already place items are done in the event of a dead end
|
||||||
|
:param on_place: callback that is called when a placement happens
|
||||||
|
:param allow_partial: only place what is possible. Remaining items will be in the item_pool list.
|
||||||
|
:param allow_excluded: if true and placement fails, it is re-attempted while ignoring excluded on Locations
|
||||||
|
"""
|
||||||
unplaced_items: typing.List[Item] = []
|
unplaced_items: typing.List[Item] = []
|
||||||
placements: typing.List[Location] = []
|
placements: typing.List[Location] = []
|
||||||
|
|
||||||
swapped_items: typing.Counter[typing.Tuple[int, str]] = Counter()
|
swapped_items: typing.Counter[typing.Tuple[int, str]] = Counter()
|
||||||
reachable_items: typing.Dict[int, typing.Deque[Item]] = {}
|
reachable_items: typing.Dict[int, typing.Deque[Item]] = {}
|
||||||
for item in itempool:
|
for item in item_pool:
|
||||||
reachable_items.setdefault(item.player, deque()).append(item)
|
reachable_items.setdefault(item.player, deque()).append(item)
|
||||||
|
|
||||||
while any(reachable_items.values()) and locations:
|
while any(reachable_items.values()) and locations:
|
||||||
|
@ -39,9 +51,9 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
|
||||||
items_to_place = [items.pop()
|
items_to_place = [items.pop()
|
||||||
for items in reachable_items.values() if items]
|
for items in reachable_items.values() if items]
|
||||||
for item in items_to_place:
|
for item in items_to_place:
|
||||||
itempool.remove(item)
|
item_pool.remove(item)
|
||||||
maximum_exploration_state = sweep_from_pool(
|
maximum_exploration_state = sweep_from_pool(
|
||||||
base_state, itempool + unplaced_items)
|
base_state, item_pool + unplaced_items)
|
||||||
|
|
||||||
has_beaten_game = world.has_beaten_game(maximum_exploration_state)
|
has_beaten_game = world.has_beaten_game(maximum_exploration_state)
|
||||||
|
|
||||||
|
@ -111,7 +123,7 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
|
||||||
|
|
||||||
reachable_items[placed_item.player].appendleft(
|
reachable_items[placed_item.player].appendleft(
|
||||||
placed_item)
|
placed_item)
|
||||||
itempool.append(placed_item)
|
item_pool.append(placed_item)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -133,6 +145,21 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
|
||||||
if on_place:
|
if on_place:
|
||||||
on_place(spot_to_fill)
|
on_place(spot_to_fill)
|
||||||
|
|
||||||
|
if allow_excluded:
|
||||||
|
# check if partial fill is the result of excluded locations, in which case retry
|
||||||
|
excluded_locations = [
|
||||||
|
location for location in locations
|
||||||
|
if location.progress_type == location.progress_type.EXCLUDED and not location.item
|
||||||
|
]
|
||||||
|
if excluded_locations:
|
||||||
|
for location in excluded_locations:
|
||||||
|
location.progress_type = location.progress_type.DEFAULT
|
||||||
|
fill_restrictive(world, base_state, excluded_locations, unplaced_items, single_player_placement, lock,
|
||||||
|
swap, on_place, allow_partial, False)
|
||||||
|
for location in excluded_locations:
|
||||||
|
if not location.item:
|
||||||
|
location.progress_type = location.progress_type.EXCLUDED
|
||||||
|
|
||||||
if not allow_partial and len(unplaced_items) > 0 and len(locations) > 0:
|
if not allow_partial and len(unplaced_items) > 0 and len(locations) > 0:
|
||||||
# There are leftover unplaceable items and locations that won't accept them
|
# There are leftover unplaceable items and locations that won't accept them
|
||||||
if world.can_beat_game():
|
if world.can_beat_game():
|
||||||
|
@ -142,7 +169,7 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
|
||||||
raise FillError(f'No more spots to place {unplaced_items}, locations {locations} are invalid. '
|
raise FillError(f'No more spots to place {unplaced_items}, locations {locations} are invalid. '
|
||||||
f'Already placed {len(placements)}: {", ".join(str(place) for place in placements)}')
|
f'Already placed {len(placements)}: {", ".join(str(place) for place in placements)}')
|
||||||
|
|
||||||
itempool.extend(unplaced_items)
|
item_pool.extend(unplaced_items)
|
||||||
|
|
||||||
|
|
||||||
def remaining_fill(world: MultiWorld,
|
def remaining_fill(world: MultiWorld,
|
||||||
|
|
|
@ -7,6 +7,8 @@ from worlds.alttp.Items import ItemFactory
|
||||||
from worlds.alttp.Regions import lookup_boss_drops
|
from worlds.alttp.Regions import lookup_boss_drops
|
||||||
from worlds.alttp.Options import smallkey_shuffle
|
from worlds.alttp.Options import smallkey_shuffle
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from .SubClasses import ALttPLocation
|
||||||
|
|
||||||
def create_dungeons(world, player):
|
def create_dungeons(world, player):
|
||||||
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
||||||
|
@ -138,9 +140,10 @@ def fill_dungeons_restrictive(world):
|
||||||
if in_dungeon_items:
|
if in_dungeon_items:
|
||||||
restricted_players = {player for player, restricted in world.restrict_dungeon_item_on_boss.items() if
|
restricted_players = {player for player, restricted in world.restrict_dungeon_item_on_boss.items() if
|
||||||
restricted}
|
restricted}
|
||||||
locations = [location for location in get_unfilled_dungeon_locations(world)
|
locations: typing.List["ALttPLocation"] = [
|
||||||
# filter boss
|
location for location in get_unfilled_dungeon_locations(world)
|
||||||
if not (location.player in restricted_players and location.name in lookup_boss_drops)]
|
# filter boss
|
||||||
|
if not (location.player in restricted_players and location.name in lookup_boss_drops)]
|
||||||
if dungeon_specific:
|
if dungeon_specific:
|
||||||
for location in locations:
|
for location in locations:
|
||||||
dungeon = location.parent_region.dungeon
|
dungeon = location.parent_region.dungeon
|
||||||
|
@ -159,7 +162,7 @@ def fill_dungeons_restrictive(world):
|
||||||
(5 if (item.player, item.name) in dungeon_specific else 0))
|
(5 if (item.player, item.name) in dungeon_specific else 0))
|
||||||
for item in in_dungeon_items:
|
for item in in_dungeon_items:
|
||||||
all_state_base.remove(item)
|
all_state_base.remove(item)
|
||||||
fill_restrictive(world, all_state_base, locations, in_dungeon_items, True, True)
|
fill_restrictive(world, all_state_base, locations, in_dungeon_items, True, True, allow_excluded=True)
|
||||||
|
|
||||||
|
|
||||||
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
|
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
|
||||||
|
|
Loading…
Reference in New Issue