Improve crystal placement algorithm.

This commit is contained in:
LLCoolDave 2017-08-05 17:52:18 +02:00
parent 271571d01e
commit 1d8f17de6f
2 changed files with 67 additions and 86 deletions

View File

@ -87,7 +87,7 @@ class World(object):
return r_location return r_location
raise RuntimeError('No such location %s' % location) raise RuntimeError('No such location %s' % location)
def get_all_state(self): def get_all_state(self, keys=False):
ret = CollectionState(self) ret = CollectionState(self)
def soft_collect(item): def soft_collect(item):
@ -111,11 +111,17 @@ class World(object):
else: else:
ret.prog_items.append('Power Glove') ret.prog_items.append('Power Glove')
elif item.advancement: elif item.advancement or item.key:
ret.prog_items.append(item.name) ret.prog_items.append(item.name)
for item in self.itempool: for item in self.itempool:
soft_collect(item) soft_collect(item)
if keys:
from Items import ItemFactory
for item in ItemFactory(['Small Key (Escape)', 'Big Key (Eastern Palace)', 'Big Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Tower of Hera)', 'Small Key (Tower of Hera)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)',
'Big Key (Palace of Darkness)'] + ['Small Key (Palace of Darkness)'] * 6 + ['Big Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Skull Woods)'] + ['Small Key (Skull Woods)'] * 3 + ['Big Key (Swamp Palace)',
'Small Key (Swamp Palace)', 'Big Key (Ice Palace)'] + ['Small Key (Ice Palace)'] * 2 + ['Big Key (Misery Mire)', 'Big Key (Turtle Rock)', 'Big Key (Ganons Tower)'] + ['Small Key (Misery Mire)'] * 3 + ['Small Key (Turtle Rock)'] * 4 + ['Small Key (Ganons Tower)'] * 4):
soft_collect(item)
ret.sweep_for_events() ret.sweep_for_events()
ret._clear_cache() ret._clear_cache()
return ret return ret

143
Main.py
View File

@ -45,14 +45,14 @@ def main(args, seed=None):
link_entrances(world) link_entrances(world)
logger.info('Generating Item Pool.')
generate_itempool(world)
logger.info('Calculating Access Rules.') logger.info('Calculating Access Rules.')
set_rules(world) set_rules(world)
logger.info('Generating Item Pool.')
generate_itempool(world)
logger.info('Placing Dungeon Items.') logger.info('Placing Dungeon Items.')
fill_dungeons(world) fill_dungeons(world)
@ -257,6 +257,50 @@ def distribute_items_staleness(world):
logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s' % ([item.name for item in itempool], [location.name for location in fill_locations])) logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s' % ([item.name for item in itempool], [location.name for location in fill_locations]))
def fill_restrictive(world, base_state, locations, itempool):
def sweep_from_pool():
new_state = base_state.copy()
for item in itempool:
new_state.collect(item, True)
new_state.sweep_for_events()
return new_state
while itempool and locations:
item_to_place = itempool.pop()
maximum_exploration_state = sweep_from_pool()
spot_to_fill = None
for location in locations:
if location.item_rule(item_to_place):
if world.check_beatable_only:
starting_state = base_state.copy()
for item in itempool:
starting_state.collect(item, True)
if maximum_exploration_state.can_reach(location):
if world.check_beatable_only:
starting_state.collect(item_to_place, True)
else:
spot_to_fill = location
break
if world.check_beatable_only and world.can_beat_game(starting_state):
spot_to_fill = location
break
if spot_to_fill is None:
# we filled all reachable spots. Maybe the game can be beaten anyway?
if world.can_beat_game():
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)
world.push_item(spot_to_fill, item_to_place, False)
locations.remove(spot_to_fill)
spot_to_fill.event = True
def distribute_items_restrictive(world, gftower_trash_count=0): def distribute_items_restrictive(world, gftower_trash_count=0):
# get list of locations to fill in # get list of locations to fill in
fill_locations = world.get_unfilled_locations() fill_locations = world.get_unfilled_locations()
@ -280,47 +324,7 @@ def distribute_items_restrictive(world, gftower_trash_count=0):
random.shuffle(fill_locations) random.shuffle(fill_locations)
def sweep_from_pool(): fill_restrictive(world, world.state, fill_locations, progitempool)
new_state = world.state.copy()
for item in progitempool:
new_state.collect(item, True)
new_state.sweep_for_events()
return new_state
while progitempool and fill_locations:
item_to_place = progitempool.pop()
maximum_exploration_state = sweep_from_pool()
spot_to_fill = None
for location in fill_locations:
if location.item_rule(item_to_place):
if world.check_beatable_only:
starting_state = world.state.copy()
for item in progitempool:
starting_state.collect(item, True)
if maximum_exploration_state.can_reach(location):
if world.check_beatable_only:
starting_state.collect(item_to_place, True)
else:
spot_to_fill = location
break
if world.check_beatable_only and world.can_beat_game(starting_state):
spot_to_fill = location
break
if spot_to_fill is None:
# we filled all reachable spots. Maybe the game can be beaten anyway?
if world.can_beat_game():
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)
world.push_item(spot_to_fill, item_to_place, False)
fill_locations.remove(spot_to_fill)
spot_to_fill.event = True
random.shuffle(fill_locations) random.shuffle(fill_locations)
@ -473,50 +477,21 @@ def generate_itempool(world):
else: else:
world.itempool.append(ItemFactory('Magic Upgrade (1/2)')) world.itempool.append(ItemFactory('Magic Upgrade (1/2)'))
# distribute crystals
crystals = ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant'])
crystal_locations = [world.get_location('Trinexx - Crystal')]
random.shuffle(crystals)
if world.shuffle_ganon:
# ensure that no crystal gets locked inside of ganons tower location as that is unsolvable
for region, crystallocation in [('Eastern Palace', 'Armos - Pendant'), ('Desert Palace North', 'Lanmolas - Pendant'), ('Tower of Hera (Bottom)', 'Moldorm - Pendant'),
('Dark Palace (Entrance)', 'Helmasaur - Crystal'), ('Thieves Town (Entrance)', 'Blind - Crystal'), ('Skull Woods Final Section (Entrance)', 'Mothula - Crystal'),
('Swamp Palace (Entrance)', 'Arrghus - Crystal'), ('Ice Palace (Entrance)', 'Kholdstare - Crystal'), ('Misery Mire (Entrance)', 'Vitreous - Crystal')]:
if world.get_entrance('Ganons Tower').connected_region.name == region:
# can't place a crystal here
world.push_item(world.get_location(crystallocation), crystals.pop(), False)
world.get_location(crystallocation).event = True
else:
crystal_locations.append(world.get_location(crystallocation))
else:
crystal_locations += [world.get_location('Armos - Pendant'), world.get_location('Lanmolas - Pendant'), world.get_location('Moldorm - Pendant'), world.get_location('Helmasaur - Crystal'),
world.get_location('Blind - Crystal'), world.get_location('Mothula - Crystal'), world.get_location('Arrghus - Crystal'), world.get_location('Kholdstare - Crystal'),
world.get_location('Vitreous - Crystal')]
crystals.extend(ItemFactory(['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7']))
random.shuffle(crystals)
# check if dam is behind pyramid fairy, if so, swamp can't hold a crystal
if world.get_entrance('Pyramid Fairy').connected_region.name == 'Dam':
try:
crystallocation = crystal_locations.pop(crystal_locations.index(world.get_location('Arrghus - Crystal')))
world.push_item(world.get_location(crystallocation), crystals.pop(), False)
crystallocation.event = True
except ValueError:
pass
crystals.extend(ItemFactory(['Crystal 5', 'Crystal 6']))
random.shuffle(crystals)
for location, crystal in zip(crystal_locations, crystals):
world.push_item(location, crystal, False)
location.event = True
# shuffle medallions # shuffle medallions
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
world.required_medallions = (mm_medallion, tr_medallion) world.required_medallions = (mm_medallion, tr_medallion)
# distribute crystals
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('Trinexx - Crystal'), world.get_location('Armos - Pendant'), world.get_location('Lanmolas - Pendant'), world.get_location('Moldorm - Pendant'), world.get_location('Helmasaur - Crystal'),
world.get_location('Blind - Crystal'), world.get_location('Mothula - Crystal'), world.get_location('Arrghus - Crystal'), world.get_location('Kholdstare - Crystal'),
world.get_location('Vitreous - Crystal')]
random.shuffle(crystal_locations)
fill_restrictive(world, world.get_all_state(keys=True), crystal_locations, crystals)
def copy_world(world): def copy_world(world):
# ToDo: Not good yet # ToDo: Not good yet