From 438d76562752568ed02ab15607d3f4528ff51a6a Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 2 Jan 2020 11:15:27 -0700 Subject: [PATCH] Directional typos on interior doors fixed. Better batching support for mass testing of seed generation. Generation issues fixed: --Filler now tests with the key in the proposed location to enable alternate key rules --Key rule checker now only considers key locations that the parent sphere did not have - better key rules --- DoorShuffle.py | 12 ++++++------ Doors.py | 30 +++++++++++++++--------------- DungeonRandomizer.py | 15 +++++++++++++-- Fill.py | 10 +++++++++- KeyDoorShuffle.py | 25 ++++++++++++++++++------- Regions.py | 12 ++++++------ Rules.py | 2 +- 7 files changed, 68 insertions(+), 38 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 77fe8ed5..5c7e7a18 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -1150,9 +1150,9 @@ def find_inaccessible_regions(world, player): world.inaccessible_regions[player].append('Hyrule Castle Ledge') world.inaccessible_regions[player].append('Sewer Drop') logger = logging.getLogger('') - logger.info('Inaccessible Regions:') + logger.debug('Inaccessible Regions:') for r in world.inaccessible_regions[player]: - logger.info('%s', r) + logger.debug('%s', r) def valid_inaccessible_region(r): @@ -1661,10 +1661,10 @@ interior_doors = [ ('Skull Big Key WN', 'Skull Lone Pot EN'), ('Skull Small Hall WS', 'Skull 2 West Lobby ES'), ('Skull 2 West Lobby NW', 'Skull X Room SW'), - ('Skull 3 Lobby WN', 'Skull East Bridge EN'), - ('Skull East Bridge ES', 'Skull West Bridge Nook WS'), - ('Skull Star Pits WS', 'Skull Torch Room ES'), - ('Skull Torch Room EN', 'Skull Vines WN'), + ('Skull 3 Lobby EN', 'Skull East Bridge WN'), + ('Skull East Bridge WS', 'Skull West Bridge Nook ES'), + ('Skull Star Pits ES', 'Skull Torch Room WS'), + ('Skull Torch Room WN', 'Skull Vines EN'), ('Skull Spike Corner ES', 'Skull Final Drop WS'), ('Thieves Hallway WS', 'Thieves Pot Alcove Mid ES'), ('Thieves Conveyor Maze SW', 'Thieves Pot Alcove Top NW'), diff --git a/Doors.py b/Doors.py index ed6cd4ba..939e96f9 100644 --- a/Doors.py +++ b/Doors.py @@ -307,9 +307,9 @@ def create_doors(world, player): create_door(player, 'PoD Lobby N', Intr).dir(No, 0x4a, Mid, High).pos(3), create_door(player, 'PoD Lobby NW', Intr).dir(No, 0x4a, Left, High).pos(0), create_door(player, 'PoD Lobby NE', Intr).dir(No, 0x4a, Right, High).pos(1), - create_door(player, 'PoD Left Cage SW', Intr).dir(No, 0x4a, Left, High).pos(0), - create_door(player, 'PoD Middle Cage S', Intr).dir(No, 0x4a, Mid, High).pos(3), - create_door(player, 'PoD Middle Cage SE', Intr).dir(No, 0x4a, Right, High).pos(1), + create_door(player, 'PoD Left Cage SW', Intr).dir(So, 0x4a, Left, High).pos(0), + create_door(player, 'PoD Middle Cage S', Intr).dir(So, 0x4a, Mid, High).pos(3), + create_door(player, 'PoD Middle Cage SE', Intr).dir(So, 0x4a, Right, High).pos(1), create_door(player, 'PoD Left Cage Down Stairs', Sprl).dir(Dn, 0x4a, 1, HTH).ss(A, 0x12, 0x80, False, True), create_door(player, 'PoD Middle Cage Down Stairs', Sprl).dir(Dn, 0x4a, 0, HTH).ss(S, 0x12, 0x80, False, True), create_door(player, 'PoD Middle Cage N', Nrml).dir(No, 0x4a, Mid, High).small_key().pos(2), @@ -358,7 +358,7 @@ def create_doors(world, player): create_door(player, 'PoD Dark Maze EN', Nrml).dir(Ea, 0x19, Top, High).small_key().pos(1), create_door(player, 'PoD Dark Maze E', Nrml).dir(Ea, 0x19, Mid, High).pos(0), create_door(player, 'PoD Compass Room WN', Intr).dir(We, 0x1a, Top, High).pos(4), - create_door(player, 'PoD Compass Room SE', Intr).dir(No, 0x1a, Mid, High).small_key().pos(0), + create_door(player, 'PoD Compass Room SE', Intr).dir(So, 0x1a, Mid, High).small_key().pos(0), create_door(player, 'PoD Harmless Hellway NE', Intr).dir(No, 0x1a, Right, High).small_key().pos(0), create_door(player, 'PoD Harmless Hellway SE', Nrml).dir(So, 0x1a, Right, High).pos(5), create_door(player, 'PoD Compass Room W Down Stairs', Sprl).dir(Dn, 0x1a, 0, HTH).ss(S, 0x12, 0x50, True, True), @@ -466,7 +466,7 @@ def create_doors(world, player): create_door(player, 'Swamp Flooded Spot Ladder', Lgcl), create_door(player, 'Swamp Flooded Room Ladder', Lgcl), create_door(player, 'Swamp Basement Shallows NW', Nrml).dir(No, 0x76, Left, High).toggler().pos(2), - create_door(player, 'Swamp Basement Shallows EN', Intr).dir(We, 0x76, Top, High).pos(0), + create_door(player, 'Swamp Basement Shallows EN', Intr).dir(Ea, 0x76, Top, High).pos(0), create_door(player, 'Swamp Basement Shallows ES', Intr).dir(Ea, 0x76, Bot, High).pos(1), create_door(player, 'Swamp Waterfall Room SW', Nrml).dir(So, 0x66, Left, Low).toggler().pos(1), create_door(player, 'Swamp Waterfall Room NW', Intr).dir(No, 0x66, Left, Low).pos(3), @@ -514,15 +514,15 @@ def create_doors(world, player): create_door(player, 'Skull X Room SW', Intr).dir(So, 0x56, Left, High).small_key().pos(0), create_door(player, 'Skull Back Drop Star Path', Lgcl), create_door(player, 'Skull 3 Lobby NW', Nrml).dir(No, 0x59, Left, High).small_key().pos(0), - create_door(player, 'Skull 3 Lobby WN', Intr).dir(We, 0x59, Top, High).pos(2), - create_door(player, 'Skull East Bridge EN', Intr).dir(Ea, 0x59, Top, High).pos(2), - create_door(player, 'Skull East Bridge ES', Intr).dir(Ea, 0x59, Bot, High).pos(3), - create_door(player, 'Skull West Bridge Nook WS', Intr).dir(We, 0x59, Bot, High).pos(3), + create_door(player, 'Skull 3 Lobby EN', Intr).dir(Ea, 0x59, Top, High).pos(2), + create_door(player, 'Skull East Bridge WN', Intr).dir(We, 0x59, Top, High).pos(2), + create_door(player, 'Skull East Bridge WS', Intr).dir(We, 0x59, Bot, High).pos(3), + create_door(player, 'Skull West Bridge Nook ES', Intr).dir(Ea, 0x59, Bot, High).pos(3), create_door(player, 'Skull Star Pits SW', Nrml).dir(So, 0x49, Left, High).small_key().pos(2), - create_door(player, 'Skull Star Pits WS', Intr).dir(We, 0x49, Bot, High).pos(3), - create_door(player, 'Skull Torch Room ES', Intr).dir(Ea, 0x49, Bot, High).pos(3), - create_door(player, 'Skull Torch Room EN', Intr).dir(Ea, 0x49, Top, High).pos(1), - create_door(player, 'Skull Vines WN', Intr).dir(We, 0x49, Top, High).pos(1), + create_door(player, 'Skull Star Pits ES', Intr).dir(Ea, 0x49, Bot, High).pos(3), + create_door(player, 'Skull Torch Room WS', Intr).dir(We, 0x49, Bot, High).pos(3), + create_door(player, 'Skull Torch Room WN', Intr).dir(We, 0x49, Top, High).pos(1), + create_door(player, 'Skull Vines EN', Intr).dir(Ea, 0x49, Top, High).pos(1), create_door(player, 'Skull Vines NW', Nrml).dir(No, 0x49, Left, High).pos(0), create_door(player, 'Skull Spike Corner SW', Nrml).dir(So, 0x39, Left, High).no_exit().trap(0x4).pos(0), create_door(player, 'Skull Spike Corner ES', Intr).dir(Ea, 0x39, Bot, High).small_key().pos(1), @@ -701,7 +701,7 @@ def create_doors(world, player): create_door(player, 'Mire Lobby Gap', Lgcl), create_door(player, 'Mire Post-Gap Gap', Lgcl), - create_door(player, 'Mire Post-Gap Down Stairs', Sprl).dir(Up, 0x98, 0, HTH).ss(X, 0x11, 0x90, False, True), + create_door(player, 'Mire Post-Gap Down Stairs', Sprl).dir(Dn, 0x98, 0, HTH).ss(X, 0x11, 0x90, False, True), create_door(player, 'Mire 2 Up Stairs', Sprl).dir(Up, 0xd2, 0, HTH).ss(X, 0x1a, 0x7c, False, True), create_door(player, 'Mire 2 NE', Nrml).dir(No, 0xd2, Right, High).trap(0x4).pos(0), create_door(player, 'Mire Hub SE', Nrml).dir(So, 0xc2, Right, High).pos(5), @@ -900,7 +900,7 @@ def create_doors(world, player): create_door(player, 'GT Torch EN', Intr).dir(Ea, 0x8c, Top, High).small_key().pos(2), create_door(player, 'GT Hope Room WN', Intr).dir(We, 0x8c, Top, High).small_key().pos(2), create_door(player, 'GT Torch SW', Intr).dir(So, 0x8c, Left, High).no_exit().pos(1), - create_door(player, 'GT Big Chest NW', Intr).dir(So, 0x8c, Left, High).pos(1), + create_door(player, 'GT Big Chest NW', Intr).dir(No, 0x8c, Left, High).pos(1), create_door(player, 'GT Blocked Stairs Down Stairs', Sprl).dir(Dn, 0x8c, 3, HTH).ss(Z, 0x12, 0x40, True, True).kill(), create_door(player, 'GT Blocked Stairs Block Path', Lgcl), create_door(player, 'GT Big Chest SW', Nrml).dir(So, 0x8c, Left, High).pos(4), diff --git a/DungeonRandomizer.py b/DungeonRandomizer.py index a6e5d07c..6f0bfdd7 100755 --- a/DungeonRandomizer.py +++ b/DungeonRandomizer.py @@ -8,6 +8,7 @@ import sys from Main import main from Utils import is_bundled, close_console, output_path +from Fill import FillError class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): @@ -303,12 +304,22 @@ def start(): guiMain(args) elif args.count is not None: seed = args.seed + failures = [] + logger = logging.getLogger('') for _ in range(args.count): - main(seed=seed, args=args) + try: + main(seed=seed, args=args) + logger.info('Finished run %s', _+1) + except (FillError, Exception, RuntimeError) as err: + failures.append((err, seed)) + logger.warning('Generation failed: %s', err) seed = random.randint(0, 999999999) - logging.getLogger('').info('Finished run %s', _) + for fail in failures: + logger.info('%s seed failed with: %s', fail[1], fail[0]) + logger.info('Generation fail rate: %f%%', 100*len(failures)/args.count) else: main(seed=args.seed, args=args) + if __name__ == '__main__': start() diff --git a/Fill.py b/Fill.py index bfc1145e..1dd4f6ab 100644 --- a/Fill.py +++ b/Fill.py @@ -190,9 +190,17 @@ def fill_restrictive(world, base_state, locations, itempool): for item_to_place in items_to_place: spot_to_fill = None for location in locations: - if location.can_fill(maximum_exploration_state, item_to_place, perform_access_check): + if item_to_place.key: # a better test to see if a key can go there + location.item = item_to_place + test_state = maximum_exploration_state.copy() + test_state.stale[item_to_place.player] = True + else: + test_state = maximum_exploration_state + if location.can_fill(test_state, item_to_place, perform_access_check): spot_to_fill = location break + elif item_to_place.key: + location.item = None if spot_to_fill is None: # we filled all reachable spots. Maybe the game can be beaten anyway? diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 7d76a0ab..217a6e9f 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -162,6 +162,13 @@ def queue_sorter(queue_item): return 1 if door.bigKey else 0 +def queue_sorter_2(queue_item): + door, counter, key_only = queue_item + if door is None: + return 0 + return 1 if door.bigKey else 0 + + def find_bk_locked_sections(key_layout, world): key_counters = key_layout.key_counters key_logic = key_layout.key_logic @@ -547,13 +554,13 @@ def flatten_pair_list(paired_list): def check_rules(original_counter, key_layout): all_key_only = set() key_only_map = {} - queue = collections.deque([(None, original_counter)]) + queue = collections.deque([(None, original_counter, original_counter.key_only_locations)]) completed = set() completed.add(cid(original_counter, key_layout)) while len(queue) > 0: - queue = collections.deque(sorted(queue, key=queue_sorter)) - access_door, counter = queue.popleft() - for loc in counter.key_only_locations: + queue = collections.deque(sorted(queue, key=queue_sorter_2)) + access_door, counter, key_only_loc = queue.popleft() + for loc in key_only_loc: if loc not in all_key_only: all_key_only.add(loc) access_rules = [] @@ -561,16 +568,20 @@ def check_rules(original_counter, key_layout): else: access_rules = key_only_map[loc] if access_door is None or access_door.name not in key_layout.key_logic.door_rules.keys(): - access_rules.append(DoorRules(0)) + if access_door is None or not access_door.bigKey: + access_rules.append(DoorRules(0)) else: - access_rules.append(key_layout.key_logic.door_rules[access_door.name]) + rule = key_layout.key_logic.door_rules[access_door.name] + if rule not in access_rules: + access_rules.append(rule) for child in counter.child_doors.keys(): if not child.bigKey or not key_layout.big_key_special or counter.big_key_opened: next_counter = find_next_counter(child, counter, key_layout) c_id = cid(next_counter, key_layout) if c_id not in completed: completed.add(c_id) - queue.append((child, next_counter)) + new_key_only = dict_difference(next_counter.key_only_locations, counter.key_only_locations) + queue.append((child, next_counter, new_key_only)) min_rule_bk = defaultdict(list) min_rule_non_bk = defaultdict(list) check_non_bk = False diff --git a/Regions.py b/Regions.py index 134d97db..5d27d312 100644 --- a/Regions.py +++ b/Regions.py @@ -432,12 +432,12 @@ def create_regions(world, player): create_dungeon_region(player, 'Skull Back Drop', 'Skull Woods', None, ['Skull Back Drop Star Path', ]), create_dungeon_region(player, 'Skull 2 West Lobby', 'Skull Woods', ['Skull Woods - West Lobby Pot Key'], ['Skull 2 West Lobby ES', 'Skull 2 West Lobby NW', 'Skull Woods Second Section Exit (West)']), create_dungeon_region(player, 'Skull X Room', 'Skull Woods', None, ['Skull X Room SW']), - create_dungeon_region(player, 'Skull 3 Lobby', 'Skull Woods', None, ['Skull 3 Lobby NW', 'Skull 3 Lobby WN', 'Skull Woods Final Section Exit']), - create_dungeon_region(player, 'Skull East Bridge', 'Skull Woods', None, ['Skull East Bridge EN', 'Skull East Bridge ES']), - create_dungeon_region(player, 'Skull West Bridge Nook', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull West Bridge Nook WS']), - create_dungeon_region(player, 'Skull Star Pits', 'Skull Woods', None, ['Skull Star Pits SW', 'Skull Star Pits WS']), - create_dungeon_region(player, 'Skull Torch Room', 'Skull Woods', None, ['Skull Torch Room ES', 'Skull Torch Room EN']), - create_dungeon_region(player, 'Skull Vines', 'Skull Woods', None, ['Skull Vines WN', 'Skull Vines NW']), + create_dungeon_region(player, 'Skull 3 Lobby', 'Skull Woods', None, ['Skull 3 Lobby NW', 'Skull 3 Lobby EN', 'Skull Woods Final Section Exit']), + create_dungeon_region(player, 'Skull East Bridge', 'Skull Woods', None, ['Skull East Bridge WN', 'Skull East Bridge WS']), + create_dungeon_region(player, 'Skull West Bridge Nook', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull West Bridge Nook ES']), + create_dungeon_region(player, 'Skull Star Pits', 'Skull Woods', None, ['Skull Star Pits SW', 'Skull Star Pits ES']), + create_dungeon_region(player, 'Skull Torch Room', 'Skull Woods', None, ['Skull Torch Room WS', 'Skull Torch Room WN']), + create_dungeon_region(player, 'Skull Vines', 'Skull Woods', None, ['Skull Vines EN', 'Skull Vines NW']), create_dungeon_region(player, 'Skull Spike Corner', 'Skull Woods', ['Skull Woods - Spike Corner Key Drop'], ['Skull Spike Corner SW', 'Skull Spike Corner ES']), create_dungeon_region(player, 'Skull Final Drop', 'Skull Woods', None, ['Skull Final Drop WS', 'Skull Final Drop Hole']), create_dungeon_region(player, 'Skull Boss', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']), diff --git a/Rules.py b/Rules.py index 2f24ea90..39647bbb 100644 --- a/Rules.py +++ b/Rules.py @@ -324,7 +324,7 @@ def global_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player)) set_rule(world.get_entrance('Skull Big Chest Hookpath', player), lambda state: state.has('Hookshot', player)) - set_rule(world.get_entrance('Skull Torch Room EN', player), lambda state: state.has('Fire Rod', player)) + set_rule(world.get_entrance('Skull Torch Room WN', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('Skull Vines NW', player), lambda state: state.has_sword(player)) set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player))