mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
Core: Cache previous swap states to use as the base state to sweep from (#3859)
The previous swap_state can often be used as the base state to create the next swap_state. This previous swap_state will already have collected all items in item_pool and is likely to have checked many locations, meaning that creating the next swap_state from it instead of from base_state is faster. From generating with extra code to raise an exception if more than 2 previous swap states were used, and using A Hat in Time and Pokemon Red/Blue yamls that often result in lots of swapping in progression fill, I could not get a single seed go through more than 2 previous swap states. A few worlds' pre-fills do often use more than 2 previous swap states, notably LADX which sometimes goes through over 20. Given a 20 player Pokemon Red/Blue multiworld that usually generates in around 16 or 17 seconds, but on a specific seed that results in 56 swaps, generation went from about 260 seconds before this patch to about 104 seconds after this patch (generated with a meta.yaml to disable progression balancing and `python -O Generate.py --skip_output`). Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
34
Fill.py
34
Fill.py
@@ -116,6 +116,13 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
|
||||
else:
|
||||
# we filled all reachable spots.
|
||||
if swap:
|
||||
# Keep a cache of previous safe swap states that might be usable to sweep from to produce the next
|
||||
# swap state, instead of sweeping from `base_state` each time.
|
||||
previous_safe_swap_state_cache: typing.Deque[CollectionState] = deque()
|
||||
# Almost never are more than 2 states needed. The rare cases that do are usually highly restrictive
|
||||
# single_player_placement=True pre-fills which can go through more than 10 states in some seeds.
|
||||
max_swap_base_state_cache_length = 3
|
||||
|
||||
# try swapping this item with previously placed items in a safe way then in an unsafe way
|
||||
swap_attempts = ((i, location, unsafe)
|
||||
for unsafe in (False, True)
|
||||
@@ -130,9 +137,30 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
|
||||
|
||||
location.item = None
|
||||
placed_item.location = None
|
||||
swap_state = sweep_from_pool(base_state, [placed_item, *item_pool] if unsafe else item_pool,
|
||||
multiworld.get_filled_locations(item.player)
|
||||
if single_player_placement else None)
|
||||
|
||||
for previous_safe_swap_state in previous_safe_swap_state_cache:
|
||||
# If a state has already checked the location of the swap, then it cannot be used.
|
||||
if location not in previous_safe_swap_state.advancements:
|
||||
# Previous swap states will have collected all items in `item_pool`, so the new
|
||||
# `swap_state` can skip having to collect them again.
|
||||
# Previous swap states will also have already checked many locations, making the sweep
|
||||
# faster.
|
||||
swap_state = sweep_from_pool(previous_safe_swap_state, (placed_item,) if unsafe else (),
|
||||
multiworld.get_filled_locations(item.player)
|
||||
if single_player_placement else None)
|
||||
break
|
||||
else:
|
||||
# No previous swap_state was usable as a base state to sweep from, so create a new one.
|
||||
swap_state = sweep_from_pool(base_state, [placed_item, *item_pool] if unsafe else item_pool,
|
||||
multiworld.get_filled_locations(item.player)
|
||||
if single_player_placement else None)
|
||||
# Unsafe states should not be added to the cache because they have collected `placed_item`.
|
||||
if not unsafe:
|
||||
if len(previous_safe_swap_state_cache) >= max_swap_base_state_cache_length:
|
||||
# Remove the oldest cached state.
|
||||
previous_safe_swap_state_cache.pop()
|
||||
# Add the new state to the start of the cache.
|
||||
previous_safe_swap_state_cache.appendleft(swap_state)
|
||||
# unsafe means swap_state assumes we can somehow collect placed_item before item_to_place
|
||||
# by continuing to swap, which is not guaranteed. This is unsafe because there is no mechanic
|
||||
# to clean that up later, so there is a chance generation fails.
|
||||
|
Reference in New Issue
Block a user