diff --git a/BaseClasses.py b/BaseClasses.py index 7c87f119..9012e93d 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2,7 +2,9 @@ import copy from enum import Enum, unique import logging import json -from collections import OrderedDict +from collections import OrderedDict, deque + +from EntranceShuffle import door_addresses from _vendor.collections_extended import bag from Utils import int16_as_bytes from Tables import normal_offset_table, spiral_offset_table @@ -355,6 +357,7 @@ class CollectionState(object): self.prog_items = bag() self.world = parent self.reachable_regions = {player: set() for player in range(1, parent.players + 1)} + self.colored_regions = {player: {} for player in range(1, parent.players + 1)} self.events = [] self.path = {} self.locations_checked = set() @@ -366,6 +369,7 @@ class CollectionState(object): player_regions = [region for region in self.world.regions if region.player == player] self.stale[player] = False rrp = self.reachable_regions[player] + ccr = self.colored_regions[player] new_regions = True reachable_regions_count = len(rrp) while new_regions: @@ -373,13 +377,53 @@ class CollectionState(object): for candidate in possible: if candidate.can_reach_private(self): rrp.add(candidate) + if candidate.type == RegionType.Dungeon: + c_switch_present = False + for ext in candidate.exits: + door = self.world.check_for_door(ext.name, player) + if door is not None and door.crystal == CrystalBarrier.Either: + c_switch_present = True + if c_switch_present: + ccr[candidate] = CrystalBarrier.Either + self.spread_crystal_access(candidate, CrystalBarrier.Either, rrp, ccr, player) + else: + for entrance in candidate.entrances: + door = self.world.check_for_door(entrance.name, player) + if door is None or entrance.parent_region.type != RegionType.Dungeon: + ccr[candidate] = CrystalBarrier.Orange + if entrance.parent_region in ccr.keys(): + color_type = ccr[entrance.parent_region] + current_type = ccr[candidate] if candidate in ccr.keys() else None + ccr[candidate] = color_type if current_type is None or color_type == current_type else CrystalBarrier.Either new_regions = len(rrp) > reachable_regions_count reachable_regions_count = len(rrp) + def spread_crystal_access(self, region, crystal, rrp, ccr, player): + queue = deque([(region, crystal)]) + visited = set() + while len(queue) > 0: + region, crystal = queue.popleft() + visited.add(region) + for ext in region.exits: + connect = ext.connected_region + if connect not in visited and connect is not None and connect.type == RegionType.Dungeon: + if connect in rrp and ext.can_reach(self) and connect: + door = self.world.check_for_door(ext.name, player) + current_crystal = ccr[connect] + if door is not None and not door.blocked and current_crystal != crystal and current_crystal != CrystalBarrier.Either: + if door.crystal in [CrystalBarrier.Either, CrystalBarrier.Null]: + ccr[connect] = crystal + queue.append((connect, crystal)) + else: + queue.append((connect, door.crystal)) + if door.crystal != current_crystal: + ccr[connect] = CrystalBarrier.Either + def copy(self): ret = CollectionState(self.world) ret.prog_items = self.prog_items.copy() ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)} + ret.colored_regions = {player: copy.copy(self.colored_regions[player]) for player in range(1, self.world.players + 1)} ret.events = copy.copy(self.events) ret.path = copy.copy(self.path) ret.locations_checked = copy.copy(self.locations_checked) @@ -400,6 +444,15 @@ class CollectionState(object): return spot.can_reach(self) + def sweep_for_crystal_access(self): + for player, rrp in self.reachable_regions.items(): + dungeon_regions = [x for x in rrp if x.type == RegionType.Dungeon] + ccr = self.colored_regions[player] + for region in dungeon_regions: + if region in ccr.keys(): + self.spread_crystal_access(region, ccr[region], rrp, ccr, player) + self.stale[player] = True + def sweep_for_events(self, key_only=False, locations=None): # this may need improvement new_locations = True @@ -415,6 +468,18 @@ class CollectionState(object): self.collect(event.item, True, event) new_locations = len(reachable_events) > checked_locations checked_locations = len(reachable_events) + if new_locations: + self.sweep_for_crystal_access() + + def can_reach_blue(self, region, player): + if region not in self.colored_regions[player].keys(): + return False + return self.colored_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either] + + def can_reach_orange(self, region, player): + if region not in self.colored_regions[player].keys(): + return False + return self.colored_regions[player][region] in [CrystalBarrier.Orange, CrystalBarrier.Either] def _do_not_flood_the_keys(self, reachable_events): adjusted_checks = list(reachable_events) @@ -699,7 +764,7 @@ class CollectionState(object): class RegionType(Enum): LightWorld = 1 DarkWorld = 2 - Cave = 3 # Also includes Houses + Cave = 3 # Also includes Houses Dungeon = 4 @property @@ -1244,10 +1309,9 @@ class ShopType(Enum): UpgradeShop = 2 class Shop(object): - def __init__(self, region, room_id, default_door_id, type, shopkeeper_config, replaceable): + def __init__(self, region, room_id, type, shopkeeper_config, replaceable): self.region = region self.room_id = room_id - self.default_door_id = default_door_id self.type = type self.inventory = [None, None, None] self.shopkeeper_config = shopkeeper_config @@ -1265,10 +1329,8 @@ class Shop(object): # [id][roomID-low][roomID-high][doorID][zero][shop_config][shopkeeper_config][sram_index] entrances = self.region.entrances config = self.item_count - if len(entrances) == 1 and entrances[0].addresses: - door_id = entrances[0].addresses+1 - elif self.default_door_id is not None: - door_id = self.default_door_id + if len(entrances) == 1 and entrances[0].name in door_addresses: + door_id = door_addresses[entrances[0].name][0]+1 else: door_id = 0 config |= 0x40 # ignore door id diff --git a/DoorShuffle.py b/DoorShuffle.py index 5c7e7a18..22267f2a 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -1034,7 +1034,7 @@ def reassign_key_doors(builder, proposal, world, player): room.delete(d.doorListPos) else: if len(room.doorList) > 1: - room.mirror(d.doorListPos) + room.mirror(d.doorListPos) # todo: I don't think this works for crossed - maybe it will else: room.delete(d.doorListPos) d.smallKey = False diff --git a/Doors.py b/Doors.py index 939e96f9..d012dca5 100644 --- a/Doors.py +++ b/Doors.py @@ -1084,11 +1084,12 @@ def create_doors(world, player): world.get_door('Hera Startile Wide Holes', player).c_switch() world.get_door('PoD Arena Main SW', player).c_switch() + world.get_door('PoD Arena Bonk Path', player).c_switch() world.get_door('PoD Arena Bridge SE', player).c_switch() + world.get_door('PoD Arena Bridge Drop Down', player).c_switch() world.get_door('PoD Arena Main Orange Barrier', player).barrier(CrystalBarrier.Orange) # maybe you can cross this way with blue up?? world.get_door('PoD Arena Main Crystal Path', player).barrier(CrystalBarrier.Blue) - world.get_door('PoD Arena Crystals E', player).barrier(CrystalBarrier.Blue) world.get_door('PoD Arena Crystal Path', player).barrier(CrystalBarrier.Blue) world.get_door('PoD Sexy Statue W', player).c_switch() world.get_door('PoD Sexy Statue NW', player).c_switch() @@ -1185,6 +1186,7 @@ def create_doors(world, player): world.get_door('GT Hookshot Entry Blue Barrier', player).barrier(CrystalBarrier.Blue) world.get_door('GT Double Switch Orange Barrier', player).barrier(CrystalBarrier.Orange) world.get_door('GT Double Switch Orange Barrier 2', player).barrier(CrystalBarrier.Orange) + world.get_door('GT Double Switch Orange Path', player).barrier(CrystalBarrier.Orange) world.get_door('GT Double Switch Key Orange Path', player).barrier(CrystalBarrier.Orange) world.get_door('GT Double Switch Key Blue Path', player).barrier(CrystalBarrier.Blue) world.get_door('GT Double Switch Blue Barrier', player).barrier(CrystalBarrier.Blue) diff --git a/Gui.py b/Gui.py index e6f8a3a1..c7ce1a9b 100755 --- a/Gui.py +++ b/Gui.py @@ -90,6 +90,34 @@ def guiMain(args=None): fileDialogFrame = Frame(rightHalfFrame) + heartbeepFrame = Frame(fileDialogFrame) + heartbeepVar = StringVar() + heartbeepVar.set('normal') + heartbeepOptionMenu = OptionMenu(heartbeepFrame, heartbeepVar, 'double', 'normal', 'half', 'quarter', 'off') + heartbeepOptionMenu.pack(side=RIGHT) + heartbeepLabel = Label(heartbeepFrame, text='Heartbeep sound rate') + heartbeepLabel.pack(side=LEFT, padx=(0,52)) + + heartcolorFrame = Frame(fileDialogFrame) + heartcolorVar = StringVar() + heartcolorVar.set('red') + heartcolorOptionMenu = OptionMenu(heartcolorFrame, heartcolorVar, 'red', 'blue', 'green', 'yellow', 'random') + heartcolorOptionMenu.pack(side=RIGHT) + heartcolorLabel = Label(heartcolorFrame, text='Heart color') + heartcolorLabel.pack(side=LEFT, padx=(0,127)) + + fastMenuFrame = Frame(fileDialogFrame) + fastMenuVar = StringVar() + fastMenuVar.set('normal') + fastMenuOptionMenu = OptionMenu(fastMenuFrame, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') + fastMenuOptionMenu.pack(side=RIGHT) + fastMenuLabel = Label(fastMenuFrame, text='Menu speed') + fastMenuLabel.pack(side=LEFT, padx=(0,100)) + + heartbeepFrame.pack(expand=True, anchor=E) + heartcolorFrame.pack(expand=True, anchor=E) + fastMenuFrame.pack(expand=True, anchor=E) + romDialogFrame = Frame(fileDialogFrame) baseRomLabel = Label(romDialogFrame, text='Base Rom') romVar = StringVar() @@ -144,7 +172,7 @@ def guiMain(args=None): modeVar.set('open') modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'inverted') modeOptionMenu.pack(side=RIGHT) - modeLabel = Label(modeFrame, text='Game Mode') + modeLabel = Label(modeFrame, text='Game mode') modeLabel.pack(side=LEFT) logicFrame = Frame(drowDownFrame) @@ -208,7 +236,7 @@ def guiMain(args=None): difficultyVar.set('normal') difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'normal', 'hard', 'expert') difficultyOptionMenu.pack(side=RIGHT) - difficultyLabel = Label(difficultyFrame, text='Game difficulty') + difficultyLabel = Label(difficultyFrame, text='Difficulty: item pool') difficultyLabel.pack(side=LEFT) timerFrame = Frame(drowDownFrame) @@ -1137,10 +1165,15 @@ def guiMain(args=None): if args.seed: seedVar.set(str(args.seed)) modeVar.set(args.mode) + swordsVar.set(args.swords) difficultyVar.set(args.difficulty) + itemFuncVar.set(args.item_functionality) timerVar.set(args.timer) progressiveVar.set(args.progressive) + accessibilityVar.set(args.accessibility) goalVar.set(args.goal) + crystalsGTVar.set(args.crystals_gt) + crystalsGanonVar.set(args.crystals_ganon) algorithmVar.set(args.algorithm) shuffleVar.set(args.shuffle) doorShuffleVar.set(args.door_shuffle) diff --git a/InvertedRegions.py b/InvertedRegions.py index 19602871..7623b398 100644 --- a/InvertedRegions.py +++ b/InvertedRegions.py @@ -302,16 +302,16 @@ def create_inverted_regions(world, player): create_cave_region(player, 'The Sky', 'A Dark Sky', None, ['DDM Landing','NEDW Landing', 'WDW Landing', 'SDW Landing', 'EDW Landing', 'DD Landing', 'DLHL Landing']) ] - for region_name, (room_id, default_door_id, shopkeeper, replaceable) in shop_table.items(): + for region_name, (room_id, shopkeeper, replaceable) in shop_table.items(): region = world.get_region(region_name, player) - shop = Shop(region, room_id, default_door_id, ShopType.Shop, shopkeeper, replaceable) + shop = Shop(region, room_id, ShopType.Shop, shopkeeper, replaceable) region.shop = shop world.shops.append(shop) for index, (item, price) in enumerate(default_shop_contents[region_name]): shop.add_inventory(index, item, price) region = world.get_region('Capacity Upgrade', player) - shop = Shop(region, 0x0115, 0x5D, ShopType.UpgradeShop, 0x04, True) + shop = Shop(region, 0x0115, ShopType.UpgradeShop, 0x04, True) region.shop = shop world.shops.append(shop) shop.add_inventory(0, 'Bomb Upgrade (+5)', 100, 7) @@ -373,18 +373,18 @@ def mark_dark_world_regions(world): seen.add(exit.connected_region) queue.append(exit.connected_region) -# (room_id, default_door_id, shopkeeper, replaceable) +# (room_id, shopkeeper, replaceable) shop_table = { - 'Cave Shop (Dark Death Mountain)': (0x0112, 0x6E, 0xC1, True), - 'Red Shield Shop': (0x0110, 0x75, 0xC1, True), - 'Dark Lake Hylia Shop': (0x010F, 0x74, 0xC1, True), - 'Dark World Lumberjack Shop': (0x010F, 0x57, 0xC1, True), - 'Village of Outcasts Shop': (0x010F, 0x60, 0xC1, True), - 'Dark World Potion Shop': (0x010F, 0x6F, 0xC1, True), - 'Light World Death Mountain Shop': (0x00FF, None, 0xA0, True), - 'Kakariko Shop': (0x011F, 0x46, 0xA0, True), - 'Cave Shop (Lake Hylia)': (0x0112, 0x58, 0xA0, True), - 'Potion Shop': (0x0109, 0x4C, 0xFF, False), + 'Cave Shop (Dark Death Mountain)': (0x0112, 0xC1, True), + 'Red Shield Shop': (0x0110, 0xC1, True), + 'Dark Lake Hylia Shop': (0x010F, 0xC1, True), + 'Dark World Lumberjack Shop': (0x010F, 0xC1, True), + 'Village of Outcasts Shop': (0x010F, 0xC1, True), + 'Dark World Potion Shop': (0x010F, 0xC1, True), + 'Light World Death Mountain Shop': (0x00FF, 0xA0, True), + 'Kakariko Shop': (0x011F, 0xA0, True), + 'Cave Shop (Lake Hylia)': (0x0112, 0xA0, True), + 'Potion Shop': (0x0109, 0xFF, False), # Bomb Shop not currently modeled as a shop, due to special nature of items } # region, [item] diff --git a/ItemList.py b/ItemList.py index eca0acdc..3318ce45 100644 --- a/ItemList.py +++ b/ItemList.py @@ -267,7 +267,7 @@ def set_up_take_anys(world, player): entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance, old_man_take_any, player) entrance.target = 0x58 - old_man_take_any.shop = Shop(old_man_take_any, 0x0112, None, ShopType.TakeAny, 0xE2, True) + old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True) world.shops.append(old_man_take_any.shop) old_man_take_any.shop.active = True @@ -290,7 +290,7 @@ def set_up_take_anys(world, player): entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance, take_any, player) entrance.target = target - take_any.shop = Shop(take_any, room_id, None, ShopType.TakeAny, 0xE3, True) + take_any.shop = Shop(take_any, room_id, ShopType.TakeAny, 0xE3, True) world.shops.append(take_any.shop) take_any.shop.active = True take_any.shop.add_inventory(0, 'Blue Potion', 0, 0) diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 217a6e9f..4bd57642 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -118,9 +118,9 @@ def analyze_dungeon(key_layout, world, player): raw_avail = chest_keys + len(key_counter.key_only_locations) available = raw_avail - key_counter.used_keys possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop) - avail_bigs = count_unique_big_doors(key_counter) + avail_bigs = exist_relevant_big_doors(key_counter, key_layout) if not key_counter.big_key_opened: - if chest_keys == count_locations_big_optional(key_counter.free_locations) and available <= possible_smalls and avail_bigs == 0: + if chest_keys == count_locations_big_optional(key_counter.free_locations) and available <= possible_smalls and not avail_bigs: key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations)) if not key_counter.big_key_opened and big_chest_in_locations(key_counter.free_locations): key_logic.sm_restricted.update(find_big_chest_locations(key_counter.free_locations)) @@ -318,6 +318,7 @@ def create_rule(key_counter, prev_counter, key_layout, world): available = raw_avail - key_counter.used_keys possible_smalls = count_unique_small_doors(key_counter, key_layout.flat_prop) required_keys = min(available, possible_smalls) + key_counter.used_keys + # required_keys = key_counter.used_keys + 1 # this sometimes makes more sense # if prev_avail < required_keys: # required_keys = prev_avail + prev_counter.used_keys # return DoorRules(required_keys) @@ -487,15 +488,16 @@ def count_unique_small_doors(key_counter, proposal): return cnt -def count_unique_big_doors(key_counter): - cnt = 0 - counted = set() - for door in key_counter.child_doors: - if door.bigKey and door not in counted: - cnt += 1 - counted.add(door) - counted.add(door.dest) - return cnt +def exist_relevant_big_doors(key_counter, key_layout): + bk_counter = find_counter(key_counter.open_doors, True, key_layout, False) + if bk_counter is not None: + diff = dict_difference(bk_counter.free_locations, key_counter.free_locations) + if len(diff) > 0: + return True + diff = dict_difference(bk_counter.key_only_locations, key_counter.key_only_locations) + if len(diff) > 0: + return True + return False def count_locations_big_optional(locations, bk=False): @@ -788,7 +790,7 @@ def state_id(state, flat_proposal): return s_id -def find_counter(opened_doors, bk_hint, key_layout): +def find_counter(opened_doors, bk_hint, key_layout, raise_on_error=True): counter = find_counter_hint(opened_doors, bk_hint, key_layout) if counter is not None: return counter @@ -801,7 +803,9 @@ def find_counter(opened_doors, bk_hint, key_layout): counter = find_counter_hint(dict.fromkeys(more_doors), bk_hint, key_layout) if counter is not None: return counter - raise Exception('Unable to find door permutation. Init CID: %s' % counter_id(opened_doors, bk_hint, key_layout.flat_prop)) + if raise_on_error: + raise Exception('Unable to find door permutation. Init CID: %s' % counter_id(opened_doors, bk_hint, key_layout.flat_prop)) + return None def find_counter_hint(opened_doors, bk_hint, key_layout): diff --git a/Main.py b/Main.py index 9ed5efc6..e7ae3d00 100644 --- a/Main.py +++ b/Main.py @@ -290,7 +290,7 @@ def copy_dynamic_regions_and_locations(world, ret): # Note: ideally exits should be copied here, but the current use case (Take anys) do not require this if region.shop: - new_reg.shop = Shop(new_reg, region.shop.room_id, region.shop.default_door_id, region.shop.type, region.shop.shopkeeper_config, region.shop.replaceable) + new_reg.shop = Shop(new_reg, region.shop.room_id, region.shop.type, region.shop.shopkeeper_config, region.shop.replaceable) ret.shops.append(new_reg.shop) for location in world.dynamic_locations: @@ -325,6 +325,7 @@ def create_playthrough(world): while sphere_candidates: if not world.keysanity: state.sweep_for_events(key_only=True) + state.sweep_for_crystal_access() sphere = [] # build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres @@ -379,6 +380,7 @@ def create_playthrough(world): while required_locations: if not world.keysanity: state.sweep_for_events(key_only=True) + state.sweep_for_crystal_access() sphere = list(filter(lambda loc: state.can_reach(loc) and state.not_flooding_a_key(world, loc), required_locations)) diff --git a/Regions.py b/Regions.py index 93aacc57..5d27d312 100644 --- a/Regions.py +++ b/Regions.py @@ -705,16 +705,16 @@ def create_regions(world, player): create_dungeon_region(player, 'GT Agahnim 2', 'Ganon\'s Tower', ['Agahnim 2'], ['GT Agahnim 2 SW']) ] - for region_name, (room_id, default_door_id, shopkeeper, replaceable) in shop_table.items(): + for region_name, (room_id, shopkeeper, replaceable) in shop_table.items(): region = world.get_region(region_name, player) - shop = Shop(region, room_id, default_door_id, ShopType.Shop, shopkeeper, replaceable) + shop = Shop(region, room_id, ShopType.Shop, shopkeeper, replaceable) region.shop = shop world.shops.append(shop) for index, (item, price) in enumerate(default_shop_contents[region_name]): shop.add_inventory(index, item, price) region = world.get_region('Capacity Upgrade', player) - shop = Shop(region, 0x0115, 0x5D, ShopType.UpgradeShop, 0x04, True) + shop = Shop(region, 0x0115, ShopType.UpgradeShop, 0x04, True) region.shop = shop world.shops.append(shop) shop.add_inventory(0, 'Bomb Upgrade (+5)', 100, 7) @@ -780,18 +780,18 @@ def mark_light_world_regions(world): seen.add(exit.connected_region) queue.append(exit.connected_region) -# (room_id, default_door_id, shopkeeper, replaceable) +# (room_id, shopkeeper, replaceable) shop_table = { - 'Cave Shop (Dark Death Mountain)': (0x0112, 0x6E, 0xC1, True), - 'Red Shield Shop': (0x0110, 0x75, 0xC1, True), - 'Dark Lake Hylia Shop': (0x010F, 0x74, 0xC1, True), - 'Dark World Lumberjack Shop': (0x010F, 0x57, 0xC1, True), - 'Village of Outcasts Shop': (0x010F, 0x60, 0xC1, True), - 'Dark World Potion Shop': (0x010F, 0x6F, 0xC1, True), - 'Light World Death Mountain Shop': (0x00FF, None, 0xA0, True), - 'Kakariko Shop': (0x011F, 0x46, 0xA0, True), - 'Cave Shop (Lake Hylia)': (0x0112, 0x58, 0xA0, True), - 'Potion Shop': (0x0109, 0x4C, 0xFF, False), + 'Cave Shop (Dark Death Mountain)': (0x0112, 0xC1, True), + 'Red Shield Shop': (0x0110, 0xC1, True), + 'Dark Lake Hylia Shop': (0x010F, 0xC1, True), + 'Dark World Lumberjack Shop': (0x010F, 0xC1, True), + 'Village of Outcasts Shop': (0x010F, 0xC1, True), + 'Dark World Potion Shop': (0x010F, 0xC1, True), + 'Light World Death Mountain Shop': (0x00FF, 0xA0, True), + 'Kakariko Shop': (0x011F, 0xA0, True), + 'Cave Shop (Lake Hylia)': (0x0112, 0xA0, True), + 'Potion Shop': (0x0109, 0xFF, False), # Bomb Shop not currently modeled as a shop, due to special nature of items } # region, [item] diff --git a/Rules.py b/Rules.py index 39647bbb..fd93c9f3 100644 --- a/Rules.py +++ b/Rules.py @@ -423,6 +423,55 @@ def global_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player)) add_key_logic_rules(world, player) + + # crystal switch rules + set_rule(world.get_entrance('PoD Arena Crystal Path', player), lambda state: state.can_reach_blue(world.get_region('PoD Arena Crystal', player), player)) + set_rule(world.get_entrance('Swamp Trench 2 Pots Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Trench 2 Pots', player), player)) + set_rule(world.get_entrance('Swamp Shortcut Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Swamp Shortcut', player), player)) + set_rule(world.get_entrance('Thieves Attic ES', player), lambda state: state.can_reach_blue(world.get_region('Thieves Attic', player), player)) + set_rule(world.get_entrance('Thieves Hellway Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway', player), player)) + set_rule(world.get_entrance('Thieves Hellway Crystal Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway N Crystal', player), player)) + set_rule(world.get_entrance('Thieves Triple Bypass SE', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player)) + set_rule(world.get_entrance('Thieves Triple Bypass WN', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player)) + set_rule(world.get_entrance('Thieves Triple Bypass EN', player), lambda state: state.can_reach_blue(world.get_region('Thieves Triple Bypass', player), player)) + set_rule(world.get_entrance('Ice Crystal Right Blue Hole', player), lambda state: state.can_reach_blue(world.get_region('Ice Crystal Right', player), player)) + set_rule(world.get_entrance('Ice Crystal Left Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Ice Crystal Left', player), player)) + set_rule(world.get_entrance('Ice Backwards Room Hole', player), lambda state: state.can_reach_blue(world.get_region('Ice Backwards Room', player), player)) + set_rule(world.get_entrance('Mire Hub Upper Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub', player), player)) + set_rule(world.get_entrance('Mire Hub Lower Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub', player), player)) + set_rule(world.get_entrance('Mire Hub Right Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub Right', player), player)) + set_rule(world.get_entrance('Mire Hub Top Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Hub Top', player), player)) + set_rule(world.get_entrance('Mire Map Spike Side Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Map Spike Side', player), player)) + set_rule(world.get_entrance('Mire Map Spot Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Map Spot', player), player)) + set_rule(world.get_entrance('Mire Crystal Dead End Left Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Dead End', player), player)) + set_rule(world.get_entrance('Mire Crystal Dead End Right Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Dead End', player), player)) + set_rule(world.get_entrance('Mire South Fish Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire South Fish', player), player)) + set_rule(world.get_entrance('Mire Compass Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Compass Room', player), player)) + set_rule(world.get_entrance('Mire Crystal Mid Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Mid', player), player)) + set_rule(world.get_entrance('Mire Crystal Left Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Mire Crystal Left', player), player)) + set_rule(world.get_entrance('TR Crystal Maze Blue Path', player), lambda state: state.can_reach_blue(world.get_region('TR Crystal Maze End', player), player)) + set_rule(world.get_entrance('GT Hookshot Entry Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Hookshot South Entry', player), player)) + set_rule(world.get_entrance('GT Double Switch Key Blue Path', player), lambda state: state.can_reach_blue(world.get_region('GT Double Switch Key Spot', player), player)) + set_rule(world.get_entrance('GT Double Switch Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('GT Double Switch Switches', player), player)) + set_rule(world.get_entrance('GT Double Switch Transition Blue', player), lambda state: state.can_reach_blue(world.get_region('GT Double Switch Transition', player), player)) + + set_rule(world.get_entrance('Swamp Barrier Ledge - Orange', player), lambda state: state.can_reach_orange(world.get_region('Swamp Barrier Ledge', player), player)) + set_rule(world.get_entrance('Swamp Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('Swamp Barrier', player), player)) + set_rule(world.get_entrance('Thieves Hellway Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Thieves Hellway', player), player)) + set_rule(world.get_entrance('Thieves Hellway Crystal Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Thieves Hellway S Crystal', player), player)) + set_rule(world.get_entrance('Ice Bomb Jump Ledge Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Ledge', player), player)) + set_rule(world.get_entrance('Ice Bomb Jump Catwalk Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Catwalk', player), player)) + set_rule(world.get_entrance('Ice Crystal Right Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Crystal Right', player), player)) + set_rule(world.get_entrance('Ice Crystal Left Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Crystal Left', player), player)) + set_rule(world.get_entrance('Mire Crystal Right Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Crystal Right', player), player)) + set_rule(world.get_entrance('Mire Crystal Mid Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Crystal Mid', player), player)) + set_rule(world.get_entrance('Mire Firesnake Skip Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Firesnake Skip', player), player)) + set_rule(world.get_entrance('Mire Antechamber Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Mire Antechamber', player), player)) + set_rule(world.get_entrance('GT Double Switch Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Entry', player), player)) + set_rule(world.get_entrance('GT Double Switch Orange Barrier 2', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Entry', player), player)) + set_rule(world.get_entrance('GT Double Switch Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Switches', player), player)) + set_rule(world.get_entrance('GT Double Switch Key Orange Path', player), lambda state: state.can_reach_orange(world.get_region('GT Double Switch Key Spot', player), player)) + # End of door rando rules. add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))