Add cross world bunny logic

This commit is contained in:
Kevin Cathcart 2018-01-27 17:17:03 -05:00
parent a0237e0863
commit f39886579a
4 changed files with 68 additions and 17 deletions

View File

@ -562,6 +562,7 @@ class Region(object):
self.dungeon = None
self.world = None
self.is_light_world = False # will be set aftermaking connections.
self.is_dark_world = False
self.spot_type = 'Region'
self.hint_text = 'Hyrule'
self.recursion_count = 0

View File

@ -154,6 +154,7 @@ def copy_world(world):
for region in world.regions:
copied_region = ret.get_region(region.name)
copied_region.is_light_world = region.is_light_world
copied_region.is_dark_world = region.is_dark_world
for entrance in region.entrances:
ret.get_entrance(entrance.name).connect(copied_region)

View File

@ -298,22 +298,30 @@ def _create_region(name, type, locations=None, exits=None):
ret.locations.append(Location(location, address, crystal, hint_text, ret))
return ret
def mark_light_world_regions(world):
# Note that in "inanity" shuffle this code may mark some dark world locations as being in light world. That is fine because this flag
# is only used for bunny logic, and you start with a Moon pearl immediately availible in Insanity shuffle.
# Exclude entrances that represent connections from the light world to the dark world
excluded_entrances = set(['Top of Pyramid', 'Lake Hylia Central Island Teleporter', 'Dark Desert Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'Kakariko Teleporter', 'Death Mountain Teleporter', 'East Death Mountain Teleporter', 'Turtle Rock Teleporter'])
starting_regions = ['Links House', 'Cave 45 Ledge', 'Graveyard Ledge', 'Mimic Cave Ledge', 'Death Mountain Floating Island (Light World)', 'Desert Ledge', 'Desert Ledge (Northeast)', 'Lake Hylia Island', 'Spectacle Rock', 'Death Mountain Return Ledge', 'Hyrule Castle Ledge', 'Maze Race Ledge']
queue = collections.deque([world.get_region(region) for region in starting_regions])
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
# That is ok. the bunny logic will check for this case and incorporate special rules.
queue = collections.deque(region for region in world.regions if region.type == RegionType.LightWorld)
seen = set(queue)
while queue:
current = queue.popleft()
current.is_light_world = True
for exit in current.exits:
if exit.name in excluded_entrances:
if exit.connected_region.type == RegionType.DarkWorld:
# Don't venture into the dark world
continue
if exit.connected_region not in seen:
seen.add(exit.connected_region)
queue.append(exit.connected_region)
queue = collections.deque(region for region in world.regions if region.type == RegionType.DarkWorld)
seen = set(queue)
while queue:
current = queue.popleft()
current.is_dark_world = True
for exit in current.exits:
if exit.connected_region.type == RegionType.LightWorld:
# Don't venture into the light world
continue
if exit.connected_region not in seen:
seen.add(exit.connected_region)

View File

@ -1,3 +1,4 @@
import collections
import logging
@ -749,25 +750,65 @@ def set_bunny_rules(world):
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', 'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid', 'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge']
if not world.get_region('Dam').is_light_world:
if world.get_region('Dam').is_dark_world:
# if Dam is is dark world, then it is required to have the pearl to get the sunken item
add_rule(world.get_location('Sunken Treasure'), lambda state: state.has_Pearl())
# similarly we need perl to get across the swamp palace moat
add_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has_Pearl())
# Add pearl requirements for bunny-impassible caves if they occur in the dark world
def path_to_access_rule(path, entrance):
return lambda state: state.can_reach(entrance) and all(rule(state) for rule in path)
def options_to_access_rule(options):
return lambda state: any(rule(state) for rule in options)
def get_rule_to_add(region):
if not region.is_light_world:
return lambda state: state.has_Pearl()
# in this case we are mixed region.
# we collect possible options.
# The base option is having the moon pearl
possible_options = [lambda state: state.has_Pearl()]
# We will search entrances recursively until we find
# one that leads to an exclusively light world region
# for each such entrance a new option ios aded that consist of:
# a) being able to reach it, and
# b) being able to access all entrances from there to `region`
seen = set([region])
queue = collections.deque([(region, [])])
while queue:
(current, path) = queue.popleft()
for entrance in current.entrances:
new_region = entrance.parent_region
if new_region in seen:
continue
new_path = path + [entrance.access_rule]
seen.add(new_region)
if not new_region.is_light_world:
continue # we don't care about pure dark world entrances
if new_region.is_dark_world:
queue.append((new_region, new_path))
else:
# we have reached pure light world, so we have a new possible option
possible_options.append(path_to_access_rule(new_path, entrance))
return options_to_access_rule(possible_options)
# Add requirements for bunny-impassible caves if they occur in the dark world
for region in [world.get_region(name) for name in bunny_impassable_caves]:
if region.is_light_world:
if not region.is_dark_world:
continue
rule = get_rule_to_add(region)
for exit in region.exits:
add_rule(exit, lambda state: state.has_Pearl())
add_rule(exit, rule)
# Add a moon pearl requirement for all locations that are actually in the dark world, except those available to the bunny
# Add requirements for all locations that are actually in the dark world, except those available to the bunny
for location in world.get_locations():
if not location.parent_region.is_light_world:
if location.parent_region.is_dark_world:
if location.name in bunny_accessible_locations:
continue
add_rule(location, lambda state: state.has_Pearl())
add_rule(location, get_rule_to_add(location.parent_region))