diff --git a/.gitignore b/.gitignore index 70c2dbd0..de385a79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea .vscode *_Spoiler.txt +*.bmbp *.pyc *.sfc *.wixobj @@ -18,3 +19,4 @@ weights/ /MultiMystery/ /Players/ /QUsb2Snes/ +/options.yaml diff --git a/AdjusterMain.py b/AdjusterMain.py index 2b6925a2..64b1042e 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -7,7 +7,7 @@ from Rom import LocalRom, apply_rom_settings def adjust(args): - start = time.process_time() + start = time.perf_counter() logger = logging.getLogger('Adjuster') logger.info('Patching ROM.') @@ -24,6 +24,6 @@ def adjust(args): rom.write_to_file(output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc')) logger.info('Done. Enjoy.') - logger.debug('Total Time: %s', time.process_time() - start) + logger.debug('Total Time: %s', time.perf_counter() - start) return args diff --git a/BaseClasses.py b/BaseClasses.py index 0193f12d..471ecf88 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -5,6 +5,7 @@ from enum import Enum, unique import logging import json from collections import OrderedDict, Counter + from EntranceShuffle import door_addresses from Utils import int16_as_bytes from typing import Union @@ -451,9 +452,9 @@ class CollectionState(object): def can_extend_magic(self, player: int, smallmagic: int = 16, fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has. basemagic = 8 - if self.has('Quarter Magic', player): + if self.has('Magic Upgrade (1/4)', player): basemagic = 32 - elif self.has('Half Magic', player): + elif self.has('Magic Upgrade (1/2)', player): basemagic = 16 if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player): if self.world.difficulty_adjustments[player] == 'hard' and not fullrefill: @@ -470,14 +471,12 @@ class CollectionState(object): or (self.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player))) or self.can_shoot_arrows(player) or self.has('Fire Rod', player) - ) + or (self.has('Bombs (10)', player) and enemies < 6)) def can_shoot_arrows(self, player: int) -> bool: if self.world.retro[player]: - # TODO: need to decide how we want to handle wooden arrows longer-term (a can-buy-a check, or via dynamic shop location) - # FIXME: Should do something about hard+ ganon only silvers. For the moment, i believe they effective grant wooden, so we are safe - return self.has('Bow', player) and ( - self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player)) + # TODO: Progressive and Non-Progressive silvers work differently (progressive is not usable until the shop arrow is bought) + return self.has('Bow', player) and self.can_buy_unlimited('Single Arrow', player) return self.has('Bow', player) def can_get_good_bee(self, player: int) -> bool: diff --git a/Bosses.py b/Bosses.py index 8d5f6a64..e25e9340 100644 --- a/Bosses.py +++ b/Bosses.py @@ -18,6 +18,7 @@ def ArmosKnightsDefeatRule(state, player): # Magic amounts are probably a bit overkill return ( state.has_blunt_weapon(player) or + state.can_shoot_arrows(player) or (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 10)) or (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or (state.has('Ice Rod', player) and state.can_extend_magic(player, 32)) or @@ -26,18 +27,20 @@ def ArmosKnightsDefeatRule(state, player): state.has('Red Boomerang', player)) def LanmolasDefeatRule(state, player): - # TODO: Allow the canes here? return ( state.has_blunt_weapon(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or + state.has('Cane of Somaria', player) or + state.has('Cane of Byrna', player) or state.can_shoot_arrows(player)) def MoldormDefeatRule(state, player): return state.has_blunt_weapon(player) def HelmasaurKingDefeatRule(state, player): - return state.has_blunt_weapon(player) or state.can_shoot_arrows(player) + # TODO: technically possible with the hammer + return state.has_sword(player) or state.can_shoot_arrows(player) def ArrghusDefeatRule(state, player): if not state.has('Hookshot', player): @@ -95,7 +98,9 @@ def VitreousDefeatRule(state, player): def TrinexxDefeatRule(state, player): if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)): return False - return state.has('Hammer', player) or state.has_beam_sword(player) or (state.has_sword(player) and state.can_extend_magic(player, 32)) + return state.has('Hammer', player) or state.has('Tempered Sword', player) or state.has('Golden Sword', player) or \ + (state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or \ + (state.has_sword(player) and state.can_extend_magic(player, 32)) def AgahnimDefeatRule(state, player): return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index aa4538f1..a75d4e08 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1255,7 +1255,7 @@ def link_inverted_entrances(world, player): caves = list(Cave_Exits + Cave_Three_Exits + Old_Man_House) single_doors = list(Single_Cave_Doors) bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Inverted_Blacksmith_Multi_Cave_Doors) door_targets = list(Inverted_Single_Cave_Targets) # place links house @@ -1337,7 +1337,7 @@ def link_inverted_entrances(world, player): old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Inverted Agahnims Tower', 'Tower of Hera']) caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits) # don't need to consider three exit caves, have one exit caves to avoid parity issues bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Inverted_Blacksmith_Multi_Cave_Doors) door_targets = list(Inverted_Single_Cave_Targets) old_man_house = list(Old_Man_House) @@ -1488,7 +1488,7 @@ def link_inverted_entrances(world, player): old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances + ['Inverted Agahnims Tower', 'Tower of Hera']) caves = list(Cave_Exits + Dungeon_Exits + Cave_Three_Exits + Old_Man_House) # don't need to consider three exit caves, have one exit caves to avoid parity issues bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Inverted_Blacksmith_Multi_Cave_Doors) door_targets = list(Inverted_Single_Cave_Targets) # randomize which desert ledge door is a must-exit @@ -1611,8 +1611,8 @@ def link_inverted_entrances(world, player): # and rentering to find bomb shop. However appended list here is all those that we currently have # bomb shop logic for. # Specifically we could potentially add: 'Dark Death Mountain Ledge (East)' and doors associated with pits - bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors + ['Turtle Rock Isolated Ledge Entrance', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance']) - blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) + bomb_shop_doors = list(Inverted_Bomb_Shop_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors + ['Turtle Rock Isolated Ledge Entrance', 'Hookshot Cave Back Entrance']) + blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Inverted_Blacksmith_Multi_Cave_Doors) door_targets = list(Inverted_Single_Cave_Targets) random.shuffle(doors) @@ -2634,8 +2634,6 @@ Inverted_Bomb_Shop_Multi_Cave_Doors = ['Hyrule Castle Entrance (South)', 'Death Mountain Return Cave (East)', 'Death Mountain Return Cave (West)', 'Spectacle Rock Cave Peak', - 'Spectacle Rock Cave', - 'Spectacle Rock Cave (Bottom)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', @@ -2650,6 +2648,8 @@ Inverted_Bomb_Shop_Multi_Cave_Doors = ['Hyrule Castle Entrance (South)', 'Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'] +Inverted_Blacksmith_Multi_Cave_Doors = Blacksmith_Multi_Cave_Doors # same as non-inverted + Inverted_LW_Single_Cave_Doors = LW_Single_Cave_Doors + ['Inverted Big Bomb Shop'] Inverted_DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', @@ -2988,8 +2988,10 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central ] inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), - ('Lake Hylia Island', 'Lake Hylia Island'), - ('Zoras River', 'Zoras River'), + ('Lake Hylia Island Pier', 'Lake Hylia Island'), + ('Lake Hylia Warp', 'Northeast Light World'), + ('Northeast Light World Warp', 'Light World'), + ('Zoras River', 'Zoras River'), ('Kings Grave Outer Rocks', 'Kings Grave Area'), ('Kings Grave Inner Rocks', 'Light World'), ('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'), diff --git a/Gui.py b/Gui.py index d4750dd9..94b6e7c3 100755 --- a/Gui.py +++ b/Gui.py @@ -196,7 +196,16 @@ def guiMain(args=None): def RomSelect(): rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")]) - romVar.set(rom) + import Patch + try: + Patch.get_base_rom_bytes(rom) # throws error on checksum fail + except Exception as e: + logging.exception(e) + messagebox.showerror(title="Error while reading ROM", message=str(e)) + else: + romVar.set(rom) + romSelectButton['state'] = "disabled" + romSelectButton["text"] = "ROM verified" romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect) baseRomLabel.pack(side=LEFT) @@ -464,6 +473,8 @@ def guiMain(args=None): elif type(v) is dict: # use same settings for every player setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)}) try: + if not guiargs.suppress_rom and not os.path.exists(guiargs.rom): + raise FileNotFoundError(f"Could not find specified rom file {guiargs.rom}") if guiargs.count is not None: seed = guiargs.seed for _ in range(guiargs.count): diff --git a/InvertedRegions.py b/InvertedRegions.py index cfbf5ed0..6100781e 100644 --- a/InvertedRegions.py +++ b/InvertedRegions.py @@ -10,7 +10,7 @@ def create_inverted_regions(world, player): ["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam', 'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave', 'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', - 'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier', 'Lake Hylia Island', + 'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia Warp', 'Bonk Rock Cave', 'Library', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow', 'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Light World River Drop', 'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)', @@ -29,7 +29,7 @@ def create_inverted_regions(world, player): "Blind\'s Hideout - Right", "Blind\'s Hideout - Far Left", "Blind\'s Hideout - Far Right"]), - create_lw_region(player, 'Northeast Light World', None, ['Zoras River', 'Waterfall of Wishing', 'Potion Shop Outer Rock', 'Northeast Dark World Mirror Spot']), + create_lw_region(player, 'Northeast Light World', None, ['Zoras River', 'Waterfall of Wishing', 'Potion Shop Outer Rock', 'Northeast Dark World Mirror Spot', 'Northeast Light World Warp']), create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Potion Shop Inner Bushes', 'Potion Shop Inner Rock', 'Potion Shop Mirror Spot', 'Potion Shop River Drop']), create_lw_region(player, 'Graveyard Cave Area', None, ['Graveyard Cave', 'Graveyard Cave Inner Bushes', 'Graveyard Cave Mirror Spot']), create_lw_region(player, 'River', None, ['Light World Pier', 'Potion Shop Pier']), diff --git a/ItemList.py b/ItemList.py index 840f65ad..2a148e79 100644 --- a/ItemList.py +++ b/ItemList.py @@ -383,7 +383,7 @@ def set_up_shops(world, player): rss = world.get_region('Red Shield Shop', player).shop if not rss.locked: rss.add_inventory(2, 'Single Arrow', 80) - for shop in random.sample([s for s in world.shops if s.custom and not s.locked and s.region.player == player], 5): + for shop in random.sample([s for s in world.shops if s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player], 5): shop.locked = True shop.add_inventory(0, 'Single Arrow', 80) shop.add_inventory(1, 'Small Key (Universal)', 100) diff --git a/Items.py b/Items.py index 6c9bbed9..58466a1c 100644 --- a/Items.py +++ b/Items.py @@ -128,6 +128,11 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla 'Compass (Escape)': (False, True, 'Compass', 0x8F, 'Now you can find no boss!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Hyrule Castle'), 'Map (Escape)': (False, True, 'Map', 0x7F, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Hyrule Castle'), 'Small Key (Agahnims Tower)': (False, False, 'SmallKey', 0xA4, 'A small key to Agahnim', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Castle Tower'), + # doors-specific items, baserom will not be able to understand these + 'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 'A big key to Agahnim', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Castle Tower'), + 'Compass (Agahnims Tower)': (False, True, 'Compass', 0x8B, 'Now you can find Aga1!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Castle Tower'), + 'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Castle Tower'), + # end of doors-specific items 'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 'A small key to darkness', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'), 'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 'A big key to darkness', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'), 'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 'Now you can find Helmasaur King!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Palace of Darkness'), diff --git a/Main.py b/Main.py index 0098b3dd..a68c29e3 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,7 @@ def main(args, seed=None): os.makedirs(args.outputpath, exist_ok=True) output_path.cached_path = args.outputpath - start = time.process_time() + start = time.perf_counter() # initialize the world world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, @@ -260,7 +260,7 @@ def main(args, seed=None): world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') - logger.debug('Total Time: %s', time.process_time() - start) + logger.debug('Total Time: %s', time.perf_counter() - start) return world diff --git a/ModuleUpdate.py b/ModuleUpdate.py index 61c9ccf2..abae05c4 100644 --- a/ModuleUpdate.py +++ b/ModuleUpdate.py @@ -31,6 +31,7 @@ def update(): traceback.print_exc() input(f'Required python module {module} not found, press enter to install it') update_command() + return else: if hasattr(module, "__version__"): module_version = module.__version__ @@ -42,6 +43,7 @@ def update(): input(f'Required python module {module} is outdated ({module_version}<{remote_version}),' ' press enter to upgrade it') update_command() + return if __name__ == "__main__": diff --git a/MultiClient.py b/MultiClient.py index ef18cbf0..a18e7fc4 100644 --- a/MultiClient.py +++ b/MultiClient.py @@ -5,8 +5,6 @@ import logging import typing import urllib.parse import atexit -import sys -import os exit_func = atexit.register(input, "Press enter to close.") @@ -31,7 +29,7 @@ class ReceivedItem(typing.NamedTuple): player: int class Context: - def __init__(self, snes_address, server_address, password): + def __init__(self, snes_address, server_address, password, found_items): self.snes_address = snes_address self.server_address = server_address @@ -64,6 +62,7 @@ class Context: self.awaiting_rom = False self.rom = None self.auth = None + self.found_items = found_items color_codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, @@ -347,6 +346,17 @@ async def snes_connect(ctx : Context, address): if problem not in seen_problems: seen_problems.add(problem) logging.error(f"Error connecting to QUsb2snes ({problem})") + if len(seen_problems) == 1: + #this is the first problem. Let's try launching QUsb2snes if it isn't already running + qusb2snes_path = Utils.get_options()["general_options"]["qusb2snes"] + import os + if os.path.isfile(qusb2snes_path): + logging.info(f"Attempting to start {qusb2snes_path}") + import subprocess + subprocess.Popen(qusb2snes_path, cwd=os.path.dirname(qusb2snes_path)) + else: + logging.info(f"Attempt to start (Q)Usb2Snes was aborted as path {qusb2snes_path} was not found, please start it yourself if it is not running") + await asyncio.sleep(1) else: ctx.snes_state = SNES_CONNECTED @@ -666,6 +676,8 @@ async def process_server_cmd(ctx : Context, cmd, args): ctx.password = None await server_auth(ctx, True) if 'InvalidRom' in args: + ctx.snes_state = SNES_DISCONNECTED + ctx.rom = None raise Exception( 'Invalid ROM detected, please verify that you have loaded the correct rom and reconnect your snes (/snes)') if 'SlotAlreadyTaken' in args: @@ -717,6 +729,12 @@ async def process_server_cmd(ctx : Context, cmd, args): logging.info( '%s sent %s to %s (%s)' % (player_sent, item, player_recvd, get_location_name_from_address(location))) + elif cmd == 'ItemFound': + found = ReceivedItem(*args) + item = color(get_item_name_from_id(found.item), 'cyan' if found.player != ctx.slot else 'green') + player_sent = color(ctx.player_names[found.player], 'yellow' if found.player != ctx.slot else 'magenta') + logging.info('%s found %s (%s)' % (player_sent, item, get_location_name_from_address(found.location))) + elif cmd == 'Hint': hints = [Utils.Hint(*hint) for hint in args] for hint in hints: @@ -731,6 +749,11 @@ async def process_server_cmd(ctx : Context, cmd, args): elif cmd == 'Print': logging.info(args) +def get_tags(ctx: Context): + tags = ['Berserker'] + if ctx.found_items: + tags.append('FoundItems') + return tags async def server_auth(ctx: Context, password_requested): if password_requested and not ctx.password: @@ -743,7 +766,7 @@ async def server_auth(ctx: Context, password_requested): ctx.awaiting_rom = False ctx.auth = ctx.rom.copy() await send_msgs(ctx.socket, [['Connect', { - 'password': ctx.password, 'rom': ctx.auth, 'version': [1, 2, 0], 'tags': ['Berserker'] + 'password': ctx.password, 'rom': ctx.auth, 'version': [1, 2, 0], 'tags': get_tags(ctx) }]]) async def console_input(ctx : Context): @@ -817,6 +840,14 @@ async def console_loop(ctx : Context): if location not in ctx.locations_checked: logging.info('Missing: ' + location) + elif precommand == "show_items": + if len(command) > 1: + ctx.found_items = command[1].lower() in {"1", "true", "on"} + else: + ctx.found_items = not ctx.found_items + logging.info(f"Set showing team items to {ctx.found_items}") + asyncio.create_task(send_msgs(ctx.socket, [['UpdateTags', get_tags(ctx)]])) + elif precommand == "license": with open("LICENSE") as f: logging.info(f.read()) @@ -968,6 +999,7 @@ async def main(): parser.add_argument('--connect', default=None, help='Address of the multiworld host.') parser.add_argument('--password', default=None, help='Password of the multiworld host.') parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) + parser.add_argument('--founditems', default=False, action='store_true', help='Show items found by other players for themselves.') args = parser.parse_args() logging.basicConfig(format='%(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO)) @@ -979,7 +1011,7 @@ async def main(): logging.info(f"Wrote rom file to {romfile}") asyncio.create_task(run_game(romfile)) - ctx = Context(args.snes, args.connect, args.password) + ctx = Context(args.snes, args.connect, args.password, args.founditems) input_task = asyncio.create_task(console_loop(ctx)) diff --git a/MultiMystery.py b/MultiMystery.py index 52007c03..66f558ca 100644 --- a/MultiMystery.py +++ b/MultiMystery.py @@ -15,8 +15,11 @@ Configuration can be found in host.yaml import os import subprocess import sys +import threading +import concurrent.futures -def feedback(text:str): + +def feedback(text: str): print(text) input("Press Enter to ignore and probably crash.") @@ -28,10 +31,12 @@ if __name__ == "__main__": ModuleUpdate.update() - from Utils import parse_yaml, get_public_ipv4 + + from Utils import get_public_ipv4, get_options + from Patch import create_patch_file - options = parse_yaml(open("host.yaml").read()) + options = get_options() multi_mystery_options = options["multi_mystery_options"] output_path = multi_mystery_options["output_path"] @@ -49,6 +54,8 @@ if __name__ == "__main__": meta_file_path = multi_mystery_options["meta_file_path"] teams = multi_mystery_options["teams"] rom_file = options["general_options"]["rom_file"] + host = options["server_options"]["host"] + port = options["server_options"]["port"] py_version = f"{sys.version_info.major}.{sys.version_info.minor}" @@ -129,38 +136,58 @@ if __name__ == "__main__": 2: "7z", 3: "bz2"}[zip_format] + ziplock = threading.Lock() + + def pack_file(file: str): - zf.write(os.path.join(output_path, file), file) - print(f"Packed {file} into zipfile {zipname}") + with ziplock: + zf.write(os.path.join(output_path, file), file) + print(f"Packed {file} into zipfile {zipname}") + def remove_zipped_file(file: str): os.remove(os.path.join(output_path, file)) print(f"Removed {file} which is now present in the zipfile") + zipname = os.path.join(output_path, f"ER_{seedname}.{typical_zip_ending}") print(f"Creating zipfile {zipname}") - ipv4 = get_public_ipv4() - with zipfile.ZipFile(zipname, "w", compression=compression, compresslevel=9) as zf: - for file in os.listdir(output_path): - if file.endswith(".sfc") and seedname in file: - if zip_diffs: - diff = os.path.split(create_patch_file(os.path.join(output_path, file), ipv4))[1] - pack_file(diff) - if zip_diffs == 2: - remove_zipped_file(diff) - if zip_roms: - pack_file(file) - if zip_roms == 2 and player_name.lower() not in file.lower(): - remove_zipped_file(file) - if zip_multidata and os.path.exists(os.path.join(output_path, multidataname)): - pack_file(multidataname) - if zip_multidata == 2: - remove_zipped_file(multidataname) - if zip_spoiler and create_spoiler: - pack_file(spoilername) - if zip_spoiler == 2: - remove_zipped_file(spoilername) + ipv4 = (host if host else get_public_ipv4()) + ":" + str(port) + + + def _handle_file(file: str): + if zip_diffs: + # the main reason for using threading, the patch is created using bsdiff4, which frees the GIL + diff = os.path.split(create_patch_file(os.path.join(output_path, file), ipv4))[1] + pack_file(diff) + if zip_diffs == 2: + remove_zipped_file(diff) + if zip_roms: + pack_file(file) + if zip_roms == 2 and player_name.lower() not in file.lower(): + remove_zipped_file(file) + + + with concurrent.futures.ThreadPoolExecutor() as pool: + futures = [] + with zipfile.ZipFile(zipname, "w", compression=compression, compresslevel=9) as zf: + for file in os.listdir(output_path): + if file.endswith(".sfc") and seedname in file: + futures.append(pool.submit(_handle_file, file)) + + if zip_multidata and os.path.exists(os.path.join(output_path, multidataname)): + pack_file(multidataname) + if zip_multidata == 2: + remove_zipped_file(multidataname) + + if zip_spoiler and create_spoiler: + pack_file(spoilername) + if zip_spoiler == 2: + remove_zipped_file(spoilername) + + for future in futures: + future.result() # make sure we close the zip AFTER any packing is done if os.path.exists(os.path.join(output_path, multidataname)): if os.path.exists("BerserkerMultiServer.exe"): diff --git a/MultiServer.py b/MultiServer.py index 17e30280..79778564 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -6,7 +6,6 @@ import logging import zlib import collections import typing -import os import ModuleUpdate @@ -39,6 +38,10 @@ class Client: self.tags = [] self.version = [0, 0, 0] + @property + def wants_item_notification(self): + return self.auth and "FoundItems" in self.tags + class Context: def __init__(self, host: str, port: int, password: str, location_check_points: int, hint_cost: int, @@ -135,7 +138,6 @@ def notify_hints(ctx: Context, team: int, hints: typing.List[Utils.Hint]): payload = texts asyncio.create_task(send_msgs(client.socket, payload)) - async def server(websocket, path, ctx: Context): client = Client(websocket) ctx.clients.append(client) @@ -210,7 +212,7 @@ def get_connected_players_string(ctx: Context): return f'{len(auth_clients)} players of {len(ctx.player_names)} connected ' + text[:-1] -def get_received_items(ctx: Context, team: int, player: int): +def get_received_items(ctx: Context, team: int, player: int) -> typing.List[ReceivedItem]: return ctx.received_items.setdefault((team, player), []) @@ -235,8 +237,6 @@ def forfeit_player(ctx: Context, team: int, slot: int): def register_location_checks(ctx: Context, team: int, slot: int, locations): - ctx.location_checks[team, slot] |= set(locations) - found_items = False for location in locations: if (location, slot) in ctx.locations: @@ -254,8 +254,17 @@ def register_location_checks(ctx: Context, team: int, slot: int, locations): recvd_items.append(new_item) if slot != target_player: broadcast_team(ctx, team, [['ItemSent', (slot, location, target_player, target_item)]]) - logging.info('(Team #%d) %s sent %s to %s (%s)' % (team+1, ctx.player_names[(team, slot)], get_item_name_from_id(target_item), ctx.player_names[(team, target_player)], get_location_name_from_address(location))) + logging.info('(Team #%d) %s sent %s to %s (%s)' % ( + team + 1, ctx.player_names[(team, slot)], get_item_name_from_id(target_item), + ctx.player_names[(team, target_player)], get_location_name_from_address(location))) found_items = True + elif target_player == slot: # local pickup, notify clients of the pickup + if location not in ctx.location_checks[team, slot]: + for client in ctx.clients: + if client.team == team and client.wants_item_notification: + asyncio.create_task( + send_msgs(client.socket, [['ItemFound', (target_item, location, slot)]])) + ctx.location_checks[team, slot] |= set(locations) send_new_items(ctx) if found_items: @@ -398,6 +407,12 @@ async def process_client_cmd(ctx: Context, client: Client, cmd, args): logging.info(f"{client.name} in team {client.team+1} scouted {', '.join([l[0] for l in locs])}") await send_msgs(client.socket, [['LocationInfo', [l[1:] for l in locs]]]) + if cmd == 'UpdateTags': + if not args or type(args) is not list: + await send_msgs(client.socket, [['InvalidArguments', 'UpdateTags']]) + return + client.tags = args + if cmd == 'Say': if type(args) is not str or not args.isprintable(): await send_msgs(client.socket, [['InvalidArguments', 'Say']]) @@ -605,26 +620,26 @@ async def forward_port(port: int): logging.info(f"Attempted to forward port {port} to {ip}, your local ip address.") -async def main(): +def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() - parser.add_argument('--host', default=None) - parser.add_argument('--port', default=38281, type=int) - parser.add_argument('--password', default=None) - parser.add_argument('--multidata', default=None) - parser.add_argument('--savefile', default=None) - parser.add_argument('--disable_save', default=False, action='store_true') - parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) - parser.add_argument('--location_check_points', default=1, type=int) - parser.add_argument('--hint_cost', default=1000, type=int) - parser.add_argument('--disable_item_cheat', default=False, action='store_true') - parser.add_argument('--port_forward', default=False, action='store_true') + defaults = Utils.get_options()["server_options"] + parser.add_argument('--host', default=defaults["host"]) + parser.add_argument('--port', default=defaults["port"], type=int) + parser.add_argument('--password', default=defaults["password"]) + parser.add_argument('--multidata', default=defaults["multidata"]) + parser.add_argument('--savefile', default=defaults["savefile"]) + parser.add_argument('--disable_save', default=defaults["disable_save"], action='store_true') + parser.add_argument('--loglevel', default=defaults["loglevel"], + choices=['debug', 'info', 'warning', 'error', 'critical']) + parser.add_argument('--location_check_points', default=defaults["location_check_points"], type=int) + parser.add_argument('--hint_cost', default=defaults["hint_cost"], type=int) + parser.add_argument('--disable_item_cheat', default=defaults["disable_item_cheat"], action='store_true') + parser.add_argument('--port_forward', default=defaults["port_forward"], action='store_true') args = parser.parse_args() + return args - if os.path.exists('host.yaml'): - file_options = Utils.parse_yaml(open("host.yaml").read())["server_options"] - for key, value in file_options.items(): - if value is not None: - setattr(args, key, value) + +async def main(args: argparse.Namespace): logging.basicConfig(format='[%(asctime)s] %(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO)) portforwardtask = None if args.port_forward: @@ -655,7 +670,7 @@ async def main(): logging.error('Failed to read multiworld data (%s)' % e) return - ip = Utils.get_public_ipv4() + ip = args.host if args.host else Utils.get_public_ipv4() @@ -686,5 +701,5 @@ async def main(): if __name__ == '__main__': loop = asyncio.get_event_loop() - loop.run_until_complete(main()) + loop.run_until_complete(main(parse_args())) loop.close() diff --git a/Mystery.py b/Mystery.py index ebca4f6e..114da3db 100644 --- a/Mystery.py +++ b/Mystery.py @@ -120,6 +120,8 @@ def main(): if path: try: settings = settings_cache[path] if settings_cache[path] else roll_settings(weights_cache[path]) + if settings.sprite is not None and not os.path.isfile(settings.sprite) and not get_sprite_from_name(settings.sprite): + logging.warning(f"Warning: The chosen sprite, \"{settings.sprite}\", for yaml \"{path}\", does not exist.") for k, v in vars(settings).items(): if v is not None: getattr(erargs, k)[player] = v @@ -271,7 +273,9 @@ def roll_settings(weights): startitems = [] for item in inventoryweights.keys(): itemvalue = get_choice(item, inventoryweights) - if item.startswith(('Progressive ', 'Small Key ', 'Rupee', 'Piece of Heart', 'Boss Heart Container', 'Sanctuary Heart Container', 'Arrow', 'Bombs ', 'Bomb ', 'Bottle')) and isinstance(itemvalue, int): + if item.startswith(('Progressive ', 'Small Key ', 'Rupee', 'Piece of Heart', 'Boss Heart Container', + 'Sanctuary Heart Container', 'Arrow', 'Bombs ', 'Bomb ', 'Bottle')) and isinstance( + itemvalue, int): for i in range(int(itemvalue)): startitems.append(item) elif itemvalue: @@ -280,11 +284,11 @@ def roll_settings(weights): startitems.append('Pegasus Boots') ret.startinventory = ','.join(startitems) + ret.remote_items = get_choice('remote_items', weights) if 'remote_items' in weights else False + if 'rom' in weights: romweights = weights['rom'] ret.sprite = get_choice('sprite', romweights) - if ret.sprite is not None and not os.path.isfile(ret.sprite) and not get_sprite_from_name(ret.sprite): - logging.Logger('').warning(f"Warning: The chosen sprite, \"{ret.sprite}\" does not exist.") ret.disablemusic = get_choice('disablemusic', romweights) ret.extendedmsu = get_choice('extendedmsu', romweights) ret.quickswap = get_choice('quickswap', romweights) diff --git a/Patch.py b/Patch.py index 8c6fca58..def899e8 100644 --- a/Patch.py +++ b/Patch.py @@ -3,7 +3,7 @@ import yaml import os import lzma import hashlib -from typing import Tuple +from typing import Tuple, Optional import Utils from Rom import JAP10HASH, read_rom @@ -11,11 +11,12 @@ from Rom import JAP10HASH, read_rom base_rom_bytes = None -def get_base_rom_bytes() -> bytes: +def get_base_rom_bytes(file_name: str = None) -> bytes: global base_rom_bytes if not base_rom_bytes: options = Utils.get_options() - file_name = options["general_options"]["rom_file"] + if not file_name: + file_name = options["general_options"]["rom_file"] if not os.path.exists(file_name): file_name = Utils.local_path(file_name) base_rom_bytes = bytes(read_rom(open(file_name, "rb"))) @@ -34,7 +35,7 @@ def generate_patch(rom: bytes, metadata=None) -> bytes: patch = bsdiff4.diff(get_base_rom_bytes(), rom) patch = yaml.dump({"meta": metadata, "patch": patch}) - return patch.encode() + return patch.encode(encoding="utf-8-sig") def create_patch_file(rom_file_to_patch: str, server: str = "") -> str: diff --git a/Plando.py b/Plando.py index 5b66876e..d864e8d4 100755 --- a/Plando.py +++ b/Plando.py @@ -20,7 +20,7 @@ from Main import create_playthrough __version__ = '0.2-dev' def main(args): - start_time = time.process_time() + start_time = time.perf_counter() # initialize the world world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, False, False, False, None, False) @@ -87,7 +87,7 @@ def main(args): world.spoiler.to_file('%s_Spoiler.txt' % outfilebase) logger.info('Done. Enjoy.') - logger.debug('Total Time: %s', time.process_time() - start_time) + logger.debug('Total Time: %s', time.perf_counter() - start_time) return world diff --git a/Rom.py b/Rom.py index 79b350e5..7a55dd9b 100644 --- a/Rom.py +++ b/Rom.py @@ -108,7 +108,7 @@ class LocalRom(object): basemd5.update(self.buffer) if JAP10HASH != basemd5.hexdigest(): logging.getLogger('').warning('Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.') - + # extend to 2MB self.buffer.extend(bytearray([0x00]) * (0x200000 - len(self.buffer))) @@ -716,8 +716,10 @@ def patch_rom(world, rom, player, team, enemized): [difficulty.progressive_sword_limit if world.swords[player] != 'swordless' else 0, overflow_replacement, difficulty.progressive_shield_limit, overflow_replacement, difficulty.progressive_armor_limit, overflow_replacement, - difficulty.progressive_bottle_limit, overflow_replacement, - difficulty.progressive_bow_limit, overflow_replacement]) + difficulty.progressive_bottle_limit, overflow_replacement]) + + #Work around for json patch ordering issues - write bow limit separately so that it is replaced in the patch + rom.write_bytes(0x180098, [difficulty.progressive_bow_limit, overflow_replacement]) if difficulty.progressive_bow_limit < 2 and world.swords[player] == 'swordless': rom.write_bytes(0x180098, [2, overflow_replacement]) @@ -1103,6 +1105,14 @@ def patch_rom(world, rom, player, team, enemized): rom.write_byte(0x18005E, world.crystals_needed_for_gt[player]) rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player]) + # Bitfield - enable text box to show with free roaming items + # + # ---o bmcs + # o - enabled for outside dungeon items + # b - enabled for inside big keys + # m - enabled for inside maps + # c - enabled for inside compasses + # s - enabled for inside small keys # block HC upstairs doors in rain state in standard mode rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' else 0x00) @@ -1120,6 +1130,14 @@ def patch_rom(world, rom, player, team, enemized): else: rom.write_byte(0x18003C, 0x00) + # Bitfield - enable free items to show up in menu + # + # ----dcba + # d - Compass + # c - Map + # b - Big Key + # a - Small Key + # rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] else 0x00) | (0x02 if world.bigkeyshuffle[player] else 0x00) | (0x04 if world.compassshuffle[player] else 0x00) @@ -1207,7 +1225,7 @@ def patch_rom(world, rom, player, team, enemized): rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required[player] else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F]) # allow smith into multi-entrance caves in appropriate shuffles - if world.shuffle[player] in ['restricted', 'full', 'crossed', 'insanity']: + if world.shuffle[player] in ['restricted', 'full', 'crossed', 'insanity'] or (world.shuffle == 'simple' and world.mode == 'inverted'): rom.write_byte(0x18004C, 0x01) # set correct flag for hera basement item @@ -1720,9 +1738,14 @@ def write_strings(rom, world, player, team): prog_bow_locs = world.find_items('Progressive Bow', player) distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None) + progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or world.swords[player] == 'swordless' if distinguished_prog_bow_loc: prog_bow_locs.remove(distinguished_prog_bow_loc) - silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) + silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!' + tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + + if any(prog_bow_locs): + silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!' tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint if any(prog_bow_locs): diff --git a/Rules.py b/Rules.py index d8c6e985..37b331b5 100644 --- a/Rules.py +++ b/Rules.py @@ -234,8 +234,7 @@ def global_rules(world, player): # you can squander the free small key from the pot by opening the south door to the north west switch room, locking you out of accessing a color switch ... # big key gives backdoor access to that from the teleporter in the north west set_rule(world.get_location('Misery Mire - Map Chest', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 1) or state.has('Big Key (Misery Mire)', player)) - # in addition, you can open the door to the map room before getting access to a color switch, so this is locked behing 2 small keys or the big key... - set_rule(world.get_location('Misery Mire - Main Lobby', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 2) or state.has_key('Big Key (Misery Mire)', player)) + set_rule(world.get_location('Misery Mire - Main Lobby', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 1) or state.has_key('Big Key (Misery Mire)', player)) # we can place a small key in the West wing iff it also contains/blocks the Big Key, as we cannot reach and softlock with the basement key door yet set_rule(world.get_entrance('Misery Mire (West)', player), lambda state: state.has_key('Small Key (Misery Mire)', player, 2) if ((item_name(state, 'Misery Mire - Compass Chest', player) in [('Big Key (Misery Mire)', player)]) or (item_name(state, 'Misery Mire - Big Key Chest', player) in [('Big Key (Misery Mire)', player)])) else state.has_key('Small Key (Misery Mire)', player, 3)) @@ -302,14 +301,14 @@ def global_rules(world, player): else: forbid_item(world.get_location('Ganons Tower - Map Chest', player), 'Small Key (Ganons Tower)', player) - # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere. We reflect this in the chest requirements. + # It is possible to need more than 2 keys to get through this entrance if you spend keys elsewhere. We reflect this in the chest requirements. # However we need to leave these at the lower values to derive that with 3 keys it is always possible to reach Bob and Ice Armos. set_rule(world.get_entrance('Ganons Tower (Double Switch Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 2)) # It is possible to need more than 3 keys .... set_rule(world.get_entrance('Ganons Tower (Firesnake Room)', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3)) #The actual requirements for these rooms to avoid key-lock - set_rule(world.get_location('Ganons Tower - Firesnake Room', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 2))) + set_rule(world.get_location('Ganons Tower - Firesnake Room', player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 3) or ((item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) or item_in_locations(state, 'Small Key (Ganons Tower)', player, [('Ganons Tower - Firesnake Room', player)])) and state.has_key('Small Key (Ganons Tower)', player, 2))) for location in randomizer_room_chests: set_rule(world.get_location(location, player), lambda state: state.has_key('Small Key (Ganons Tower)', player, 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', player, zip(randomizer_room_chests, [player] * len(randomizer_room_chests))) and state.has_key('Small Key (Ganons Tower)', player, 3))) @@ -467,8 +466,11 @@ def inverted_rules(world, player): world.get_region('Inverted Links House', player).entrances[0].can_reach = lambda state: True world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True - old_rule = world.get_region('Hyrule Castle Ledge', player).can_reach_private - world.get_region('Hyrule Castle Ledge', player).can_reach_private = lambda state: (state.has_Mirror(player) and state.has('Beat Agahnim 1', player) and state.can_reach_light_world(player)) or old_rule(state) + old_rule_old_man = world.get_region('Old Man House', player).can_reach_private + world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule_old_man(state) + + old_rule_castle_ledge = world.get_region('Hyrule Castle Ledge', player).can_reach_private + world.get_region('Hyrule Castle Ledge', player).can_reach_private = lambda state: (state.has_Mirror(player) and state.has('Beat Agahnim 1', player) and state.can_reach_light_world(player)) or old_rule_castle_ledge(state) # overworld requirements set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player)) @@ -508,7 +510,11 @@ def inverted_rules(world, player): set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player)) set_rule(world.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player)) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. Can be improved in future Todo - set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) or (state.can_reach('Light World', 'Region', player) and state.has_Mirror(player))) + set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) and (state.has_Pearl(player) or state.has('Beat Agahnim 1', player)) or (state.can_reach('Light World', 'Region', player) and state.has_Mirror(player))) # Need LW access using Mirror or Portal + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith + set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player) and state.has_Pearl(player)) + set_rule(world.get_location('Sick Kid', player), lambda state: state.has_bottle(player)) set_rule(world.get_location('Mushroom', player), lambda state: state.has_Pearl(player)) # need pearl to pick up bushes set_rule(world.get_entrance('Bush Covered Lawn Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Bush Covered Lawn Inner Bushes', player), lambda state: state.has_Pearl(player)) @@ -627,7 +633,9 @@ def no_glitches_rules(world, player): else: set_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player))) set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Lake Hylia Island', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to + set_rule(world.get_entrance('Lake Hylia Warp', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to + set_rule(world.get_entrance('Northeast Light World Warp', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) @@ -1058,6 +1066,7 @@ def set_big_bomb_rules(world, player): # means you need an escape route of either Flippers or Flute add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Flippers', player) or state.has('Flute', player)) and (basic_routes(state) or state.has_Mirror(player))) + def set_inverted_big_bomb_rules(world, player): bombshop_entrance = world.get_region('Inverted Big Bomb Shop', player).entrances[0] Normal_LW_entrances = ['Blinds Hideout', @@ -1106,36 +1115,37 @@ def set_inverted_big_bomb_rules(world, player): 'Hyrule Castle Entrance (East)', 'Inverted Ganons Tower', 'Cave 45', - 'Checkerboard Cave'] - LW_DM_entrances = ['Old Man Cave (East)', - 'Old Man House (Bottom)', - 'Old Man House (Top)', - 'Death Mountain Return Cave (East)', - 'Spectacle Rock Cave Peak', - 'Spectacle Rock Cave', - 'Spectacle Rock Cave (Bottom)', - 'Tower of Hera', - 'Death Mountain Return Cave (West)', - 'Paradox Cave (Top)', - 'Fairy Ascension Cave (Top)', - 'Spiral Cave', - 'Paradox Cave (Bottom)', - 'Paradox Cave (Middle)', - 'Hookshot Fairy', - 'Spiral Cave (Bottom)', - 'Mimic Cave', - 'Fairy Ascension Cave (Bottom)', - 'Desert Palace Entrance (West)', - 'Desert Palace Entrance (North)', - 'Desert Palace Entrance (South)'] + 'Checkerboard Cave', + 'Inverted Big Bomb Shop'] + Isolated_LW_entrances = ['Old Man Cave (East)', + 'Old Man House (Bottom)', + 'Old Man House (Top)', + 'Death Mountain Return Cave (East)', + 'Spectacle Rock Cave Peak', + 'Tower of Hera', + 'Death Mountain Return Cave (West)', + 'Paradox Cave (Top)', + 'Fairy Ascension Cave (Top)', + 'Spiral Cave', + 'Paradox Cave (Bottom)', + 'Paradox Cave (Middle)', + 'Hookshot Fairy', + 'Spiral Cave (Bottom)', + 'Mimic Cave', + 'Fairy Ascension Cave (Bottom)', + 'Desert Palace Entrance (West)', + 'Desert Palace Entrance (North)', + 'Desert Palace Entrance (South)'] + Eastern_DW_entrances = ['Palace of Darkness', + 'Palace of Darkness Hint', + 'Dark Lake Hylia Fairy', + 'East Dark World Hint'] Northern_DW_entrances = ['Brewery', 'C-Shaped House', 'Chest Game', 'Dark World Hammer Peg Cave', - 'Red Shield Shop', - 'Dark Sanctuary Hint', + 'Inverted Dark Sanctuary', 'Fortune Teller (Dark)', - 'Dark World Shop', 'Dark World Lumberjack Shop', 'Thieves Town', 'Skull Woods First Section Door', @@ -1143,7 +1153,7 @@ def set_inverted_big_bomb_rules(world, player): Southern_DW_entrances = ['Hype Cave', 'Bonk Fairy (Dark)', 'Archery Game', - 'Inverted Big Bomb Shop', + 'Inverted Links House', 'Dark Lake Hylia Shop', 'Swamp Palace'] Isolated_DW_entrances = ['Spike Cave', @@ -1160,23 +1170,24 @@ def set_inverted_big_bomb_rules(world, player): 'Hookshot Cave', 'Turtle Rock Isolated Ledge Entrance', 'Hookshot Cave Back Entrance', - 'Inverted Agahnims Tower', - 'Dark Lake Hylia Ledge Fairy', + 'Inverted Agahnims Tower'] + LW_walkable_entrances = ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', - 'Misery Mire'] + 'Misery Mire', + 'Red Shield Shop'] LW_bush_entrances = ['Bush Covered House', 'Light World Bomb Hut', 'Graveyard Cave'] - - set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Inverted Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) + LW_inaccessible_entrances = ['Desert Palace Entrance (East)', + 'Spectacle Rock Cave', + 'Spectacle Rock Cave (Bottom)'] - #crossing peg bridge starting from the southern dark world - def cross_peg_bridge(state): - return state.has('Hammer', player) + set_rule(world.get_entrance('Pyramid Fairy', player), + lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Inverted Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) # Key for below abbreviations: # P = pearl @@ -1184,40 +1195,46 @@ def set_inverted_big_bomb_rules(world, player): # H = hammer # M = Mirror # G = Glove - if bombshop_entrance.name in Normal_LW_entrances: + if bombshop_entrance.name in Eastern_DW_entrances: + # Just walk to the pyramid + pass + elif bombshop_entrance.name in Normal_LW_entrances: # Just walk to the castle and mirror. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player)) - elif bombshop_entrance.name in LW_DM_entrances: + elif bombshop_entrance.name in Isolated_LW_entrances: # For these entrances, you cannot walk to the castle/pyramid and thus must use Mirror and then Flute. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and state.has_Mirror(player)) elif bombshop_entrance.name in Northern_DW_entrances: # You can just fly with the Flute, you can take a long walk with Mitts and Hammer, # or you can leave a Mirror portal nearby and then walk to the castle to Mirror again. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name in Southern_DW_entrances: # This is the same as north DW without the Mitts rock present. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: cross_peg_bridge(state) or state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Hammer', player) or state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name in Isolated_DW_entrances: # There's just no way to escape these places with the bomb and no Flute. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player)) + elif bombshop_entrance.name in LW_walkable_entrances: + # You can fly with the flute, or leave a mirror portal and walk through the light world + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name in LW_bush_entrances: # These entrances are behind bushes in LW so you need either Pearl or the tools to solve NDW bomb shop locations. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and (state.can_flute(player) or state.has_Pearl(player) or (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and (state.can_flute(player) or state.has_Pearl(player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)))) + elif bombshop_entrance.name == 'Dark World Shop': + # This is mostly the same as NDW but the Mirror path requires the Pearl, or using the Hammer + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player) and (state.has_Pearl(player) or state.has('Hammer', player)))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': # This is mostly the same as NDW but the Mirror path requires being able to lift a rock. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has_Mirror(player) and state.can_lift_rocks(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_lift_rocks(player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name == 'Old Man Cave (West)': # The three paths back are Mirror and DW walk, Mirror and Flute, or LW walk and then Mirror. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and ((state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.can_lift_rocks(player) and state.has_Pearl(player)) or state.can_flute(player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and ((state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.can_lift_rocks(player) and state.has_Pearl(player)) or state.can_flute(player))) elif bombshop_entrance.name == 'Dark World Potion Shop': # You either need to Flute to 5 or cross the rock/hammer choice pass to the south. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or state.has('Hammer', player) or state.can_lift_rocks(player)) elif bombshop_entrance.name == 'Kings Grave': # Either lift the rock and walk to the castle to Mirror or Mirror immediately and Flute. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or state.can_lift_heavy_rocks(player)) and state.has_Mirror(player)) - elif bombshop_entrance.name == 'Two Brothers House (West)': - # First you must Mirror. Then you can either Flute, cross the peg bridge, or use the Agah 1 portal to Mirror again. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or cross_peg_bridge(state) or state.has('Beat Agahnim 1', player)) and state.has_Mirror(player)) elif bombshop_entrance.name == 'Waterfall of Wishing': # You absolutely must be able to swim to return it from here. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Flippers', player) and state.has_Pearl(player) and state.has_Mirror(player)) @@ -1227,6 +1244,19 @@ def set_inverted_big_bomb_rules(world, player): elif bombshop_entrance.name == 'Capacity Upgrade': # You must Mirror but then can use either Ice Palace return path. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Flippers', player) or state.can_flute(player)) and state.has_Mirror(player)) + # Either lift the rock and walk to the castle to Mirror or Mirror immediately and Flute. + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.has_Pearl(player) and state.can_lift_heavy_rocks(player))) and state.has_Mirror(player)) + elif bombshop_entrance.name == 'Two Brothers House (West)': + # First you must Mirror. Then you can either Flute, cross the peg bridge, or use the Agah 1 portal to Mirror again. + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) and state.has_Mirror(player)) + elif bombshop_entrance.name in LW_inaccessible_entrances: + # You can't get to the pyramid from these entrances without bomb duping. + raise Exception('No valid path to open Pyramid Fairy. (Could not route from %s)' % bombshop_entrance.name) + elif bombshop_entrance.name == 'Pyramid Fairy': + # Self locking. The shuffles don't put the bomb shop here, but doesn't lock anything important. + set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False) + else: + raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name) def set_bunny_rules(world, player): diff --git a/Utils.py b/Utils.py index ae024869..95c4ea05 100644 --- a/Utils.py +++ b/Utils.py @@ -1,5 +1,4 @@ import os -import re import subprocess import sys import typing @@ -49,14 +48,17 @@ def local_path(path): if local_path.cached_path: return os.path.join(local_path.cached_path, path) - elif is_bundled() and hasattr(sys, "_MEIPASS"): - # we are running in a PyInstaller bundle - local_path.cached_path = sys._MEIPASS # pylint: disable=protected-access,no-member - + elif is_bundled(): + if hasattr(sys, "_MEIPASS"): + # we are running in a PyInstaller bundle + local_path.cached_path = sys._MEIPASS # pylint: disable=protected-access,no-member + else: + # cx_Freeze + local_path.cached_path = os.path.dirname(os.path.abspath(sys.argv[0])) else: # we are running in a normal Python environment - # or cx_Freeze - local_path.cached_path = os.path.dirname(os.path.abspath(sys.argv[0])) + import __main__ + local_path.cached_path = os.path.dirname(os.path.abspath(__main__.__file__)) return os.path.join(local_path.cached_path, path) @@ -169,20 +171,16 @@ def get_public_ipv4() -> str: return ip -_options = None - - def get_options() -> dict: - global _options - if _options: - return _options - else: + if not hasattr(get_options, "options"): locations = ("options.yaml", "host.yaml", local_path("options.yaml"), local_path("host.yaml")) for location in locations: if os.path.exists(location): with open(location) as f: - _options = parse_yaml(f.read()) - return _options - raise FileNotFoundError(f"Could not find {locations[0]} to load options.") + get_options.options = parse_yaml(f.read()) + break + else: + raise FileNotFoundError(f"Could not find {locations[1]} to load options.") + return get_options.options diff --git a/host.yaml b/host.yaml index a9e99440..ad3b4ef0 100644 --- a/host.yaml +++ b/host.yaml @@ -1,26 +1,28 @@ general_options: #File name of the v1.0 J rom rom_file: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc" + # set this to your (Q)Usb2Snes location if you want the MultiClient to attempt an auto start, does nothing if not found + qusb2snes: "QUsb2Snes\\QUsb2Snes.exe" #options for MultiServer #null means nothing, for the server this means to default the value #these overwrite command line arguments! server_options: host: null - port: null + port: 38281 password: null multidata: null savefile: null - disable_save: null - loglevel: null + disable_save: false + loglevel: "info" #automatically forward the port that is used, then close that port after 24 hours - port_forward: null + port_forward: false #Disallow !getitem. Old /getitem cannot be blocked this way - disable_item_cheat: null + disable_item_cheat: false #Client hint system #points given to player for each acquired item location_check_points: 1 #point cost to receive a hint via !hint for players - hint_cost: 50 #set to 0 if you want free hints + hint_cost: 1000 #set to 0 if you want free hints #options for MultiMystery.py multi_mystery_options: #teams, however, note that there is currently no way to supply names for teams 2+ through MultiMystery @@ -50,7 +52,7 @@ multi_mystery_options: zip_spoiler: 0 #include the multidata file in the zip, 2 -> delete the non-zipped one, which also means the server won't autostart zip_multidata: 0 - #zip algorithm to use + #zip algorithm to use. zip is recommended for patch files, 7z is recommended for roms. All of them get the job done. zip_format: 1 # 1 -> zip, 2 -> 7z, 3->bz2 #create roms flagged as race roms race: 0 \ No newline at end of file diff --git a/icon.ico b/icon.ico new file mode 100644 index 00000000..797410ab Binary files /dev/null and b/icon.ico differ diff --git a/inno_setup.iss b/inno_setup.iss new file mode 100644 index 00000000..e74dc446 --- /dev/null +++ b/inno_setup.iss @@ -0,0 +1,116 @@ +#define sourcepath "build\exe.win-amd64-3.8\" +#define MyAppName "BerserkerMultiWorld" +#define MyAppExeName "BerserkerMultiClient.exe" +#define MyAppIcon "icon.ico" + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. +; Do not use the same AppId value in installers for other applications. +AppId={{6D826EE0-49BE-4B36-BACE-09C6971CD85C}} +AppName={#MyAppName} +AppVerName={#MyAppName} +DefaultDirName={commonappdata}\{#MyAppName} +DisableProgramGroupPage=yes +OutputDir=setups +OutputBaseFilename=Setup {#MyAppName} +Compression=lzma2 +SolidCompression=yes +LZMANumBlockThreads=8 +ArchitecturesInstallIn64BitMode=x64 +ChangesAssociations=yes +ArchitecturesAllowed=x64 +AllowNoIcons=yes +SetupIconFile={#MyAppIcon} +UninstallDisplayIcon={app}\{#MyAppExeName} +LicenseFile= LICENSE +WizardStyle= modern +SetupLogging=yes + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; + + +[Dirs] +NAME: "{app}"; Flags: setntfscompression; Permissions: everyone-modify users-modify authusers-modify; + +[Files] +Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external +Source: "{#sourcepath}*"; Excludes: "*.key, *.log, *.hpkey"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{group}\{#MyAppName} Folder"; Filename: "{app}"; +Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; +Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopicon +Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/passive /norestart"; Check: IsVCRedist64BitNeeded; StatusMsg: "Installing VC++ redistributable..." +; Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent + +[UninstallDelete] +Type: dirifempty; Name: "{app}" + +[Registry] + +Root: HKCR; Subkey: ".bmbp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "" +Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "{#MyAppName} Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "" +Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\{#MyAppExeName},0"; ValueType: string; ValueName: "" +Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; ValueType: string; ValueName: "" + + + +[Code] +// See: https://stackoverflow.com/a/51614652/2287576 +function IsVCRedist64BitNeeded(): boolean; +var + strVersion: string; +begin + if (RegQueryStringValue(HKEY_LOCAL_MACHINE, + 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Version', strVersion)) then + begin + // Is the installed version at least 14.24 ? + Log('VC Redist x64 Version : found ' + strVersion); + Result := (CompareStr(strVersion, 'v14.24.28127.4') < 0); + end + else + begin + // Not even an old version installed + Log('VC Redist x64 is not already installed'); + Result := True; + end; +end; + +var ROMFilePage: TInputFileWizardPage; +var R : longint; + +procedure InitializeWizard(); +begin + ROMFilePage := + CreateInputFilePage( + wpLicense, + 'Select ROM File', + 'Where is your Zelda no Densetsu - Kamigami no Triforce (Japan).sfc located?', + 'Select the file, then click Next.'); + + ROMFilePage.Add( + 'Location of ROM file:', + 'SNES ROM files|*.sfc|All files|*.*', + '.sfc'); +end; + +function GetROMPath(Param: string): string; +begin + if Assigned(RomFilePage) then begin + R := CompareStr(GetMD5OfFile(ROMFilePage.Values[0]), '03a63945398191337e896e5771f77173') + if R <> 0 then + MsgBox('ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); + Result := ROMFilePage.Values[0] + end + else + Result := ''; + end; diff --git a/requirements.txt b/requirements.txt index a2e16cbe..94956255 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ colorama>=0.4.3 websockets>=8.1 -PyYAML>=5.3 +PyYAML>=5.3.1 fuzzywuzzy>=0.18.0 bsdiff4>=1.1.9 upnpy>=1.1.5 -prompt_toolkit>=3.0.4 \ No newline at end of file +prompt_toolkit>=3.0.5 \ No newline at end of file diff --git a/setup.py b/setup.py index 05495896..7ead2143 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,6 @@ import shutil import sys import sysconfig from pathlib import Path - import cx_Freeze is_64bits = sys.maxsize > 2 ** 32 @@ -15,9 +14,8 @@ sbuildfolder = str(buildfolder) libfolder = Path(buildfolder, "lib") library = Path(libfolder, "library.zip") print("Outputting to: " + str(buildfolder)) -build_resources = "exe_resources" compress = False -holoviews = False +icon="icon.ico" from hashlib import sha3_512 import base64 @@ -54,7 +52,8 @@ exes = [] for script, scriptname in scripts.items(): exes.append(cx_Freeze.Executable( script=script, - targetName=scriptname + ("" if sys.platform == "linux" else ".exe")) + targetName=scriptname + ("" if sys.platform == "linux" else ".exe"), + icon=icon) ) @@ -63,9 +62,9 @@ import datetime buildtime = datetime.datetime.now() cx_Freeze.setup( - name="HonorarPlus", + name="BerserkerMultiWorld", version=f"{buildtime.year}.{buildtime.month}.{buildtime.day}.{buildtime.hour}", - description="HonorarPlus", + description="BerserkerMultiWorld", executables=exes, options={ "build_exe": { diff --git a/test/TestDeathMountain.py b/test/TestDeathMountain.py new file mode 100644 index 00000000..089ae52d --- /dev/null +++ b/test/TestDeathMountain.py @@ -0,0 +1,53 @@ +from BaseClasses import World +from Dungeons import create_dungeons +from EntranceShuffle import link_entrances +from ItemList import difficulties +from Regions import create_regions +from Rules import set_rules +from test.TestVanilla import TestVanilla + + +class TestDeathMountain(TestVanilla): + def setUp(self): + self.world = World(1, 'vanilla', 'noglitches', 'open', 'random', 'normal', 'normal', 'none', 'on', 'ganon', 'balanced', + True, False, False, False, False, False, False, False, False, None, + 'none', False) + self.world.difficulty_requirements = difficulties['normal'] + create_regions(self.world, 1) + create_dungeons(self.world, 1) + link_entrances(self.world, 1) + set_rules(self.world, 1) + + def testWestDeathMountain(self): + self.run_tests([ + ["Ether Tablet", False, []], + ["Ether Tablet", False, [], ['Progressive Glove', 'Ocarina']], + ["Ether Tablet", False, [], ['Lamp', 'Ocarina']], + ["Ether Tablet", False, [], ['Magic Mirror', 'Hookshot']], + ["Ether Tablet", False, [], ['Magic Mirror', 'Hammer']], + ["Ether Tablet", False, ['Progressive Sword'], ['Progressive Sword']], + ["Ether Tablet", False, [], ['Book of Mudora']], + ["Ether Tablet", True, ['Ocarina', 'Magic Mirror', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Ether Tablet", True, ['Progressive Glove', 'Lamp', 'Magic Mirror', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Ether Tablet", True, ['Ocarina', 'Hammer', 'Hookshot', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Ether Tablet", True, ['Progressive Glove', 'Lamp', 'Hammer', 'Hookshot', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + + ["Old Man", False, []], + ["Old Man", False, [], ['Progressive Glove', 'Ocarina']], + ["Old Man", False, [], ['Lamp']], + ["Old Man", True, ['Ocarina', 'Lamp']], + ["Old Man", True, ['Progressive Glove', 'Lamp']], + + ["Spectacle Rock Cave", False, []], + ["Spectacle Rock Cave", False, [], ['Progressive Glove', 'Ocarina']], + ["Spectacle Rock Cave", False, [], ['Lamp', 'Ocarina']], + ["Spectacle Rock Cave", True, ['Ocarina']], + ["Spectacle Rock Cave", True, ['Progressive Glove', 'Lamp']], + + ["Spectacle Rock", False, []], + ["Spectacle Rock", False, [], ['Progressive Glove', 'Ocarina']], + ["Spectacle Rock", False, [], ['Lamp', 'Ocarina']], + ["Spectacle Rock", False, [], ['Magic Mirror']], + ["Spectacle Rock", True, ['Ocarina', 'Magic Mirror']], + ["Spectacle Rock", True, ['Progressive Glove', 'Lamp', 'Magic Mirror']], + ]) diff --git a/test/TestVanilla.py b/test/TestVanilla.py new file mode 100644 index 00000000..3bf4a67e --- /dev/null +++ b/test/TestVanilla.py @@ -0,0 +1,41 @@ +import unittest + +from BaseClasses import World, CollectionState +from Dungeons import create_dungeons, get_dungeon_item_pool +from EntranceShuffle import link_entrances +from InvertedRegions import mark_dark_world_regions +from ItemList import difficulties +from Items import ItemFactory +from Regions import create_regions +from Rules import set_rules + + +class TestVanilla(unittest.TestCase): + def setUp(self): + self.world = World(1, 'vanilla', 'noglitches', 'open', 'random', 'normal', 'normal', 'none', 'on', 'ganon', 'balanced', + True, False, False, False, False, False, False, False, False, None, + 'none', False) + self.world.difficulty_requirements = difficulties['normal'] + create_regions(self.world, 1) + create_dungeons(self.world, 1) + link_entrances(self.world, 1) + mark_dark_world_regions(self.world) + set_rules(self.world, 1) + + def run_tests(self, access_pool): + for location, access, *item_pool in access_pool: + items = item_pool[0] + all_except = item_pool[1] if len(item_pool) > 1 else None + with self.subTest(location=location, access=access, items=items, all_except=all_except): + if all_except and len(all_except) > 0: + items = self.world.itempool[:] + items = [item for item in items if item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] + items.extend(ItemFactory(item_pool[0], 1)) + else: + items = ItemFactory(items, 1) + state = CollectionState(self.world) + for item in items: + item.advancement = True + state.collect(item) + + self.assertEqual(self.world.get_location(location, 1).can_reach(state), access) \ No newline at end of file diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/dungeons/TestAgahnimsTower.py b/test/dungeons/TestAgahnimsTower.py new file mode 100644 index 00000000..1c71d51b --- /dev/null +++ b/test/dungeons/TestAgahnimsTower.py @@ -0,0 +1,24 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestAgahnimsTower(TestDungeon): + + def testTower(self): + self.starting_regions = ['Agahnims Tower'] + self.run_tests([ + ["Castle Tower - Room 03", False, []], + ["Castle Tower - Room 03", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Room 03", True, ['Progressive Sword']], + + ["Castle Tower - Dark Maze", False, []], + ["Castle Tower - Dark Maze", False, [], ['Small Key (Agahnims Tower)']], + ["Castle Tower - Dark Maze", False, [], ['Lamp']], + ["Castle Tower - Dark Maze", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']], + ["Castle Tower - Dark Maze", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Lamp']], + + ["Agahnim 1", False, []], + ["Agahnim 1", False, ['Small Key (Agahnims Tower)'], ['Small Key (Agahnims Tower)']], + ["Agahnim 1", False, [], ['Progressive Sword']], + ["Agahnim 1", False, [], ['Lamp']], + ["Agahnim 1", True, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp', 'Progressive Sword']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestDarkPalace.py b/test/dungeons/TestDarkPalace.py new file mode 100644 index 00000000..48c1928e --- /dev/null +++ b/test/dungeons/TestDarkPalace.py @@ -0,0 +1,80 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestDarkPalace(TestDungeon): + + def testDarkPalace(self): + self.starting_regions = ['Palace of Darkness (Entrance)'] + key = 'Small Key (Palace of Darkness)' + self.run_tests([ + ["Palace of Darkness - Shooter Room", True, []], + + ["Palace of Darkness - The Arena - Ledge", False, []], + ["Palace of Darkness - The Arena - Ledge", False, [], ['Progressive Bow']], + ["Palace of Darkness - The Arena - Ledge", True, ['Progressive Bow']], + + ["Palace of Darkness - Map Chest", False, []], + ["Palace of Darkness - Map Chest", False, [], ['Progressive Bow']], + ["Palace of Darkness - Map Chest", True, ['Progressive Bow']], + + #Lower requirement for self-locking key + #No lower requirement when bow/hammer is out of logic + ["Palace of Darkness - Big Key Chest", False, []], + ["Palace of Darkness - Big Key Chest", False, [key]*5, [key]], + ["Palace of Darkness - Big Key Chest", True, [key]*6], + + ["Palace of Darkness - The Arena - Bridge", False, []], + ["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Progressive Bow']], + ["Palace of Darkness - The Arena - Bridge", False, [], [key, 'Hammer']], + ["Palace of Darkness - The Arena - Bridge", True, [key]], + ["Palace of Darkness - The Arena - Bridge", True, ['Progressive Bow', 'Hammer']], + + ["Palace of Darkness - Stalfos Basement", False, []], + ["Palace of Darkness - Stalfos Basement", False, [], [key, 'Progressive Bow']], + ["Palace of Darkness - Stalfos Basement", False, [], [key, 'Hammer']], + ["Palace of Darkness - Stalfos Basement", True, [key]], + ["Palace of Darkness - Stalfos Basement", True, ['Progressive Bow', 'Hammer']], + + ["Palace of Darkness - Compass Chest", False, []], + ["Palace of Darkness - Compass Chest", False, [key]*3, [key]], + ["Palace of Darkness - Compass Chest", True, [key]*4], + + #@todo: Advanced? + ["Palace of Darkness - Dark Basement - Left", False, []], + ["Palace of Darkness - Dark Basement - Left", False, [], ['Lamp']], + ["Palace of Darkness - Dark Basement - Left", False, [key]*3, [key]], + ["Palace of Darkness - Dark Basement - Left", True, ['Lamp'] + [key]*4], + + ["Palace of Darkness - Dark Basement - Right", False, []], + ["Palace of Darkness - Dark Basement - Right", False, [], ['Lamp']], + ["Palace of Darkness - Dark Basement - Right", False, [key] * 3, [key]], + ["Palace of Darkness - Dark Basement - Right", True, ['Lamp'] + [key] * 4], + + ["Palace of Darkness - Harmless Hellway", False, []], + ["Palace of Darkness - Harmless Hellway", False, [key]*5, [key]], + ["Palace of Darkness - Harmless Hellway", True, [key]*6], + + ["Palace of Darkness - Dark Maze - Top", False, []], + ["Palace of Darkness - Dark Maze - Top", False, [], ['Lamp']], + ["Palace of Darkness - Dark Maze - Top", False, [key]*5, [key]], + ["Palace of Darkness - Dark Maze - Top", True, ['Lamp'] + [key]*6], + + ["Palace of Darkness - Dark Maze - Bottom", False, []], + ["Palace of Darkness - Dark Maze - Bottom", False, [], ['Lamp']], + ["Palace of Darkness - Dark Maze - Bottom", False, [key]*5, [key]], + ["Palace of Darkness - Dark Maze - Bottom", True, ['Lamp'] + [key]*6], + + ["Palace of Darkness - Big Chest", False, []], + ["Palace of Darkness - Big Chest", False, [], ['Lamp']], + ["Palace of Darkness - Big Chest", False, [], ['Big Key (Palace of Darkness)']], + ["Palace of Darkness - Big Chest", False, [key]*5, [key]], + ["Palace of Darkness - Big Chest", True, ['Lamp', 'Big Key (Palace of Darkness)'] + [key]*6], + + ["Palace of Darkness - Boss", False, []], + ["Palace of Darkness - Boss", False, [], ['Lamp']], + ["Palace of Darkness - Boss", False, [], ['Hammer']], + ["Palace of Darkness - Boss", False, [], ['Progressive Bow']], + ["Palace of Darkness - Boss", False, [], ['Big Key (Palace of Darkness)']], + ["Palace of Darkness - Boss", False, [key]*5, [key]], + ["Palace of Darkness - Boss", True, ['Lamp', 'Hammer', 'Progressive Bow', 'Big Key (Palace of Darkness)'] + [key]*6], + ]) \ No newline at end of file diff --git a/test/dungeons/TestDesertPalace.py b/test/dungeons/TestDesertPalace.py new file mode 100644 index 00000000..82255146 --- /dev/null +++ b/test/dungeons/TestDesertPalace.py @@ -0,0 +1,39 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestDesertPalace(TestDungeon): + + def testDesertPalace(self): + self.starting_regions = ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)'] + self.run_tests([ + ["Desert Palace - Map Chest", True, []], + + ["Desert Palace - Big Chest", False, []], + ["Desert Palace - Big Chest", False, [], ['Big Key (Desert Palace)']], + ["Desert Palace - Big Chest", True, ['Big Key (Desert Palace)']], + + ["Desert Palace - Torch", False, []], + ["Desert Palace - Torch", False, [], ['Pegasus Boots']], + ["Desert Palace - Torch", True, ['Pegasus Boots']], + + ["Desert Palace - Compass Chest", False, []], + ["Desert Palace - Compass Chest", False, [], ['Small Key (Desert Palace)']], + ["Desert Palace - Compass Chest", True, ['Small Key (Desert Palace)']], + + #@todo: Require a real weapon for enemizer? + ["Desert Palace - Big Key Chest", False, []], + ["Desert Palace - Big Key Chest", False, [], ['Small Key (Desert Palace)']], + ["Desert Palace - Big Key Chest", True, ['Small Key (Desert Palace)']], + + ["Desert Palace - Boss", False, []], + ["Desert Palace - Boss", False, [], ['Small Key (Desert Palace)']], + ["Desert Palace - Boss", False, [], ['Big Key (Desert Palace)']], + ["Desert Palace - Boss", False, [], ['Lamp', 'Fire Rod']], + ["Desert Palace - Boss", False, [], ['Progressive Sword', 'Hammer', 'Fire Rod', 'Ice Rod', 'Progressive Bow', 'Cane of Somaria', 'Cane of Byrna']], + ["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Fire Rod']], + ["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Progressive Sword']], + ["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Hammer']], + ["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Ice Rod']], + ["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Cane of Somaria']], + ["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Cane of Byrna']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestDungeon.py b/test/dungeons/TestDungeon.py new file mode 100644 index 00000000..1eb3cc7b --- /dev/null +++ b/test/dungeons/TestDungeon.py @@ -0,0 +1,49 @@ +import unittest + +from BaseClasses import World, CollectionState +from Dungeons import create_dungeons, get_dungeon_item_pool +from EntranceShuffle import mandatory_connections, connect_simple +from ItemList import difficulties, generate_itempool +from Items import ItemFactory +from Regions import create_regions +from Rules import set_rules + + +class TestDungeon(unittest.TestCase): + def setUp(self): + self.world = World(1, 'vanilla', 'noglitches', 'open', 'random', 'normal', 'normal', 'none', 'on', 'ganon', 'balanced', + True, False, False, False, False, False, False, False, False, None, + 'none', False) + self.starting_regions = [] + self.world.difficulty_requirements = difficulties['normal'] + create_regions(self.world, 1) + create_dungeons(self.world, 1) + for exitname, regionname in mandatory_connections: + connect_simple(self.world, exitname, regionname, 1) + connect_simple(self.world, self.world.get_entrance('Big Bomb Shop', 1), self.world.get_region('Big Bomb Shop', 1), 1) + self.world.swamp_patch_required[1] = True + set_rules(self.world, 1) + generate_itempool(self.world, 1) + self.world.itempool.extend(get_dungeon_item_pool(self.world)) + self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + + def run_tests(self, access_pool): + for region in self.starting_regions: + self.world.get_region(region, 1).can_reach_private = lambda _: True + + for location, access, *item_pool in access_pool: + items = item_pool[0] + all_except = item_pool[1] if len(item_pool) > 1 else None + with self.subTest(location=location, access=access, items=items, all_except=all_except): + if all_except and len(all_except) > 0: + items = self.world.itempool[:] + items = [item for item in items if item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] + items.extend(ItemFactory(item_pool[0], 1)) + else: + items = ItemFactory(items, 1) + state = CollectionState(self.world) + for item in items: + item.advancement = True + state.collect(item) + + self.assertEqual(self.world.get_location(location, 1).can_reach(state), access) \ No newline at end of file diff --git a/test/dungeons/TestEasternPalace.py b/test/dungeons/TestEasternPalace.py new file mode 100644 index 00000000..775fa8a1 --- /dev/null +++ b/test/dungeons/TestEasternPalace.py @@ -0,0 +1,29 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestEasternPalace(TestDungeon): + + def testEastern(self): + self.starting_regions = ["Eastern Palace"] + self.run_tests([ + ["Eastern Palace - Compass Chest", True, []], + + ["Eastern Palace - Cannonball Chest", True, []], + + ["Eastern Palace - Big Chest", False, []], + ["Eastern Palace - Big Chest", False, [], ['Big Key (Eastern Palace)']], + ["Eastern Palace - Big Chest", True, ['Big Key (Eastern Palace)']], + + ["Eastern Palace - Map Chest", True, []], + + ["Eastern Palace - Big Key Chest", False, []], + ["Eastern Palace - Big Key Chest", False, [], ['Lamp']], + ["Eastern Palace - Big Key Chest", True, ['Lamp']], + + #@todo: Advanced? + ["Eastern Palace - Boss", False, []], + ["Eastern Palace - Boss", False, [], ['Lamp']], + ["Eastern Palace - Boss", False, [], ['Progressive Bow']], + ["Eastern Palace - Boss", False, [], ['Big Key (Eastern Palace)']], + ["Eastern Palace - Boss", True, ['Lamp', 'Progressive Bow', 'Big Key (Eastern Palace)']] + ]) diff --git a/test/dungeons/TestGanonsTower.py b/test/dungeons/TestGanonsTower.py new file mode 100644 index 00000000..1517dc25 --- /dev/null +++ b/test/dungeons/TestGanonsTower.py @@ -0,0 +1,144 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestGanonsTower(TestDungeon): + + def testGanonsTower(self): + self.starting_regions = ['Ganons Tower (Entrance)'] + self.run_tests([ + ["Ganons Tower - Bob's Torch", False, []], + ["Ganons Tower - Bob's Torch", False, [], ['Pegasus Boots']], + ["Ganons Tower - Bob's Torch", True, ['Pegasus Boots']], + + ["Ganons Tower - DMs Room - Top Left", False, []], + ["Ganons Tower - DMs Room - Top Left", False, [], ['Hammer']], + ["Ganons Tower - DMs Room - Top Left", False, [], ['Hookshot']], + ["Ganons Tower - DMs Room - Top Left", True, ['Hookshot', 'Hammer']], + + ["Ganons Tower - DMs Room - Top Right", False, []], + ["Ganons Tower - DMs Room - Top Right", False, [], ['Hammer']], + ["Ganons Tower - DMs Room - Top Right", False, [], ['Hookshot']], + ["Ganons Tower - DMs Room - Top Right", True, ['Hookshot', 'Hammer']], + + ["Ganons Tower - DMs Room - Bottom Left", False, []], + ["Ganons Tower - DMs Room - Bottom Left", False, [], ['Hammer']], + ["Ganons Tower - DMs Room - Bottom Left", False, [], ['Hookshot']], + ["Ganons Tower - DMs Room - Bottom Left", True, ['Hookshot', 'Hammer']], + + ["Ganons Tower - DMs Room - Bottom Right", False, []], + ["Ganons Tower - DMs Room - Bottom Right", False, [], ['Hammer']], + ["Ganons Tower - DMs Room - Bottom Right", False, [], ['Hookshot']], + ["Ganons Tower - DMs Room - Bottom Right", True, ['Hookshot', 'Hammer']], + + ["Ganons Tower - Randomizer Room - Top Left", False, []], + ["Ganons Tower - Randomizer Room - Top Left", False, [], ['Hammer']], + ["Ganons Tower - Randomizer Room - Top Left", False, [], ['Hookshot']], + ["Ganons Tower - Randomizer Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Randomizer Room - Top Right", False, []], + ["Ganons Tower - Randomizer Room - Top Right", False, [], ['Hammer']], + ["Ganons Tower - Randomizer Room - Top Right", False, [], ['Hookshot']], + ["Ganons Tower - Randomizer Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Randomizer Room - Bottom Left", False, []], + ["Ganons Tower - Randomizer Room - Bottom Left", False, [], ['Hammer']], + ["Ganons Tower - Randomizer Room - Bottom Left", False, [], ['Hookshot']], + ["Ganons Tower - Randomizer Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Randomizer Room - Bottom Right", False, []], + ["Ganons Tower - Randomizer Room - Bottom Right", False, [], ['Hammer']], + ["Ganons Tower - Randomizer Room - Bottom Right", False, [], ['Hookshot']], + ["Ganons Tower - Randomizer Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Firesnake Room", False, []], + ["Ganons Tower - Firesnake Room", False, [], ['Hammer']], + ["Ganons Tower - Firesnake Room", False, [], ['Hookshot']], + ["Ganons Tower - Firesnake Room", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Map Chest", False, []], + ["Ganons Tower - Map Chest", False, [], ['Hammer']], + ["Ganons Tower - Map Chest", False, [], ['Hookshot', 'Pegasus Boots']], + ["Ganons Tower - Map Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + ["Ganons Tower - Map Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hammer', 'Pegasus Boots']], + + ["Ganons Tower - Big Chest", False, []], + ["Ganons Tower - Big Chest", False, [], ['Big Key (Ganons Tower)']], + ["Ganons Tower - Big Chest", True, ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Chest", True, ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Hope Room - Left", True, []], + + ["Ganons Tower - Hope Room - Right", True, []], + + ["Ganons Tower - Bob's Chest", False, []], + ["Ganons Tower - Bob's Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Bob's Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Tile Room", False, []], + ["Ganons Tower - Tile Room", False, [], ['Cane of Somaria']], + ["Ganons Tower - Tile Room", True, ['Cane of Somaria']], + + ["Ganons Tower - Compass Room - Top Left", False, []], + ["Ganons Tower - Compass Room - Top Left", False, [], ['Cane of Somaria']], + ["Ganons Tower - Compass Room - Top Left", False, [], ['Fire Rod']], + ["Ganons Tower - Compass Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']], + + ["Ganons Tower - Compass Room - Top Right", False, []], + ["Ganons Tower - Compass Room - Top Right", False, [], ['Cane of Somaria']], + ["Ganons Tower - Compass Room - Top Right", False, [], ['Fire Rod']], + ["Ganons Tower - Compass Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']], + + ["Ganons Tower - Compass Room - Bottom Left", False, []], + ["Ganons Tower - Compass Room - Bottom Left", False, [], ['Cane of Somaria']], + ["Ganons Tower - Compass Room - Bottom Left", False, [], ['Fire Rod']], + ["Ganons Tower - Compass Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']], + + ["Ganons Tower - Compass Room - Bottom Right", False, []], + ["Ganons Tower - Compass Room - Bottom Right", False, [], ['Cane of Somaria']], + ["Ganons Tower - Compass Room - Bottom Right", False, [], ['Fire Rod']], + ["Ganons Tower - Compass Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']], + + ["Ganons Tower - Big Key Chest", False, []], + ["Ganons Tower - Big Key Chest", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Key Chest", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Big Key Room - Left", False, []], + ["Ganons Tower - Big Key Room - Left", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Key Room - Left", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Big Key Room - Right", False, []], + ["Ganons Tower - Big Key Room - Right", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']], + ["Ganons Tower - Big Key Room - Right", True, ['Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']], + + ["Ganons Tower - Mini Helmasaur Room - Left", False, []], + ["Ganons Tower - Mini Helmasaur Room - Left", False, [], ['Progressive Bow']], + ["Ganons Tower - Mini Helmasaur Room - Left", False, [], ['Big Key (Ganons Tower)']], + ["Ganons Tower - Mini Helmasaur Room - Left", False, [], ['Lamp', 'Fire Rod']], + ["Ganons Tower - Mini Helmasaur Room - Left", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp']], + ["Ganons Tower - Mini Helmasaur Room - Left", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod']], + + ["Ganons Tower - Mini Helmasaur Room - Right", False, []], + ["Ganons Tower - Mini Helmasaur Room - Right", False, [], ['Progressive Bow']], + ["Ganons Tower - Mini Helmasaur Room - Right", False, [], ['Big Key (Ganons Tower)']], + ["Ganons Tower - Mini Helmasaur Room - Right", False, [], ['Lamp', 'Fire Rod']], + ["Ganons Tower - Mini Helmasaur Room - Right", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp']], + ["Ganons Tower - Mini Helmasaur Room - Right", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod']], + + ["Ganons Tower - Pre-Moldorm Chest", False, []], + ["Ganons Tower - Pre-Moldorm Chest", False, [], ['Progressive Bow']], + ["Ganons Tower - Pre-Moldorm Chest", False, [], ['Big Key (Ganons Tower)']], + ["Ganons Tower - Pre-Moldorm Chest", False, [], ['Lamp', 'Fire Rod']], + ["Ganons Tower - Pre-Moldorm Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp']], + ["Ganons Tower - Pre-Moldorm Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod']], + + ["Ganons Tower - Validation Chest", False, []], + ["Ganons Tower - Validation Chest", False, [], ['Hookshot']], + ["Ganons Tower - Validation Chest", False, [], ['Progressive Bow']], + ["Ganons Tower - Validation Chest", False, [], ['Big Key (Ganons Tower)']], + ["Ganons Tower - Validation Chest", False, [], ['Lamp', 'Fire Rod']], + ["Ganons Tower - Validation Chest", False, [], ['Progressive Sword', 'Hammer']], + ["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp', 'Hookshot', 'Progressive Sword']], + ["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Hookshot', 'Progressive Sword']], + ["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp', 'Hookshot', 'Hammer']], + ["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Hookshot', 'Hammer']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestIcePalace.py b/test/dungeons/TestIcePalace.py new file mode 100644 index 00000000..e415c5d0 --- /dev/null +++ b/test/dungeons/TestIcePalace.py @@ -0,0 +1,79 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestIcePalace(TestDungeon): + + def testIcePalace(self): + self.starting_regions = ['Ice Palace (Entrance)'] + self.run_tests([ + ["Ice Palace - Big Key Chest", False, []], + ["Ice Palace - Big Key Chest", False, [], ['Hammer']], + ["Ice Palace - Big Key Chest", False, [], ['Progressive Glove']], + ["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + #@todo: Change from item randomizer - Right side key door is only in logic if big key is in there + #["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cape', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cape', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + + ["Ice Palace - Compass Chest", False, []], + ["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Compass Chest", True, ['Fire Rod']], + ["Ice Palace - Compass Chest", True, ['Bombos', 'Progressive Sword']], + + ["Ice Palace - Map Chest", False, []], + ["Ice Palace - Map Chest", False, [], ['Hammer']], + ["Ice Palace - Map Chest", False, [], ['Progressive Glove']], + ["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Map Chest", True, ['Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Map Chest", True, ['Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']], + #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cape', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cape', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + + ["Ice Palace - Spike Room", False, []], + ["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Spike Room", True, ['Fire Rod', 'Hookshot', 'Small Key (Ice Palace)']], + ["Ice Palace - Spike Room", True, ['Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)']], + #["Ice Palace - Spike Room", True, ['Cape', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Spike Room", True, ['Cape', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Spike Room", True, ['Cane of Byrna', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + #["Ice Palace - Spike Room", True, ['Cane of Byrna', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + + ["Ice Palace - Freezor Chest", False, []], + ["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Freezor Chest", True, ['Fire Rod']], + ["Ice Palace - Freezor Chest", True, ['Bombos', 'Progressive Sword']], + + ["Ice Palace - Iced T Room", False, []], + ["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Iced T Room", True, ['Fire Rod']], + ["Ice Palace - Iced T Room", True, ['Bombos', 'Progressive Sword']], + + ["Ice Palace - Big Chest", False, []], + ["Ice Palace - Big Chest", False, [], ['Big Key (Ice Palace)']], + ["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Big Chest", True, ['Big Key (Ice Palace)', 'Fire Rod']], + ["Ice Palace - Big Chest", True, ['Big Key (Ice Palace)', 'Bombos', 'Progressive Sword']], + + ["Ice Palace - Boss", False, []], + ["Ice Palace - Boss", False, [], ['Hammer']], + ["Ice Palace - Boss", False, [], ['Progressive Glove']], + ["Ice Palace - Boss", False, [], ['Big Key (Ice Palace)']], + ["Ice Palace - Boss", False, [], ['Fire Rod', 'Bombos']], + ["Ice Palace - Boss", False, [], ['Fire Rod', 'Progressive Sword']], + ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)']], + ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']], + ["Ice Palace - Boss", True, ['Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestMiseryMire.py b/test/dungeons/TestMiseryMire.py new file mode 100644 index 00000000..5f0a354c --- /dev/null +++ b/test/dungeons/TestMiseryMire.py @@ -0,0 +1,84 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestMiseryMire(TestDungeon): + + def testMiseryMire(self): + self.starting_regions = ['Misery Mire (Entrance)'] + self.run_tests([ + ["Misery Mire - Bridge Chest", False, []], + ["Misery Mire - Bridge Chest", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Bridge Chest", False, [], ['Progressive Sword', 'Hammer', 'Fire Rod', 'Cane of Somaria', 'Progressive Bow', 'Ice Rod']], #Ice Rod works! + ["Misery Mire - Bridge Chest", True, ['Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Bridge Chest", True, ['Progressive Sword', 'Hookshot']], + ["Misery Mire - Bridge Chest", True, ['Hammer', 'Pegasus Boots']], + ["Misery Mire - Bridge Chest", True, ['Hammer', 'Hookshot']], + ["Misery Mire - Bridge Chest", True, ['Fire Rod', 'Pegasus Boots']], + ["Misery Mire - Bridge Chest", True, ['Fire Rod', 'Hookshot']], + ["Misery Mire - Bridge Chest", True, ['Cane of Somaria', 'Pegasus Boots']], + ["Misery Mire - Bridge Chest", True, ['Cane of Somaria', 'Hookshot']], + ["Misery Mire - Bridge Chest", True, ['Progressive Bow', 'Pegasus Boots']], + ["Misery Mire - Bridge Chest", True, ['Progressive Bow', 'Hookshot']], + ["Misery Mire - Bridge Chest", True, ['Ice Rod', 'Pegasus Boots']], + ["Misery Mire - Bridge Chest", True, ['Ice Rod', 'Hookshot']], + + ["Misery Mire - Big Chest", False, []], + ["Misery Mire - Big Chest", False, [], ['Big Key (Misery Mire)']], + ["Misery Mire - Big Chest", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Big Chest", False, [], ['Progressive Sword', 'Hammer', 'Fire Rod', 'Cane of Somaria', 'Progressive Bow', 'Ice Rod']], + ["Misery Mire - Big Chest", True, ['Big Key (Misery Mire)', 'Pegasus Boots', 'Progressive Sword']], + ["Misery Mire - Big Chest", True, ['Big Key (Misery Mire)', 'Hookshot', 'Progressive Sword']], + + ["Misery Mire - Main Lobby", False, []], + ["Misery Mire - Main Lobby", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Main Lobby", False, [], ['Small Key (Misery Mire)', 'Big Key (Misery Mire)']], + ["Misery Mire - Main Lobby", True, ['Small Key (Misery Mire)', 'Hookshot', 'Progressive Sword']], + ["Misery Mire - Main Lobby", True, ['Small Key (Misery Mire)', 'Pegasus Boots', 'Progressive Sword']], + ["Misery Mire - Main Lobby", True, ['Big Key (Misery Mire)', 'Hookshot', 'Progressive Sword']], + ["Misery Mire - Main Lobby", True, ['Big Key (Misery Mire)', 'Pegasus Boots', 'Progressive Sword']], + + ["Misery Mire - Big Key Chest", False, []], + ["Misery Mire - Big Key Chest", False, [], ['Fire Rod', 'Lamp']], + ["Misery Mire - Big Key Chest", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Big Key Chest", False, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)'], ['Small Key (Misery Mire)']], + ["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Hookshot']], + ["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Hookshot']], + + ["Misery Mire - Compass Chest", False, []], + ["Misery Mire - Compass Chest", False, [], ['Fire Rod', 'Lamp']], + ["Misery Mire - Compass Chest", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Compass Chest", False, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)'], ['Small Key (Misery Mire)']], + ["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Hookshot']], + ["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Hookshot']], + + ["Misery Mire - Map Chest", False, []], + ["Misery Mire - Map Chest", False, [], ['Small Key (Misery Mire)', 'Big Key (Misery Mire)']], + ["Misery Mire - Map Chest", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Map Chest", True, ['Small Key (Misery Mire)', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Map Chest", True, ['Small Key (Misery Mire)', 'Progressive Sword', 'Hookshot']], + ["Misery Mire - Map Chest", True, ['Big Key (Misery Mire)', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Map Chest", True, ['Big Key (Misery Mire)', 'Progressive Sword', 'Hookshot']], + + ["Misery Mire - Spike Chest", False, []], + ["Misery Mire - Spike Chest", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Spike Chest", True, ['Progressive Sword', 'Pegasus Boots', 'Cape']], + ["Misery Mire - Spike Chest", True, ['Progressive Sword', 'Hookshot', 'Cape']], + ["Misery Mire - Spike Chest", True, ['Progressive Sword', 'Pegasus Boots', 'Cane of Byrna']], + ["Misery Mire - Spike Chest", True, ['Progressive Sword', 'Hookshot', 'Cane of Byrna']], + ["Misery Mire - Spike Chest", True, ['Progressive Sword', 'Pegasus Boots', 'Boss Heart Container']], + ["Misery Mire - Spike Chest", True, ['Progressive Sword', 'Hookshot', 'Boss Heart Container']], + + ["Misery Mire - Boss", False, []], + ["Misery Mire - Boss", False, [], ['Lamp']], + ["Misery Mire - Boss", False, [], ['Cane of Somaria']], + ["Misery Mire - Boss", False, [], ['Progressive Sword', 'Hammer', 'Progressive Bow']], + ["Misery Mire - Boss", False, [], ['Big Key (Misery Mire)']], + ["Misery Mire - Boss", False, [], ['Pegasus Boots', 'Hookshot']], + ["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']], + ["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']], + ["Misery Mire - Boss", True, ['Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestSkullWoods.py b/test/dungeons/TestSkullWoods.py new file mode 100644 index 00000000..01ab4b58 --- /dev/null +++ b/test/dungeons/TestSkullWoods.py @@ -0,0 +1,96 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestSkullWoods(TestDungeon): + + def testSkullWoodsFrontAllEntrances(self): + self.starting_regions = ['Skull Woods First Section', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'] + self.run_tests([ + ["Skull Woods - Big Chest", False, []], + ["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']], + + ["Skull Woods - Compass Chest", True, []], + + ["Skull Woods - Map Chest", True, []], + + ["Skull Woods - Pot Prison", True, []], + + ["Skull Woods - Pinball Room", True, []] + ]) + + def testSkullWoodsFrontOnly(self): + self.starting_regions = ['Skull Woods First Section'] + self.run_tests([ + ["Skull Woods - Big Chest", False, []], + ["Skull Woods - Big Chest", False, [], ['Never in logic']], + + ["Skull Woods - Compass Chest", False, []], + ["Skull Woods - Compass Chest", False, ['Small Key (Skull Woods)'], ['Small Key (Skull Woods)']], + ["Skull Woods - Compass Chest", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)']], + + ["Skull Woods - Map Chest", True, []], + + ["Skull Woods - Pot Prison", False, []], + ["Skull Woods - Pot Prison", False, ['Small Key (Skull Woods)'], ['Small Key (Skull Woods)']], + ["Skull Woods - Pot Prison", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)']], + + ["Skull Woods - Pinball Room", False, []], + ["Skull Woods - Pinball Room", False, [], ['Small Key (Skull Woods)']], + ["Skull Woods - Pinball Room", True, ['Small Key (Skull Woods)']] + ]) + + def testSkullWoodsLeftOnly(self): + self.starting_regions = ['Skull Woods First Section (Left)'] + self.run_tests([ + ["Skull Woods - Big Chest", False, []], + ["Skull Woods - Big Chest", False, [], ['Never in logic']], + + ["Skull Woods - Compass Chest", True, []], + + ["Skull Woods - Map Chest", False, []], + ["Skull Woods - Map Chest", False, [], ['Small Key (Skull Woods)']], + ["Skull Woods - Map Chest", True, ['Small Key (Skull Woods)']], + + ["Skull Woods - Pot Prison", True, []], + + ["Skull Woods - Pinball Room", True, []] + ]) + + def testSkullWoodsBackOnly(self): + self.starting_regions = ['Skull Woods First Section (Top)'] + self.run_tests([ + ["Skull Woods - Big Chest", False, []], + ["Skull Woods - Big Chest", False, [], ['Big Key (Skull Woods)']], + ["Skull Woods - Big Chest", True, ['Big Key (Skull Woods)']], + + ["Skull Woods - Compass Chest", False, []], + ["Skull Woods - Compass Chest", False, ['Small Key (Skull Woods)'], ['Small Key (Skull Woods)']], + ["Skull Woods - Compass Chest", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)']], + + ["Skull Woods - Map Chest", True, []], + + ["Skull Woods - Pot Prison", False, []], + ["Skull Woods - Pot Prison", False, ['Small Key (Skull Woods)'], ['Small Key (Skull Woods)']], + ["Skull Woods - Pot Prison", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)']], + + ["Skull Woods - Pinball Room", False, []], + ["Skull Woods - Pinball Room", False, [], ['Small Key (Skull Woods)']], + ["Skull Woods - Pinball Room", True, ['Small Key (Skull Woods)']] + ]) + + def testSkullWoodsMiddle(self): + self.starting_regions = ['Skull Woods Second Section'] + self.run_tests([["Skull Woods - Big Key Chest", True, []]]) + + def testSkullWoodsBack(self): + self.starting_regions = ['Skull Woods Final Section (Entrance)'] + self.run_tests([ + ["Skull Woods - Bridge Room", True, []], + + ["Skull Woods - Boss", False, []], + ["Skull Woods - Boss", False, [], ['Fire Rod']], + ["Skull Woods - Boss", False, [], ['Progressive Sword']], + ["Skull Woods - Boss", False, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)'], ['Small Key (Skull Woods)']], + ["Skull Woods - Boss", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Fire Rod', 'Progressive Sword']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestSwampPalace.py b/test/dungeons/TestSwampPalace.py new file mode 100644 index 00000000..534939c9 --- /dev/null +++ b/test/dungeons/TestSwampPalace.py @@ -0,0 +1,80 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestSwampPalace(TestDungeon): + + def testSwampPalace(self): + self.starting_regions = ['Swamp Palace (Entrance)'] + self.run_tests([ + ["Swamp Palace - Entrance", False, []], + ["Swamp Palace - Entrance", False, [], ['Flippers']], + ["Swamp Palace - Entrance", False, [], ['Open Floodgate']], + ["Swamp Palace - Entrance", True, ['Open Floodgate', 'Flippers']], + + ["Swamp Palace - Big Chest", False, []], + ["Swamp Palace - Big Chest", False, [], ['Flippers']], + ["Swamp Palace - Big Chest", False, [], ['Open Floodgate']], + ["Swamp Palace - Big Chest", False, [], ['Hammer']], + ["Swamp Palace - Big Chest", False, [], ['Big Key (Swamp Palace)']], + ["Swamp Palace - Big Chest", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Big Chest", True, ['Open Floodgate', 'Big Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']], + + ["Swamp Palace - Big Key Chest", False, []], + ["Swamp Palace - Big Key Chest", False, [], ['Flippers']], + ["Swamp Palace - Big Key Chest", False, [], ['Open Floodgate']], + ["Swamp Palace - Big Key Chest", False, [], ['Hammer']], + ["Swamp Palace - Big Key Chest", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Big Key Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']], + + ["Swamp Palace - Map Chest", False, []], + ["Swamp Palace - Map Chest", False, [], ['Flippers']], + ["Swamp Palace - Map Chest", False, [], ['Open Floodgate']], + ["Swamp Palace - Map Chest", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Map Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers']], + + ["Swamp Palace - West Chest", False, []], + ["Swamp Palace - West Chest", False, [], ['Flippers']], + ["Swamp Palace - West Chest", False, [], ['Open Floodgate']], + ["Swamp Palace - West Chest", False, [], ['Hammer']], + ["Swamp Palace - West Chest", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - West Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']], + + ["Swamp Palace - Compass Chest", False, []], + ["Swamp Palace - Compass Chest", False, [], ['Flippers']], + ["Swamp Palace - Compass Chest", False, [], ['Open Floodgate']], + ["Swamp Palace - Compass Chest", False, [], ['Hammer']], + ["Swamp Palace - Compass Chest", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Compass Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']], + + ["Swamp Palace - Flooded Room - Left", False, []], + ["Swamp Palace - Flooded Room - Left", False, [], ['Flippers']], + ["Swamp Palace - Flooded Room - Left", False, [], ['Open Floodgate']], + ["Swamp Palace - Flooded Room - Left", False, [], ['Hammer']], + ["Swamp Palace - Flooded Room - Left", False, [], ['Hookshot']], + ["Swamp Palace - Flooded Room - Left", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Flooded Room - Left", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']], + + ["Swamp Palace - Flooded Room - Right", False, []], + ["Swamp Palace - Flooded Room - Right", False, [], ['Flippers']], + ["Swamp Palace - Flooded Room - Right", False, [], ['Open Floodgate']], + ["Swamp Palace - Flooded Room - Right", False, [], ['Hammer']], + ["Swamp Palace - Flooded Room - Right", False, [], ['Hookshot']], + ["Swamp Palace - Flooded Room - Right", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Flooded Room - Right", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']], + + ["Swamp Palace - Waterfall Room", False, []], + ["Swamp Palace - Waterfall Room", False, [], ['Flippers']], + ["Swamp Palace - Waterfall Room", False, [], ['Open Floodgate']], + ["Swamp Palace - Waterfall Room", False, [], ['Hammer']], + ["Swamp Palace - Waterfall Room", False, [], ['Hookshot']], + ["Swamp Palace - Waterfall Room", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Waterfall Room", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']], + + ["Swamp Palace - Boss", False, []], + ["Swamp Palace - Boss", False, [], ['Flippers']], + ["Swamp Palace - Boss", False, [], ['Open Floodgate']], + ["Swamp Palace - Boss", False, [], ['Hammer']], + ["Swamp Palace - Boss", False, [], ['Hookshot']], + ["Swamp Palace - Boss", False, [], ['Small Key (Swamp Palace)']], + ["Swamp Palace - Boss", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestThievesTown.py b/test/dungeons/TestThievesTown.py new file mode 100644 index 00000000..5fdf9037 --- /dev/null +++ b/test/dungeons/TestThievesTown.py @@ -0,0 +1,40 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestThievesTown(TestDungeon): + + def testThievesTown(self): + self.starting_regions = ['Thieves Town (Entrance)'] + self.run_tests([ + ["Thieves' Town - Attic", False, []], + ["Thieves' Town - Attic", False, [], ['Big Key (Thieves Town)']], + ["Thieves' Town - Attic", False, [], ['Small Key (Thieves Town)']], + ["Thieves' Town - Attic", True, ['Big Key (Thieves Town)', 'Small Key (Thieves Town)']], + + ["Thieves' Town - Big Key Chest", True, []], + + ["Thieves' Town - Map Chest", True, []], + + ["Thieves' Town - Compass Chest", True, []], + + ["Thieves' Town - Ambush Chest", True, []], + + ["Thieves' Town - Big Chest", False, []], + ["Thieves' Town - Big Chest", False, [], ['Big Key (Thieves Town)']], + ["Thieves' Town - Big Chest", False, [], ['Small Key (Thieves Town)']], + ["Thieves' Town - Big Chest", False, [], ['Hammer']], + ["Thieves' Town - Big Chest", True, ['Hammer', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)']], + + ["Thieves' Town - Blind's Cell", False, []], + ["Thieves' Town - Blind's Cell", False, [], ['Big Key (Thieves Town)']], + ["Thieves' Town - Blind's Cell", True, ['Big Key (Thieves Town)']], + + ["Thieves' Town - Boss", False, []], + ["Thieves' Town - Boss", False, [], ['Big Key (Thieves Town)']], + ["Thieves' Town - Boss", False, [], ['Small Key (Thieves Town)']], + ["Thieves' Town - Boss", False, [], ['Hammer', 'Progressive Sword', 'Cane of Somaria', 'Cane of Byrna']], + ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']], + ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']], + ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']], + ["Thieves' Town - Boss", True, ['Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']], + ]) \ No newline at end of file diff --git a/test/dungeons/TestTowerOfHera.py b/test/dungeons/TestTowerOfHera.py new file mode 100644 index 00000000..9762d208 --- /dev/null +++ b/test/dungeons/TestTowerOfHera.py @@ -0,0 +1,32 @@ +from test.dungeons.TestDungeon import TestDungeon + + +class TestTowerOfHera(TestDungeon): + + def testTowerOfHera(self): + self.starting_regions = ['Tower of Hera (Bottom)'] + self.run_tests([ + ["Tower of Hera - Big Key Chest", False, []], + ["Tower of Hera - Big Key Chest", False, [], ['Small Key (Tower of Hera)']], + ["Tower of Hera - Big Key Chest", False, [], ['Lamp', 'Fire Rod']], + ["Tower of Hera - Big Key Chest", True, ['Small Key (Tower of Hera)', 'Lamp']], + ["Tower of Hera - Big Key Chest", True, ['Small Key (Tower of Hera)', 'Fire Rod']], + + ["Tower of Hera - Basement Cage", True, []], + + ["Tower of Hera - Map Chest", True, []], + + ["Tower of Hera - Compass Chest", False, []], + ["Tower of Hera - Compass Chest", False, [], ['Big Key (Tower of Hera)']], + ["Tower of Hera - Compass Chest", True, ['Big Key (Tower of Hera)']], + + ["Tower of Hera - Big Chest", False, []], + ["Tower of Hera - Big Chest", False, [], ['Big Key (Tower of Hera)']], + ["Tower of Hera - Big Chest", True, ['Big Key (Tower of Hera)']], + + ["Tower of Hera - Boss", False, []], + ["Tower of Hera - Boss", False, [], ['Big Key (Tower of Hera)']], + ["Tower of Hera - Boss", False, [], ['Progressive Sword', 'Hammer']], + ["Tower of Hera - Boss", True, ['Progressive Sword', 'Big Key (Tower of Hera)']], + ["Tower of Hera - Boss", True, ['Hammer', 'Big Key (Tower of Hera)']], + ]) \ No newline at end of file diff --git a/test/dungeons/__init__.py b/test/dungeons/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/inverted/TestInverted.py b/test/inverted/TestInverted.py new file mode 100644 index 00000000..82e1c0f0 --- /dev/null +++ b/test/inverted/TestInverted.py @@ -0,0 +1,28 @@ +from BaseClasses import World +from Dungeons import create_dungeons, get_dungeon_item_pool +from EntranceShuffle import link_inverted_entrances +from InvertedRegions import create_inverted_regions +from ItemList import generate_itempool, difficulties +from Items import ItemFactory +from Regions import mark_light_world_regions +from Rules import set_rules +from test.TestVanilla import TestVanilla + + +class TestInverted(TestVanilla): + def setUp(self): + self.world = World(1, 'vanilla', 'noglitches', 'inverted', 'random', 'normal', 'normal', 'none', 'on', 'ganon', 'balanced', + True, False, False, False, False, False, False, False, False, None, + 'none', False) + self.world.difficulty_requirements = difficulties['normal'] + create_inverted_regions(self.world, 1) + create_dungeons(self.world, 1) + link_inverted_entrances(self.world, 1) + generate_itempool(self.world, 1) + self.world.required_medallions[1] = ['Ether', 'Quake'] + self.world.itempool.extend(get_dungeon_item_pool(self.world)) + self.world.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.world.get_location('Agahnim 1', 1).item = None + self.world.get_location('Agahnim 2', 1).item = None + mark_light_world_regions(self.world) + set_rules(self.world, 1) diff --git a/test/inverted/TestInvertedBombRules.py b/test/inverted/TestInvertedBombRules.py new file mode 100644 index 00000000..4914d2e3 --- /dev/null +++ b/test/inverted/TestInvertedBombRules.py @@ -0,0 +1,47 @@ +import unittest + +from BaseClasses import World +from Dungeons import create_dungeons +from EntranceShuffle import connect_entrance, Inverted_LW_Entrances, Inverted_LW_Dungeon_Entrances, Inverted_LW_Single_Cave_Doors, Inverted_Old_Man_Entrances, Inverted_DW_Entrances, Inverted_DW_Dungeon_Entrances, Inverted_DW_Single_Cave_Doors, \ + Inverted_LW_Entrances_Must_Exit, Inverted_LW_Dungeon_Entrances_Must_Exit, Inverted_Bomb_Shop_Multi_Cave_Doors, Inverted_Bomb_Shop_Single_Cave_Doors, Inverted_Blacksmith_Single_Cave_Doors, Inverted_Blacksmith_Multi_Cave_Doors +from InvertedRegions import create_inverted_regions +from ItemList import difficulties +from Rules import set_inverted_big_bomb_rules + + +class TestInvertedBombRules(unittest.TestCase): + + def setUp(self): + self.world = World(1, 'vanilla', 'noglitches', 'inverted', 'random', 'normal', 'normal', 'none', 'on', 'ganon', 'balanced', + True, False, False, False, False, False, False, False, False, None, + 'none', False) + self.world.difficulty_requirements = difficulties['normal'] + create_inverted_regions(self.world, 1) + create_dungeons(self.world, 1) + + #TODO: Just making sure I haven't missed an entrance. It would be good to test the rules make sense as well. + def testInvertedBombRulesAreComplete(self): + entrances = list(Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Single_Cave_Doors + Inverted_Old_Man_Entrances + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + Inverted_DW_Single_Cave_Doors) + must_exits = list(Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit) + for entrance_name in (entrances + must_exits): + if entrance_name not in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']: + entrance = self.world.get_entrance(entrance_name, 1) + connect_entrance(self.world, entrance, 'Inverted Big Bomb Shop', 1) + set_inverted_big_bomb_rules(self.world, 1) + entrance.connected_region.entrances.remove(entrance) + entrance.connected_region = None + + def testInvalidEntrancesAreNotUsed(self): + entrances = list(Inverted_Blacksmith_Multi_Cave_Doors + Inverted_Blacksmith_Single_Cave_Doors + Inverted_Bomb_Shop_Multi_Cave_Doors + Inverted_Bomb_Shop_Single_Cave_Doors) + invalid_entrances = ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)', 'Pyramid Fairy'] + for invalid_entrance in invalid_entrances: + self.assertNotIn(invalid_entrance, entrances) + + def testInvalidEntrances(self): + for entrance_name in ['Desert Palace Entrance (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)']: + entrance = self.world.get_entrance(entrance_name, 1) + connect_entrance(self.world, entrance, 'Inverted Big Bomb Shop', 1) + with self.assertRaises(Exception): + set_inverted_big_bomb_rules(self.world, 1) + entrance.connected_region.entrances.remove(entrance) + entrance.connected_region = None diff --git a/test/inverted/TestInvertedDarkWorld.py b/test/inverted/TestInvertedDarkWorld.py new file mode 100644 index 00000000..c2249c7e --- /dev/null +++ b/test/inverted/TestInvertedDarkWorld.py @@ -0,0 +1,116 @@ +from test.inverted.TestInverted import TestInverted + + +class TestInvertedDeathMountain(TestInverted): + + def testNorthWest(self): + self.run_tests([ + ["Brewery", True, []], + + ["C-Shaped House", True, []], + + ["Chest Game", True, []], + + ["Peg Cave", False, []], + ["Peg Cave", False, [], ['Hammer']], + ["Peg Cave", False, [], ['Progressive Glove', 'Magic Mirror']], + ["Peg Cave", True, ['Hammer', 'Progressive Glove', 'Progressive Glove']], + ["Peg Cave", True, ['Hammer', 'Progressive Glove', 'Magic Mirror', 'Moon Pearl']], + ["Peg Cave", True, ['Hammer', 'Beat Agahnim 1', 'Magic Mirror']], + + ["Bumper Cave Ledge", False, []], + ["Bumper Cave Ledge", False, [], ['Moon Pearl']], + ["Bumper Cave Ledge", False, [], ['Cape']], + ["Bumper Cave Ledge", False, [], ['Progressive Glove']], + ["Bumper Cave Ledge", False, [], ['Magic Mirror']], + ["Bumper Cave Ledge", True, ['Moon Pearl', 'Cape', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Bumper Cave Ledge", True, ['Moon Pearl', 'Cape', 'Magic Mirror', 'Progressive Glove', 'Hammer']], + ["Bumper Cave Ledge", True, ['Moon Pearl', 'Cape', 'Magic Mirror', 'Progressive Glove', 'Beat Agahnim 1']], + + ["Blacksmith", False, []], + ["Blacksmith", False, [], ['Progressive Glove', 'Magic Mirror']], + ["Blacksmith", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + #@todo: Can get this without moon pearl + #["Blacksmith", True, ['Beat Agahnim 1', 'Magic Mirror']], + ["Blacksmith", True, ['Beat Agahnim 1', 'Magic Mirror', 'Moon Pearl']], + ["Blacksmith", True, ['Progressive Glove', 'Hammer', 'Magic Mirror', 'Moon Pearl']], + + ["Purple Chest", False, []], + ["Purple Chest", False, [], ['Progressive Glove', 'Magic Mirror']], + ["Purple Chest", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + # @todo: Can get this without moon pearl + #["Purple Chest", True, ['Beat Agahnim 1', 'Magic Mirror']], + ["Purple Chest", True, ['Beat Agahnim 1', 'Magic Mirror', 'Moon Pearl']], + ["Purple Chest", True, ['Progressive Glove', 'Hammer', 'Magic Mirror', 'Moon Pearl']], + ]) + + def testNorthEast(self): + self.run_tests([ + ["Catfish", False, []], + ["Catfish", False, [], ['Progressive Glove', 'Flippers']], + ["Catfish", False, [], ['Progressive Glove', 'Magic Mirror']], + ["Catfish", False, [], ['Progressive Glove', 'Moon Pearl']], + ["Catfish", True, ['Beat Agahnim 1', 'Magic Mirror', 'Progressive Glove']], + ["Catfish", True, ['Beat Agahnim 1', 'Moon Pearl', 'Magic Mirror', 'Flippers']], + ["Catfish", True, ['Progressive Glove', 'Hammer']], + ["Catfish", True, ['Progressive Glove', 'Flippers']], + ["Catfish", True, ['Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Moon Pearl']], + + ["Pyramid", False, []], + ["Pyramid", True, ['Beat Agahnim 1', 'Magic Mirror']], + ["Pyramid", True, ['Hammer']], + ["Pyramid", True, ['Flippers', 'Progressive Glove']], + ["Pyramid", True, ['Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Moon Pearl']], + + ["Pyramid Fairy - Left", False, []], + ["Pyramid Fairy - Left", False, [], ['Magic Mirror']], + ["Pyramid Fairy - Left", False, [], ['Crystal 5']], + ["Pyramid Fairy - Left", False, [], ['Crystal 6']], + ["Pyramid Fairy - Left", True, ['Crystal 5', 'Crystal 6', 'Magic Mirror', 'Hammer', 'Progressive Glove', 'Moon Pearl']], + ["Pyramid Fairy - Left", True, ['Crystal 5', 'Crystal 6', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Pyramid Fairy - Left", True, ['Crystal 5', 'Crystal 6', 'Magic Mirror', 'Beat Agahnim 1']], + + ["Pyramid Fairy - Right", False, []], + ["Pyramid Fairy - Right", False, [], ['Magic Mirror']], + ["Pyramid Fairy - Right", False, [], ['Crystal 5']], + ["Pyramid Fairy - Right", False, [], ['Crystal 6']], + ["Pyramid Fairy - Right", True, ['Crystal 5', 'Crystal 6', 'Magic Mirror', 'Hammer', 'Progressive Glove', 'Moon Pearl']], + ["Pyramid Fairy - Right", True, ['Crystal 5', 'Crystal 6', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + ["Pyramid Fairy - Right", True, ['Crystal 5', 'Crystal 6', 'Magic Mirror', 'Beat Agahnim 1']], + ]) + + def testSouth(self): + self.run_tests([ + ["Hype Cave - Top", True, []], + + ["Hype Cave - Middle Right", True, []], + + ["Hype Cave - Middle Left", True, []], + + ["Hype Cave - Bottom", True, []], + + ["Hype Cave - Generous Guy", True, []], + + ["Stumpy", True, []], + + ["Digging Game", True, []], + + ["Link's House", True, []], + ]) + + def testMireArea(self): + self.run_tests([ + ["Mire Shed - Left", False, []], + ["Mire Shed - Left", False, [], ['Ocarina', 'Magic Mirror']], + ["Mire Shed - Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove']], + ["Mire Shed - Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Hammer']], + ["Mire Shed - Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1']], + ["Mire Shed - Left", True, ['Magic Mirror', 'Beat Agahnim 1']], + + ["Mire Shed - Right", False, []], + ["Mire Shed - Right", False, [], ['Ocarina', 'Magic Mirror']], + ["Mire Shed - Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove']], + ["Mire Shed - Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Hammer']], + ["Mire Shed - Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1']], + ["Mire Shed - Right", True, ['Magic Mirror', 'Beat Agahnim 1']], + ]) \ No newline at end of file diff --git a/test/inverted/TestInvertedDeathMountain.py b/test/inverted/TestInvertedDeathMountain.py new file mode 100644 index 00000000..fbd75ddd --- /dev/null +++ b/test/inverted/TestInvertedDeathMountain.py @@ -0,0 +1,229 @@ +from test.inverted.TestInverted import TestInverted + + +class TestInvertedDeathMountain(TestInverted): + + def testWestDeathMountain(self): + self.run_tests([ + ["Old Man", False, []], + ["Old Man", False, [], ['Progressive Glove', 'Ocarina']], + ["Old Man", False, [], ['Lamp']], + ["Old Man", True, ['Progressive Glove', 'Lamp']], + ["Old Man", False, ['Ocarina', 'Lamp']], + + ["Spectacle Rock Cave", False, []], + ["Spectacle Rock Cave", False, [], ['Progressive Glove', 'Ocarina']], + ["Spectacle Rock Cave", False, [], ['Lamp', 'Ocarina']], + ["Spectacle Rock Cave", False, ['Ocarina', 'Progressive Glove', 'Hammer']], + ["Spectacle Rock Cave", False, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Hammer']], + ["Spectacle Rock Cave", False, ['Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Spectacle Rock Cave", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Spectacle Rock Cave", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Spectacle Rock Cave", True, ['Progressive Glove', 'Lamp']], + ]) + + def testEastDeathMountain(self): + self.run_tests([ + ["Spiral Cave", False, []], + ["Spiral Cave", False, [], ['Moon Pearl']], + ["Spiral Cave", False, [], ['Progressive Glove', 'Ocarina']], + ["Spiral Cave", False, [], ['Lamp', 'Ocarina']], + ["Spiral Cave", False, ['Progressive Glove'], ['Hookshot', 'Progressive Glove']], + ["Spiral Cave", False, ['Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Spiral Cave", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Spiral Cave", False, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Spiral Cave", False, ['Ocarina', 'Hookshot', 'Moon Pearl']], + ["Spiral Cave", True, ['Ocarina', 'Hookshot', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Spiral Cave", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hookshot']], + ["Spiral Cave", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Spiral Cave", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Lower - Far Left", False, []], + ["Paradox Cave Lower - Far Left", False, [], ['Moon Pearl']], + ["Paradox Cave Lower - Far Left", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Lower - Far Left", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Lower - Far Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Lower - Far Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Far Left", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Lower - Left", False, []], + ["Paradox Cave Lower - Left", False, [], ['Moon Pearl']], + ["Paradox Cave Lower - Left", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Lower - Left", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Lower - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Lower - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Left", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Left", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Lower - Middle", False, []], + ["Paradox Cave Lower - Middle", False, [], ['Moon Pearl']], + ["Paradox Cave Lower - Middle", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Lower - Middle", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Lower - Middle", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Lower - Middle", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Middle", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Lower - Right", False, []], + ["Paradox Cave Lower - Right", False, [], ['Moon Pearl']], + ["Paradox Cave Lower - Right", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Lower - Right", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Lower - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Lower - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Right", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Right", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Lower - Far Right", False, []], + ["Paradox Cave Lower - Far Right", False, [], ['Moon Pearl']], + ["Paradox Cave Lower - Far Right", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Lower - Far Right", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Lower - Far Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Lower - Far Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Lower - Far Right", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Upper - Left", False, []], + ["Paradox Cave Upper - Left", False, [], ['Moon Pearl']], + ["Paradox Cave Upper - Left", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Upper - Left", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Upper - Left", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Upper - Left", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Upper - Left", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Paradox Cave Upper - Right", False, []], + ["Paradox Cave Upper - Right", False, [], ['Moon Pearl']], + ["Paradox Cave Upper - Right", False, [], ['Progressive Glove', 'Ocarina']], + ["Paradox Cave Upper - Right", False, [], ['Lamp', 'Ocarina']], + ["Paradox Cave Upper - Right", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Paradox Cave Upper - Right", False, ['Progressive Glove', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", False, ['Ocarina', 'Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Ocarina', 'Progressive Glove', 'Hammer', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Lamp', 'Hookshot', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl']], + ["Paradox Cave Upper - Right", True, ['Ocarina', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Mimic Cave", False, []], + ["Mimic Cave", False, [], ['Moon Pearl']], + ["Mimic Cave", False, [], ['Hammer']], + ["Mimic Cave", False, [], ['Progressive Glove', 'Ocarina']], + ["Mimic Cave", False, [], ['Lamp', 'Ocarina']], + ["Mimic Cave", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Hammer', 'Hookshot']], + ["Mimic Cave", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']], + ["Mimic Cave", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']], + ["Mimic Cave", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']], + + ["Ether Tablet", False, []], + ["Ether Tablet", False, [], ['Moon Pearl']], + ["Ether Tablet", False, [], ['Progressive Glove', 'Ocarina']], + ["Ether Tablet", False, [], ['Lamp', 'Ocarina']], + ["Ether Tablet", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Ether Tablet", False, [], ['Hammer']], + ["Ether Tablet", False, ['Progressive Sword'], ['Progressive Sword']], + ["Ether Tablet", False, [], ['Book of Mudora']], + ["Ether Tablet", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Hammer', 'Hookshot', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Ether Tablet", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Ether Tablet", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Ether Tablet", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + + ["Spectacle Rock", False, []], + ["Spectacle Rock", False, [], ['Moon Pearl']], + ["Spectacle Rock", False, [], ['Progressive Glove', 'Ocarina']], + ["Spectacle Rock", False, [], ['Lamp', 'Ocarina']], + ["Spectacle Rock", False, ['Progressive Glove'], ['Progressive Glove', 'Hookshot']], + ["Spectacle Rock", False, [], ['Hammer']], + ["Spectacle Rock", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Hammer', 'Hookshot']], + ["Spectacle Rock", True, ['Ocarina', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove', 'Hammer']], + ["Spectacle Rock", True, ['Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer', 'Hookshot']], + ["Spectacle Rock", True, ['Progressive Glove', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Hammer']], + ]) + + def testEastDarkWorldDeathMountain(self): + self.run_tests([ + ["Superbunny Cave - Top", False, []], + ["Superbunny Cave - Top", False, [], ['Progressive Glove', 'Ocarina']], + ["Superbunny Cave - Top", True, ['Progressive Glove', 'Lamp']], + ["Superbunny Cave - Top", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina']], + ["Superbunny Cave - Top", True, ['Hammer', 'Progressive Glove', 'Moon Pearl', 'Ocarina']], + + ["Superbunny Cave - Bottom", False, []], + ["Superbunny Cave - Bottom", False, [], ['Progressive Glove', 'Ocarina']], + ["Superbunny Cave - Bottom", True, ['Progressive Glove', 'Lamp']], + ["Superbunny Cave - Bottom", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina']], + ["Superbunny Cave - Bottom", True, ['Hammer', 'Progressive Glove', 'Moon Pearl', 'Ocarina']], + + ["Hookshot Cave - Bottom Right", False, []], + ["Hookshot Cave - Bottom Right", False, [], ['Progressive Glove', 'Ocarina']], + ["Hookshot Cave - Bottom Right", False, [], ['Pegasus Boots', 'Hookshot']], + ["Hookshot Cave - Bottom Right", True, ['Progressive Glove', 'Lamp', 'Pegasus Boots']], + ["Hookshot Cave - Bottom Right", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina', 'Pegasus Boots']], + ["Hookshot Cave - Bottom Right", True, ['Progressive Glove', 'Hammer', 'Moon Pearl', 'Ocarina', 'Pegasus Boots']], + ["Hookshot Cave - Bottom Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], + ["Hookshot Cave - Bottom Right", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina', 'Hookshot']], + ["Hookshot Cave - Bottom Right", True, ['Progressive Glove', 'Hammer', 'Moon Pearl', 'Ocarina', 'Hookshot']], + + ["Hookshot Cave - Bottom Left", False, []], + ["Hookshot Cave - Bottom Left", False, [], ['Progressive Glove', 'Ocarina']], + ["Hookshot Cave - Bottom Left", False, [], ['Pegasus Boots', 'Hookshot']], + ["Hookshot Cave - Bottom Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], + ["Hookshot Cave - Bottom Left", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina', 'Hookshot']], + ["Hookshot Cave - Bottom Left", True, ['Progressive Glove', 'Hammer', 'Moon Pearl', 'Ocarina', 'Hookshot']], + + ["Hookshot Cave - Top Left", False, []], + ["Hookshot Cave - Top Left", False, [], ['Progressive Glove', 'Ocarina']], + ["Hookshot Cave - Top Left", False, [], ['Pegasus Boots', 'Hookshot']], + ["Hookshot Cave - Top Left", True, ['Progressive Glove', 'Lamp', 'Hookshot']], + ["Hookshot Cave - Top Left", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina', 'Hookshot']], + ["Hookshot Cave - Top Left", True, ['Progressive Glove', 'Hammer', 'Moon Pearl', 'Ocarina', 'Hookshot']], + + ["Hookshot Cave - Top Right", False, []], + ["Hookshot Cave - Top Right", False, [], ['Progressive Glove', 'Ocarina']], + ["Hookshot Cave - Top Right", False, [], ['Pegasus Boots', 'Hookshot']], + ["Hookshot Cave - Top Right", True, ['Progressive Glove', 'Lamp', 'Hookshot']], + ["Hookshot Cave - Top Right", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl', 'Ocarina', 'Hookshot']], + ["Hookshot Cave - Top Right", True, ['Progressive Glove', 'Hammer', 'Moon Pearl', 'Ocarina', 'Hookshot']], + ]) + + def testWestDarkWorldDeathMountain(self): + self.run_tests([ + ["Spike Cave", False, []], + ["Spike Cave", False, [], ['Progressive Glove']], + ["Spike Cave", False, [], ['Hammer']], + ["Spike Cave", False, [], ['Cape', 'Cane of Byrna']], + ["Spike Cave", False, [], ['Cane of Byrna', 'AnyBottle', 'Magic Upgrade (1/2)']], + ["Spike Cave", False, [], ['AnyBottle', 'Magic Upgrade (1/2)', 'Pegasus Boots', 'Boss Heart Container', 'Piece of Heart', 'Sanctuary Heart Container']], + ["Spike Cave", False, ['Bottle', 'Hammer', 'Progressive Glove', 'Lamp', 'Cape']], + ["Spike Cave", True, ['Bottle', 'Hammer', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Cape']], + ["Spike Cave", True, ['Bottle', 'Hammer', 'Progressive Glove', 'Ocarina', 'Moon Pearl', 'Cape']], + ["Spike Cave", False, ['Bottle', 'Hammer', 'Progressive Glove', 'Lamp', 'Cane of Byrna']], + ["Spike Cave", True, ['Bottle', 'Hammer', 'Progressive Glove', 'Lamp', 'Moon Pearl', 'Cane of Byrna']], + ["Spike Cave", True, ['Bottle', 'Hammer', 'Progressive Glove', 'Ocarina', 'Moon Pearl', 'Cane of Byrna']], + ["Spike Cave", True, ['Magic Upgrade (1/2)', 'Hammer', 'Progressive Glove', 'Lamp', 'Cape']], + ["Spike Cave", True, ['Magic Upgrade (1/2)', 'Hammer', 'Progressive Glove', 'Ocarina', 'Moon Pearl', 'Cape']], + ["Spike Cave", True, ['Magic Upgrade (1/2)', 'Hammer', 'Progressive Glove', 'Lamp', 'Cane of Byrna']], + ["Spike Cave", True, ['Magic Upgrade (1/2)', 'Hammer', 'Progressive Glove', 'Ocarina', 'Moon Pearl', 'Cane of Byrna']], + ["Spike Cave", True, ['Pegasus Boots', 'Hammer', 'Progressive Glove', 'Lamp', 'Cane of Byrna']], + ["Spike Cave", True, ['Pegasus Boots', 'Hammer', 'Progressive Glove', 'Ocarina', 'Moon Pearl', 'Cane of Byrna']], + ["Spike Cave", True, ['Boss Heart Container', 'Hammer', 'Progressive Glove', 'Lamp', 'Cane of Byrna']], + ["Spike Cave", True, ['Boss Heart Container', 'Hammer', 'Progressive Glove', 'Ocarina', 'Moon Pearl', 'Cane of Byrna']], + ]) + diff --git a/test/inverted/TestInvertedLightWorld.py b/test/inverted/TestInvertedLightWorld.py new file mode 100644 index 00000000..6a1e5baf --- /dev/null +++ b/test/inverted/TestInvertedLightWorld.py @@ -0,0 +1,380 @@ +from test.inverted.TestInverted import TestInverted + + +class TestInvertedLightWorld(TestInverted): + def setUp(self): + super().setUp() + + def testLostWoods(self): + self.run_tests([ + ["Master Sword Pedestal", False, []], + ["Master Sword Pedestal", False, [], ['Green Pendant']], + ["Master Sword Pedestal", False, [], ['Red Pendant']], + ["Master Sword Pedestal", False, [], ['Blue Pendant']], + # @todo: Can get this without moon pearl + # ["Master Sword Pedestal", True, ['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1']], + ["Master Sword Pedestal", True, ['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Moon Pearl']], + ["Master Sword Pedestal", True, ['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Master Sword Pedestal", True, ['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Mushroom", False, []], + ["Mushroom", False, [], ['Moon Pearl']], + ["Mushroom", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Mushroom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mushroom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Lost Woods Hideout", False, []], + ["Lost Woods Hideout", False, [], ['Moon Pearl']], + ["Lost Woods Hideout", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Lost Woods Hideout", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Lost Woods Hideout", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Lumberjack Tree", False, []], + ["Lumberjack Tree", False, [], ['Pegasus Boots']], + ["Lumberjack Tree", False, [], ['Beat Agahnim 1']], + ["Lumberjack Tree", False, [], ['Moon Pearl']], + ["Lumberjack Tree", True, ['Pegasus Boots', 'Beat Agahnim 1', 'Moon Pearl']], + ]) + + def testKakariko(self): + self.run_tests([ + ["Kakariko Tavern", False, []], + ["Kakariko Tavern", False, [], ['Moon Pearl']], + ["Kakariko Tavern", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Tavern", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Tavern", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Chicken House", False, []], + ["Chicken House", False, [], ['Moon Pearl']], + ["Chicken House", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Chicken House", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Kakariko Well - Top", False, []], + ["Kakariko Well - Top", False, [], ['Moon Pearl']], + ["Kakariko Well - Top", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Kakariko Well - Left", False, []], + ["Kakariko Well - Left", False, [], ['Moon Pearl']], + ["Kakariko Well - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Kakariko Well - Middle", False, []], + ["Kakariko Well - Middle", False, [], ['Moon Pearl']], + ["Kakariko Well - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Kakariko Well - Right", False, []], + ["Kakariko Well - Right", False, [], ['Moon Pearl']], + ["Kakariko Well - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Kakariko Well - Bottom", False, []], + ["Kakariko Well - Bottom", False, [], ['Moon Pearl']], + ["Kakariko Well - Bottom", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Kakariko Well - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Kakariko Well - Bottom", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Blind's Hideout - Top", False, []], + ["Blind's Hideout - Top", False, [], ['Moon Pearl']], + ["Blind's Hideout - Top", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Top", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Blind's Hideout - Left", False, []], + ["Blind's Hideout - Left", False, [], ['Moon Pearl']], + ["Blind's Hideout - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Blind's Hideout - Right", False, []], + ["Blind's Hideout - Right", False, [], ['Moon Pearl']], + ["Blind's Hideout - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Blind's Hideout - Far Left", False, []], + ["Blind's Hideout - Far Left", False, [], ['Moon Pearl']], + ["Blind's Hideout - Far Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Blind's Hideout - Far Right", False, []], + ["Blind's Hideout - Far Right", False, [], ['Moon Pearl']], + ["Blind's Hideout - Far Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Blind's Hideout - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Blind's Hideout - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Bottle Merchant", False, []], + #@todo: Can get this without moon pearl + #["Bottle Merchant", True, ['Beat Agahnim 1']], + ["Bottle Merchant", True, ['Beat Agahnim 1', 'Moon Pearl']], + ["Bottle Merchant", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Bottle Merchant", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Sick Kid", False, []], + ["Sick Kid", False, [], ['AnyBottle']], + ["Sick Kid", False, ['Bottle (Bee)']], + ["Sick Kid", False, ['Bottle (Fairy)']], + ["Sick Kid", False, ['Bottle (Red Potion)']], + ["Sick Kid", False, ['Bottle (Green Potion)']], + ["Sick Kid", False, ['Bottle (Blue Potion)']], + ["Sick Kid", False, ['Bottle']], + ["Sick Kid", False, ['Bottle (Good Bee)']], + ["Sick Kid", True, ['Bottle (Bee)', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle (Bee)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle (Bee)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sick Kid", True, ['Bottle (Fairy)', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle (Fairy)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle (Fairy)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sick Kid", True, ['Bottle (Red Potion)', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle (Red Potion)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle (Red Potion)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sick Kid", True, ['Bottle (Green Potion)', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle (Green Potion)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle (Green Potion)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sick Kid", True, ['Bottle (Blue Potion)', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle (Blue Potion)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle (Blue Potion)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sick Kid", True, ['Bottle', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Sick Kid", True, ['Bottle (Good Bee)', 'Beat Agahnim 1']], + ["Sick Kid", True, ['Bottle (Good Bee)', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sick Kid", True, ['Bottle (Good Bee)', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Magic Bat", False, []], + ["Magic Bat", False, [], ['Magic Powder']], + ["Magic Bat", False, [], ['Hammer']], + ["Magic Bat", False, [], ['Moon Pearl']], + ["Magic Bat", False, ['Magic Powder', 'Hammer', 'Moon Pearl']], + ["Magic Bat", True, ['Magic Powder', 'Hammer', 'Moon Pearl', 'Beat Agahnim 1']], + ["Magic Bat", True, ['Magic Powder', 'Hammer', 'Moon Pearl', 'Progressive Glove']], + + ["Library", False, []], + ["Library", False, [], ['Pegasus Boots']], + ["Library", False, [], ['Moon Pearl']], + ["Library", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']], + ["Library", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Library", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Maze Race", False, []], + ["Maze Race", False, [], ['Moon Pearl']], + ["Maze Race", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Maze Race", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ]) + + def testSouthLightWorld(self): + self.run_tests([ + ["Desert Ledge", False, []], + ["Desert Ledge", False, [], ['Book of Mudora']], + ["Desert Ledge", False, [], ['Moon Pearl']], + ["Desert Ledge", True, ['Book of Mudora', 'Moon Pearl', 'Beat Agahnim 1']], + ["Desert Ledge", True, ['Book of Mudora', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Desert Ledge", True, ['Book of Mudora', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Checkerboard Cave", False, []], + ["Checkerboard Cave", False, [], ['Progressive Glove']], + ["Checkerboard Cave", False, [], ['Moon Pearl']], + ["Checkerboard Cave", True, ['Progressive Glove', 'Beat Agahnim 1', 'Moon Pearl']], + ["Checkerboard Cave", True, ['Progressive Glove', 'Hammer', 'Moon Pearl']], + ["Checkerboard Cave", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Aginah's Cave", False, []], + ["Aginah's Cave", False, [], ['Moon Pearl']], + ["Aginah's Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Aginah's Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Bombos Tablet", False, []], + ["Bombos Tablet", False, ['Progressive Sword'], ['Progressive Sword']], + ["Bombos Tablet", False, [], ['Book of Mudora']], + ["Bombos Tablet", False, [], ['Moon Pearl', 'Beat Agahnim 1']], + ["Bombos Tablet", True, ['Beat Agahnim 1', 'Book of Mudora', 'Progressive Sword', 'Progressive Sword']], + ["Bombos Tablet", True, ['Moon Pearl', 'Book of Mudora', 'Progressive Glove', 'Progressive Glove', 'Progressive Sword', 'Progressive Sword']], + ["Bombos Tablet", True, ['Moon Pearl', 'Book of Mudora', 'Progressive Glove', 'Hammer', 'Progressive Sword', 'Progressive Sword']], + ["Bombos Tablet", True, ['Moon Pearl', 'Book of Mudora', 'Beat Agahnim 1', 'Progressive Sword', 'Progressive Sword']], + + ["Floodgate Chest", False, []], + ["Floodgate Chest", False, [], ['Moon Pearl']], + ["Floodgate Chest", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Floodgate Chest", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Floodgate Chest", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Sunken Treasure", False, []], + ["Sunken Treasure", False, [], ['Moon Pearl']], + ["Sunken Treasure", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sunken Treasure", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sunken Treasure", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Mini Moldorm Cave - Far Left", False, []], + ["Mini Moldorm Cave - Far Left", False, [], ['Moon Pearl']], + ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Far Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Mini Moldorm Cave - Left", False, []], + ["Mini Moldorm Cave - Left", False, [], ['Moon Pearl']], + ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Mini Moldorm Cave - Generous Guy", False, []], + ["Mini Moldorm Cave - Generous Guy", False, [], ['Moon Pearl']], + ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Generous Guy", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Mini Moldorm Cave - Right", False, []], + ["Mini Moldorm Cave - Right", False, [], ['Moon Pearl']], + ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Mini Moldorm Cave - Far Right", False, []], + ["Mini Moldorm Cave - Far Right", False, [], ['Moon Pearl']], + ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Mini Moldorm Cave - Far Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Ice Rod Cave", False, []], + ["Ice Rod Cave", False, [], ['Moon Pearl']], + ["Ice Rod Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Ice Rod Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ]) + + def testZoraArea(self): + self.run_tests([ + ["King Zora", False, []], + ["King Zora", False, [], ['Progressive Glove', 'Flippers']], + ["King Zora", False, [], ['Moon Pearl']], + ["King Zora", True, ['Flippers', 'Moon Pearl', 'Beat Agahnim 1']], + ["King Zora", True, ['Progressive Glove', 'Moon Pearl', 'Beat Agahnim 1']], + ["King Zora", True, ['Progressive Glove', 'Moon Pearl', 'Hammer']], + ["King Zora", True, ['Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Zora's Ledge", False, []], + ["Zora's Ledge", False, [], ['Flippers']], + ["Zora's Ledge", False, [], ['Moon Pearl']], + ["Zora's Ledge", True, ['Flippers', 'Moon Pearl', 'Beat Agahnim 1']], + ["Zora's Ledge", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Zora's Ledge", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Waterfall Fairy - Left", False, []], + ["Waterfall Fairy - Left", False, [], ['Flippers']], + ["Waterfall Fairy - Left", False, [], ['Moon Pearl']], + ["Waterfall Fairy - Left", True, ['Flippers', 'Moon Pearl', 'Beat Agahnim 1']], + ["Waterfall Fairy - Left", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Waterfall Fairy - Left", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Waterfall Fairy - Right", False, []], + ["Waterfall Fairy - Right", False, [], ['Flippers']], + ["Waterfall Fairy - Right", False, [], ['Moon Pearl']], + ["Waterfall Fairy - Right", True, ['Flippers', 'Moon Pearl', 'Beat Agahnim 1']], + ["Waterfall Fairy - Right", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Waterfall Fairy - Right", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ]) + + def testLightWorld(self): + self.run_tests([ + ["Link's Uncle", False, []], + ["Link's Uncle", False, [], ['Moon Pearl']], + ["Link's Uncle", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Link's Uncle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Link's Uncle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Secret Passage", False, []], + ["Secret Passage", False, [], ['Moon Pearl']], + ["Secret Passage", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Secret Passage", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Secret Passage", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["King's Tomb", False, []], + ["King's Tomb", False, [], ['Pegasus Boots']], + ["King's Tomb", False, ['Progressive Glove'], ['Progressive Glove']], + ["King's Tomb", False, [], ['Moon Pearl']], + ["King's Tomb", True, ['Pegasus Boots', 'Progressive Glove', 'Progressive Glove', 'Moon Pearl']], + + ["Sahasrahla's Hut - Left", False, []], + ["Sahasrahla's Hut - Left", False, [], ['Moon Pearl']], + ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Left", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Sahasrahla's Hut - Middle", False, []], + ["Sahasrahla's Hut - Middle", False, [], ['Moon Pearl']], + ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Middle", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Sahasrahla's Hut - Right", False, []], + ["Sahasrahla's Hut - Right", False, [], ['Moon Pearl']], + ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Beat Agahnim 1']], + ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla's Hut - Right", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Sahasrahla", False, []], + ["Sahasrahla", False, [], ['Green Pendant']], + ["Sahasrahla", True, ['Green Pendant', 'Beat Agahnim 1']], + ["Sahasrahla", True, ['Green Pendant', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Sahasrahla", True, ['Green Pendant', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Bonk Rock Cave", False, []], + ["Bonk Rock Cave", False, [], ['Pegasus Boots']], + ["Bonk Rock Cave", False, [], ['Moon Pearl']], + ["Bonk Rock Cave", True, ['Pegasus Boots', 'Moon Pearl', 'Beat Agahnim 1']], + ["Bonk Rock Cave", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Bonk Rock Cave", True, ['Pegasus Boots', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Hobo", False, []], + ["Hobo", False, [], ['Flippers']], + ["Hobo", False, [], ['Flippers', 'Moon Pearl']], + ["Hobo", True, ['Flippers', 'Moon Pearl', 'Beat Agahnim 1']], + ["Hobo", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Hobo", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Cave 45", False, []], + ["Cave 45", False, [], ['Moon Pearl']], + ["Cave 45", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Cave 45", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Cave 45", True, ['Moon Pearl', 'Beat Agahnim 1']], + + ["Graveyard Cave", False, []], + ["Graveyard Cave", False, [], ['Moon Pearl']], + ["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Graveyard Cave", True, ['Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Graveyard Cave", True, ['Moon Pearl', 'Beat Agahnim 1']], + + ["Potion Shop", False, []], + ["Potion Shop", False, [], ['Mushroom']], + ["Potion Shop", False, [], ['Moon Pearl']], + ["Potion Shop", True, ['Mushroom', 'Moon Pearl', 'Beat Agahnim 1']], + ["Potion Shop", True, ['Mushroom', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Potion Shop", True, ['Mushroom', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Lake Hylia Island", False, []], + ["Lake Hylia Island", False, [], ['Moon Pearl']], + ["Lake Hylia Island", False, [], ['Flippers']], + ["Lake Hylia Island", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + ["Lake Hylia Island", True, ['Flippers', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Lake Hylia Island", True, ['Flippers', 'Moon Pearl', 'Beat Agahnim 1']], + + ["Flute Spot", False, []], + ["Flute Spot", False, [], ['Shovel']], + ["Flute Spot", False, [], ['Moon Pearl']], + ["Flute Spot", True, ['Shovel', 'Moon Pearl', 'Beat Agahnim 1']], + ["Flute Spot", True, ['Shovel', 'Moon Pearl', 'Progressive Glove', 'Hammer']], + ["Flute Spot", True, ['Shovel', 'Moon Pearl', 'Progressive Glove', 'Progressive Glove']], + + ["Ganon", False, []], + ["Ganon", False, [], ['Moon Pearl']], + ["Ganon", False, [], ['Beat Agahnim 2']], + ]) \ No newline at end of file diff --git a/test/inverted/TestInvertedTurtleRock.py b/test/inverted/TestInvertedTurtleRock.py new file mode 100644 index 00000000..7975abeb --- /dev/null +++ b/test/inverted/TestInvertedTurtleRock.py @@ -0,0 +1,207 @@ +from test.inverted.TestInverted import TestInverted + + +class TestInvertedTurtleRock(TestInverted): + + def testTurtleRock(self): + self.run_tests([ + ["Turtle Rock - Compass Chest", False, []], + ["Turtle Rock - Compass Chest", False, [], ['Cane of Somaria']], + ["Turtle Rock - Compass Chest", False, [], ['Quake', 'Magic Mirror']], + ["Turtle Rock - Compass Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria']], + ["Turtle Rock - Compass Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']], + ["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + + ["Turtle Rock - Chain Chomps", False, []], + ["Turtle Rock - Chain Chomps", False, [], ['Magic Mirror', 'Cane of Somaria']], + #@todo: Item rando only needs 1 key + ["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], + ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot']], + ["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']], + + ["Turtle Rock - Roller Room - Left", False, []], + ["Turtle Rock - Roller Room - Left", False, [], ['Cane of Somaria']], + ["Turtle Rock - Roller Room - Left", False, [], ['Fire Rod']], + ["Turtle Rock - Roller Room - Left", False, [], ['Quake', 'Magic Mirror']], + ["Turtle Rock - Roller Room - Left", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria']], + ["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']], + ["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + + ["Turtle Rock - Roller Room - Right", False, []], + ["Turtle Rock - Roller Room - Right", False, [], ['Cane of Somaria']], + ["Turtle Rock - Roller Room - Right", False, [], ['Fire Rod']], + ["Turtle Rock - Roller Room - Right", False, [], ['Quake', 'Magic Mirror']], + ["Turtle Rock - Roller Room - Right", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria']], + ["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']], + ["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + + ["Turtle Rock - Big Chest", False, []], + ["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Hookshot']], + + ["Turtle Rock - Big Key Chest", False, []], + ["Turtle Rock - Big Key Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + + ["Turtle Rock - Crystaroller Room", False, []], + ["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], + ["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Cane of Somaria']], + ["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Lamp']], + ["Turtle Rock - Crystaroller Room", False, [], ['Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Crystaroller Room", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']], + ["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']], + ["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot']], + ["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror']], + ["Turtle Rock - Crystaroller Room", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']], + ["Turtle Rock - Crystaroller Room", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria']], + ["Turtle Rock - Crystaroller Room", True, ['Lamp', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria']], + ["Turtle Rock - Crystaroller Room", True, ['Lamp', 'Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria']], + + #@todo: Advanced? + ["Turtle Rock - Eye Bridge - Bottom Left", False, []], + ["Turtle Rock - Eye Bridge - Bottom Left", False, ['Progressive Shield', 'Progressive Shield'], ['Progressive Shield', 'Cape', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], + ["Turtle Rock - Eye Bridge - Bottom Left", False, [], ['Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Eye Bridge - Bottom Left", False, [], ['Magic Mirror', 'Lamp']], + ["Turtle Rock - Eye Bridge - Bottom Left", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + + ["Turtle Rock - Eye Bridge - Bottom Right", False, []], + ["Turtle Rock - Eye Bridge - Bottom Right", False, ['Progressive Shield', 'Progressive Shield'], ['Progressive Shield', 'Cape', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], + ["Turtle Rock - Eye Bridge - Bottom Right", False, [], ['Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Eye Bridge - Bottom Right", False, [], ['Magic Mirror', 'Lamp']], + ["Turtle Rock - Eye Bridge - Bottom Right", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Bottom Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + + ["Turtle Rock - Eye Bridge - Top Left", False, []], + ["Turtle Rock - Eye Bridge - Top Left", False, ['Progressive Shield', 'Progressive Shield'], ['Progressive Shield', 'Cape', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], + ["Turtle Rock - Eye Bridge - Top Left", False, [], ['Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Eye Bridge - Top Left", False, [], ['Magic Mirror', 'Lamp']], + ["Turtle Rock - Eye Bridge - Top Left", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Left", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + + ["Turtle Rock - Eye Bridge - Top Right", False, []], + ["Turtle Rock - Eye Bridge - Top Right", False, ['Progressive Shield', 'Progressive Shield'], ['Progressive Shield', 'Cape', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']], + ["Turtle Rock - Eye Bridge - Top Right", False, [], ['Magic Mirror', 'Cane of Somaria']], + ["Turtle Rock - Eye Bridge - Top Right", False, [], ['Magic Mirror', 'Lamp']], + ["Turtle Rock - Eye Bridge - Top Right", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cane of Byrna']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Cape']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + ["Turtle Rock - Eye Bridge - Top Right", True, ['Moon Pearl', 'Ocarina', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']], + + ["Turtle Rock - Boss", False, []], + ["Turtle Rock - Boss", False, [], ['Cane of Somaria']], + ["Turtle Rock - Boss", False, [], ['Ice Rod']], + ["Turtle Rock - Boss", False, [], ['Fire Rod']], + ["Turtle Rock - Boss", False, [], ['Progressive Sword', 'Hammer']], + ["Turtle Rock - Boss", False, [], ['Big Key (Turtle Rock)']], + ["Turtle Rock - Boss", False, [], ['Magic Mirror', 'Lamp']], + ["Turtle Rock - Boss", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']], + ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Moon Pearl', 'Ocarina', 'Beat Agahnim 1', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], + ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Moon Pearl', 'Beat Agahnim 1', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], + ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], + ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']], + ["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Ocarina', 'Beat Agahnim 1', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']] + ]) \ No newline at end of file diff --git a/test/inverted/__init__.py b/test/inverted/__init__.py new file mode 100644 index 00000000..e69de29b