Add restrictive filling algorithm, which only restricts an item from being placed in a location if it would not be possible to beat the game if done.
This commit is contained in:
parent
f30495dfda
commit
2308a96884
|
@ -196,7 +196,7 @@ class World(object):
|
||||||
goal = ['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'].index(self.goal)
|
goal = ['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'].index(self.goal)
|
||||||
shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle)
|
shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle)
|
||||||
difficulty = ['normal', 'timed', 'timed-ohko', 'timed-countdown'].index(self.difficulty)
|
difficulty = ['normal', 'timed', 'timed-ohko', 'timed-countdown'].index(self.difficulty)
|
||||||
algorithm = ['freshness', 'flood', 'vt21', 'vt22'].index(self.algorithm)
|
algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'restrictive'].index(self.algorithm)
|
||||||
return logic | (mode << 1) | (dungeonitems << 2) | (goal << 3) | (shuffle << 6) | (difficulty << 10) | (algorithm << 12)
|
return logic | (mode << 1) | (dungeonitems << 2) | (goal << 3) | (shuffle << 6) | (difficulty << 10) | (algorithm << 12)
|
||||||
|
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ class CollectionState(object):
|
||||||
if to_remove is not None:
|
if to_remove is not None:
|
||||||
try:
|
try:
|
||||||
self.prog_items.remove(to_remove)
|
self.prog_items.remove(to_remove)
|
||||||
except IndexError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
|
|
||||||
# invalidate caches, nothing can be trusted anymore now
|
# invalidate caches, nothing can be trusted anymore now
|
||||||
|
|
61
Main.py
61
Main.py
|
@ -66,6 +66,8 @@ def main(args, seed=None):
|
||||||
distribute_items_cutoff(world, 0.66)
|
distribute_items_cutoff(world, 0.66)
|
||||||
elif args.algorithm == 'freshness':
|
elif args.algorithm == 'freshness':
|
||||||
distribute_items_staleness(world)
|
distribute_items_staleness(world)
|
||||||
|
elif args.algorithm == 'restrictive':
|
||||||
|
distribute_items_restrictive(world)
|
||||||
|
|
||||||
world.spoiler += print_location_spoiler(world)
|
world.spoiler += print_location_spoiler(world)
|
||||||
|
|
||||||
|
@ -247,6 +249,59 @@ 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 distribute_items_restrictive(world):
|
||||||
|
# get list of locations to fill in
|
||||||
|
fill_locations = world.get_unfilled_locations()
|
||||||
|
random.shuffle(fill_locations)
|
||||||
|
|
||||||
|
# get items to distribute
|
||||||
|
random.shuffle(world.itempool)
|
||||||
|
progitempool = [item for item in world.itempool if item.advancement]
|
||||||
|
prioitempool = [item for item in world.itempool if not item.advancement and item.priority]
|
||||||
|
restitempool = [item for item in world.itempool if not item.advancement and not item.priority]
|
||||||
|
|
||||||
|
def sweep_from_pool(excluded_item):
|
||||||
|
new_state = world.state.copy()
|
||||||
|
for item in progitempool:
|
||||||
|
new_state.collect(item, True)
|
||||||
|
new_state.remove(excluded_item)
|
||||||
|
new_state.sweep_for_events()
|
||||||
|
return new_state
|
||||||
|
|
||||||
|
while progitempool and fill_locations:
|
||||||
|
item_to_place = progitempool.pop()
|
||||||
|
maximum_exploration_state = sweep_from_pool(item_to_place)
|
||||||
|
|
||||||
|
spot_to_fill = None
|
||||||
|
for location in fill_locations:
|
||||||
|
if maximum_exploration_state.can_reach(location) and location.item_rule(item_to_place):
|
||||||
|
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():
|
||||||
|
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
|
||||||
|
|
||||||
|
while prioitempool and fill_locations:
|
||||||
|
spot_to_fill = fill_locations.pop()
|
||||||
|
item_to_place = prioitempool.pop()
|
||||||
|
world.push_item(spot_to_fill, item_to_place, False)
|
||||||
|
|
||||||
|
while restitempool and fill_locations:
|
||||||
|
spot_to_fill = fill_locations.pop()
|
||||||
|
item_to_place = restitempool.pop()
|
||||||
|
world.push_item(spot_to_fill, item_to_place, False)
|
||||||
|
|
||||||
|
logging.getLogger('').debug('Unplaced items: %s - Unfilled Locations: %s' % ([item.name for item in progitempool + prioitempool + restitempool], [location.name for location in fill_locations]))
|
||||||
|
|
||||||
|
|
||||||
def flood_items(world):
|
def flood_items(world):
|
||||||
# get items to distribute
|
# get items to distribute
|
||||||
random.shuffle(world.itempool)
|
random.shuffle(world.itempool)
|
||||||
|
@ -408,6 +463,7 @@ def copy_world(world):
|
||||||
ret.sewer_light_cone = world.sewer_light_cone
|
ret.sewer_light_cone = world.sewer_light_cone
|
||||||
ret.light_world_light_cone = world.light_world_light_cone
|
ret.light_world_light_cone = world.light_world_light_cone
|
||||||
ret.dark_world_light_cone = world.dark_world_light_cone
|
ret.dark_world_light_cone = world.dark_world_light_cone
|
||||||
|
ret.seed = world.seed
|
||||||
create_regions(ret)
|
create_regions(ret)
|
||||||
|
|
||||||
# connect copied world
|
# connect copied world
|
||||||
|
@ -524,11 +580,12 @@ if __name__ == '__main__':
|
||||||
'Timed starts with clock at zero. Green Clocks subtract 4 minutes (Total: 20), Blue Clocks subtract 2 minutes (Total: 10), Red Clocks add 2 minutes (Total: 10). Winner is player with lowest time at the end.\n'
|
'Timed starts with clock at zero. Green Clocks subtract 4 minutes (Total: 20), Blue Clocks subtract 2 minutes (Total: 10), Red Clocks add 2 minutes (Total: 10). Winner is player with lowest time at the end.\n'
|
||||||
'Timed OHKO starts clock at 10 minutes. Green Clocks add 5 minutes (Total: 25). As long as clock is at 0, Link will die in one hit.\n'
|
'Timed OHKO starts clock at 10 minutes. Green Clocks add 5 minutes (Total: 25). As long as clock is at 0, Link will die in one hit.\n'
|
||||||
'Timed Countdown starts with clock at 40 minutes. Same clocks as Timed mode. If time runs out, you lose (but can still keep playing).')
|
'Timed Countdown starts with clock at 40 minutes. Same clocks as Timed mode. If time runs out, you lose (but can still keep playing).')
|
||||||
parser.add_argument('--algorithm', default='freshness', const='freshness', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22'],
|
parser.add_argument('--algorithm', default='freshness', const='freshness', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'restrictive'],
|
||||||
help='Select item filling algorithm. vt21 is unbiased in its selection, but has tendency to put Ice Rod in Turtle Rock.\n'
|
help='Select item filling algorithm. vt21 is unbiased in its selection, but has tendency to put Ice Rod in Turtle Rock.\n'
|
||||||
'vt22 drops off stale locations after 1/3 of progress items were placed to try to circumvent vt21\'s shortcomings.\n'
|
'vt22 drops off stale locations after 1/3 of progress items were placed to try to circumvent vt21\'s shortcomings.\n'
|
||||||
'freshness keeps track of stale locations (ones that cannot be reached yet) and decreases likeliness of selecting them the more often they were found unreachable.\n'
|
'freshness keeps track of stale locations (ones that cannot be reached yet) and decreases likeliness of selecting them the more often they were found unreachable.\n'
|
||||||
'flood pushes out items starting from Link\'s House and is slightly biased to placing progression items with less restrictions.')
|
'flood pushes out items starting from Link\'s House and is slightly biased to placing progression items with less restrictions.\n'
|
||||||
|
'restrictive shuffles items and then places them in a random location that it is not impossible to be in.')
|
||||||
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
|
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
|
||||||
help='Select Entrance Shuffling Algorithm.\n'
|
help='Select Entrance Shuffling Algorithm.\n'
|
||||||
'Simple shuffles Dungeon Entrances/Exits between each other and keeps all 4-entrance dungeons confined to one location. All caves outside of death mountain are shuffled in pairs.\n'
|
'Simple shuffles Dungeon Entrances/Exits between each other and keeps all 4-entrance dungeons confined to one location. All caves outside of death mountain are shuffled in pairs.\n'
|
||||||
|
|
Loading…
Reference in New Issue