Retry Logic for Dungeon Prizes

Will help make avoid seed failure for custom pool seeds.

This won't help with a seed that has a layout that is not compatible
with the item pool though.
This commit is contained in:
Kevin Cathcart 2018-01-27 16:21:32 -05:00
parent 405e157da8
commit e19c0ada70
2 changed files with 34 additions and 9 deletions

14
Fill.py
View File

@ -1,6 +1,8 @@
import random
import logging
class FillError(RuntimeError):
pass
def distribute_items_cutoff(world, cutoffrate=0.33):
# get list of locations to fill in
@ -53,7 +55,7 @@ def distribute_items_cutoff(world, cutoffrate=0.33):
logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.')
progress_done = True
continue
raise RuntimeError('No more progress items left to place.')
raise FillError('No more progress items left to place.')
spot_to_fill = None
for location in fill_locations if placed_advancement_items / total_advancement_items < cutoffrate else reversed(fill_locations):
@ -66,7 +68,7 @@ def distribute_items_cutoff(world, cutoffrate=0.33):
if world.can_beat_game():
logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break
raise RuntimeError('No more spots to place %s' % item_to_place)
raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, True)
itempool.remove(item_to_place)
@ -121,7 +123,7 @@ def distribute_items_staleness(world):
logging.getLogger('').warning('Not all locations reachable. Game beatable anyway.')
progress_done = True
continue
raise RuntimeError('No more progress items left to place.')
raise FillError('No more progress items left to place.')
spot_to_fill = None
for location in fill_locations:
@ -147,7 +149,7 @@ def distribute_items_staleness(world):
if world.can_beat_game():
logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break
raise RuntimeError('No more spots to place %s' % item_to_place)
raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, True)
itempool.remove(item_to_place)
@ -185,7 +187,7 @@ def fill_restrictive(world, base_state, locations, itempool):
if not world.check_beatable_only:
logging.getLogger('').warning('Not all items placed. Game beatable anyway.')
break
raise RuntimeError('No more spots to place %s' % item_to_place)
raise FillError('No more spots to place %s' % item_to_place)
world.push_item(spot_to_fill, item_to_place, False)
locations.remove(spot_to_fill)
@ -281,7 +283,7 @@ def flood_items(world):
if candidate_item_to_place is not None:
item_to_place = candidate_item_to_place
else:
raise RuntimeError('No more progress items left to place.')
raise FillError('No more progress items left to place.')
# find item to replace with progress item
location_list = world.get_reachable_locations()

View File

@ -1,8 +1,9 @@
from collections import namedtuple
import logging
import random
from Items import ItemFactory
from Fill import fill_restrictive
from Fill import FillError, fill_restrictive
from Dungeons import get_dungeon_item_pool
#This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
@ -248,14 +249,36 @@ def generate_itempool(world):
world.required_medallions = (mm_medallion, tr_medallion)
# distribute crystals
fill_prizes(world)
def fill_prizes(world, attempts=15):
crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'])
crystal_locations = [world.get_location('Turtle Rock - Prize'), world.get_location('Eastern Palace - Prize'), world.get_location('Desert Palace - Prize'), world.get_location('Tower of Hera - Prize'), world.get_location('Palace of Darkness - Prize'),
world.get_location('Thieves Town - Prize'), world.get_location('Skull Woods - Prize'), world.get_location('Swamp Palace - Prize'), world.get_location('Ice Palace - Prize'),
world.get_location('Misery Mire - Prize')]
placed_prizes = [loc.item.name for loc in crystal_locations if loc.item is not None]
unplaced_prizes = [crystal for crystal in crystals if crystal.name not in placed_prizes]
empty_crystal_locations = [loc for loc in crystal_locations if loc.item is None]
while attempts:
attempts -= 1
try:
prizepool = list(unplaced_prizes)
prize_locs = list(empty_crystal_locations)
random.shuffle(prizepool)
random.shuffle(prize_locs)
fill_restrictive(world, world.get_all_state(keys=True), prize_locs, prizepool)
except FillError:
logging.getLogger('').info("Failed to place dungeon prizes. Will retry %s more times", attempts)
for location in empty_crystal_locations:
location.item = None
continue
break
else:
raise FillError('Unable to place dungeon prizes')
random.shuffle(crystal_locations)
fill_restrictive(world, world.get_all_state(keys=True), crystal_locations, crystals)
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode):
pool = []