Fix deterministic issues in repeat seed generation

This commit is contained in:
codemann8
2021-12-19 19:59:30 -06:00
parent 7699c1fc63
commit d79adbf3c8
4 changed files with 31 additions and 28 deletions

View File

@@ -1658,7 +1658,8 @@ class Entrance(object):
from OWEdges import OWTileRegions from OWEdges import OWTileRegions
from OverworldShuffle import ow_connections from OverworldShuffle import ow_connections
owid = OWTileRegions[follower_region.name] owid = OWTileRegions[follower_region.name]
(mirror_map, other_world) = ow_connections[owid % 0x40] (mirror_map_orig, other_world) = ow_connections[owid % 0x40]
mirror_map = list(mirror_map_orig).copy()
mirror_map.extend(other_world) mirror_map.extend(other_world)
mirror_exit = None mirror_exit = None
while len(mirror_map): while len(mirror_map):
@@ -1710,7 +1711,8 @@ class Entrance(object):
from OWEdges import OWTileRegions from OWEdges import OWTileRegions
from OverworldShuffle import ow_connections from OverworldShuffle import ow_connections
owid = OWTileRegions[dest_region.name] owid = OWTileRegions[dest_region.name]
(mirror_map, other_world) = ow_connections[owid % 0x40] (mirror_map_orig, other_world) = ow_connections.copy()[owid % 0x40]
mirror_map = list(mirror_map_orig).copy()
mirror_map.extend(other_world) mirror_map.extend(other_world)
mirror_map = [(x, d) for (x, d) in mirror_map if x in [e.name for e in dest_region.exits]] mirror_map = [(x, d) for (x, d) in mirror_map if x in [e.name for e in dest_region.exits]]
# loop thru potential places to leave a mirror portal # loop thru potential places to leave a mirror portal

View File

@@ -1820,7 +1820,7 @@ def find_inaccessible_regions(world, player):
else: else:
start_regions = ['Links House', 'Dark Sanctuary Hint'] start_regions = ['Links House', 'Dark Sanctuary Hint']
regs = convert_regions(start_regions, world, player) regs = convert_regions(start_regions, world, player)
all_regions = set([r for r in world.regions if r.player == player and r.type is not RegionType.Dungeon]) all_regions = [r for r in world.regions if r.player == player and r.type is not RegionType.Dungeon]
visited_regions = set() visited_regions = set()
queue = deque(regs) queue = deque(regs)
while len(queue) > 0: while len(queue) > 0:
@@ -1836,7 +1836,7 @@ def find_inaccessible_regions(world, player):
if connect and connect not in queue and connect not in visited_regions: if connect and connect not in queue and connect not in visited_regions:
if connect.type is not RegionType.Dungeon or connect.name.endswith(' Portal'): if connect.type is not RegionType.Dungeon or connect.name.endswith(' Portal'):
queue.append(connect) queue.append(connect)
world.inaccessible_regions[player].extend([r.name for r in all_regions.difference(visited_regions) if valid_inaccessible_region(r)]) world.inaccessible_regions[player].extend([r.name for r in all_regions if r not in visited_regions and valid_inaccessible_region(r)])
if (world.mode[player] == 'inverted') != (0x1b in world.owswaps[player][0] and world.owMixed[player]): if (world.mode[player] == 'inverted') != (0x1b in world.owswaps[player][0] and world.owMixed[player]):
ledge = world.get_region('Hyrule Castle Ledge', player) ledge = world.get_region('Hyrule Castle Ledge', player)
if any(x for x in ledge.exits if x.connected_region and x.connected_region.name == 'Agahnims Tower Portal'): if any(x for x in ledge.exits if x.connected_region and x.connected_region.name == 'Agahnims Tower Portal'):

View File

@@ -1,5 +1,5 @@
import logging import logging
from collections import defaultdict from collections import defaultdict, OrderedDict
import RaceRandom as random import RaceRandom as random
from BaseClasses import CollectionState, RegionType from BaseClasses import CollectionState, RegionType
from OverworldShuffle import build_accessible_region_list from OverworldShuffle import build_accessible_region_list
@@ -432,21 +432,21 @@ def link_entrances(world, player):
connector_entrances = [e for e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] if e in (dw_entrances if not invFlag else lw_entrances)] connector_entrances = [e for e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] if e in (dw_entrances if not invFlag else lw_entrances)]
connect_inaccessible_regions(world, [], connector_entrances, caves, player) connect_inaccessible_regions(world, [], connector_entrances, caves, player)
if invFlag: if invFlag:
lw_dungeons = list(set(lw_dungeons) & set(caves)) lw_dungeons = list(OrderedDict.fromkeys(lw_dungeons + caves))
else: else:
dw_dungeons = list(set(dw_dungeons) & set(caves)) dw_dungeons = list(OrderedDict.fromkeys(dw_dungeons + caves))
caves = list(set(Cave_Base) & set(caves)) + (lw_dungeons if not invFlag else dw_dungeons) caves = list(OrderedDict.fromkeys(Cave_Base + caves)) + (lw_dungeons if not invFlag else dw_dungeons)
connector_entrances = [e for e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] if e in (lw_entrances if not invFlag else dw_entrances)] connector_entrances = [e for e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] if e in (lw_entrances if not invFlag else dw_entrances)]
connect_inaccessible_regions(world, connector_entrances, [], caves, player) connect_inaccessible_regions(world, connector_entrances, [], caves, player)
if not invFlag: if not invFlag:
lw_dungeons = list(set(lw_dungeons) & set(caves)) lw_dungeons = list(OrderedDict.fromkeys(lw_dungeons + caves))
else: else:
dw_dungeons = list(set(dw_dungeons) & set(caves)) dw_dungeons = list(OrderedDict.fromkeys(dw_dungeons + caves))
lw_dungeons = lw_dungeons + (Old_Man_House if not invFlag else []) lw_dungeons = lw_dungeons + (Old_Man_House if not invFlag else [])
dw_dungeons = dw_dungeons + ([] if not invFlag else Old_Man_House) dw_dungeons = dw_dungeons + ([] if not invFlag else Old_Man_House)
caves = list(set(Cave_Base) & set(caves)) + DW_Mid_Dungeon_Exits caves = list(OrderedDict.fromkeys(Cave_Base + caves)) + DW_Mid_Dungeon_Exits
# place old man, has limited options # place old man, has limited options
lw_entrances = [e for e in lw_entrances if e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] and e in entrance_pool] lw_entrances = [e for e in lw_entrances if e in list(zip(*default_connector_connections + default_dungeon_connections + open_default_dungeon_connections))[0] and e in entrance_pool]
@@ -1429,10 +1429,10 @@ def place_blacksmith(world, links_house, player):
if invFlag: if invFlag:
dark_sanc = world.get_entrance('Dark Sanctuary Hint Exit', player).connected_region.name dark_sanc = world.get_entrance('Dark Sanctuary Hint Exit', player).connected_region.name
blacksmith_doors = list(set(blacksmith_doors + list(build_accessible_entrance_list(world, dark_sanc, player, assumed_inventory, False, True, True)))) blacksmith_doors = list(OrderedDict.fromkeys(blacksmith_doors + list(build_accessible_entrance_list(world, dark_sanc, player, assumed_inventory, False, True, True))))
elif world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: elif world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3:
sanc_region = world.get_entrance('Sanctuary Exit', player).connected_region.name sanc_region = world.get_entrance('Sanctuary Exit', player).connected_region.name
blacksmith_doors = list(set(blacksmith_doors + list(build_accessible_entrance_list(world, sanc_region, player, assumed_inventory, False, True, True)))) blacksmith_doors = list(OrderedDict.fromkeys(blacksmith_doors + list(build_accessible_entrance_list(world, sanc_region, player, assumed_inventory, False, True, True))))
if world.shuffle[player] in ['lite', 'lean']: if world.shuffle[player] in ['lite', 'lean']:
blacksmith_doors = [e for e in blacksmith_doors if e in list(zip(*(default_item_connections + (default_shop_connections if world.shopsanity[player] else []))))[0]] blacksmith_doors = [e for e in blacksmith_doors if e in list(zip(*(default_item_connections + (default_shop_connections if world.shopsanity[player] else []))))[0]]
@@ -1491,7 +1491,7 @@ def junk_fill_inaccessible(world, player):
accessible_regions.append(region_name) accessible_regions.append(region_name)
break break
for region_name in accessible_regions.copy(): for region_name in accessible_regions.copy():
accessible_regions = list(set(accessible_regions + list(build_accessible_region_list(base_world, region_name, player, False, True, False, False)))) accessible_regions = list(OrderedDict.fromkeys(accessible_regions + list(build_accessible_region_list(base_world, region_name, player, False, True, False, False))))
world.inaccessible_regions[player] = [r for r in world.inaccessible_regions[player] if r not in accessible_regions] world.inaccessible_regions[player] = [r for r in world.inaccessible_regions[player] if r not in accessible_regions]
# get inaccessible entrances # get inaccessible entrances
@@ -1527,7 +1527,7 @@ def connect_inaccessible_regions(world, lw_entrances, dw_entrances, caves, playe
accessible_regions.append(region_name) accessible_regions.append(region_name)
break break
for region_name in accessible_regions.copy(): for region_name in accessible_regions.copy():
accessible_regions = list(set(accessible_regions + list(build_accessible_region_list(world, region_name, player, True, True, False, False)))) accessible_regions = list(OrderedDict.fromkeys(accessible_regions + list(build_accessible_region_list(world, region_name, player, True, True, False, False))))
world.inaccessible_regions[player] = [r for r in world.inaccessible_regions[player] if r not in accessible_regions] world.inaccessible_regions[player] = [r for r in world.inaccessible_regions[player] if r not in accessible_regions]
# split inaccessible into 2 lists for each world # split inaccessible into 2 lists for each world
@@ -1665,12 +1665,12 @@ def build_accessible_entrance_list(world, start_region, player, assumed_inventor
new_regions.append(ledge) new_regions.append(ledge)
explored_regions.extend(new_regions) explored_regions.extend(new_regions)
entrances = set() entrances = list()
for region_name in explored_regions: for region_name in explored_regions:
region = base_world.get_region(region_name, player) region = base_world.get_region(region_name, player)
for exit in region.exits: for exit in region.exits:
if exit.name in entrance_pool and (not exit_rules or exit.access_rule(blank_state)): if exit.name in entrance_pool and (not exit_rules or exit.access_rule(blank_state)):
entrances.add(exit.name) entrances.append(exit.name)
return entrances return entrances
@@ -1798,7 +1798,7 @@ Cave_Three_Exits_Base = [('Spectacle Rock Cave Exit (Peak)', 'Spectacle Rock Cav
Old_Man_House_Base = [('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')] Old_Man_House_Base = [('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')]
Entrance_Pool_Base = {'Links House', Entrance_Pool_Base = ['Links House',
'Desert Palace Entrance (South)', 'Desert Palace Entrance (South)',
'Desert Palace Entrance (West)', 'Desert Palace Entrance (West)',
'Desert Palace Entrance (East)', 'Desert Palace Entrance (East)',
@@ -1936,9 +1936,9 @@ Entrance_Pool_Base = {'Links House',
'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (West)',
'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (East)',
'Skull Woods First Section Hole (North)', 'Skull Woods First Section Hole (North)',
'Pyramid Hole'} 'Pyramid Hole']
Exit_Pool_Base = {'Links House Exit', Exit_Pool_Base = ['Links House Exit',
'Desert Palace Exit (South)', 'Desert Palace Exit (South)',
'Desert Palace Exit (West)', 'Desert Palace Exit (West)',
'Desert Palace Exit (East)', 'Desert Palace Exit (East)',
@@ -2076,7 +2076,7 @@ Exit_Pool_Base = {'Links House Exit',
'Skull Left Drop', 'Skull Left Drop',
'Skull Pinball', 'Skull Pinball',
'Skull Pot Circle', 'Skull Pot Circle',
'Pyramid'} 'Pyramid']
# these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions # these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions
mandatory_connections = [('Links House S&Q', 'Links House'), mandatory_connections = [('Links House S&Q', 'Links House'),

View File

@@ -1,4 +1,5 @@
import RaceRandom as random, logging, copy import RaceRandom as random, logging, copy
from collections import OrderedDict
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance
from Regions import mark_dark_world_regions, mark_light_world_regions from Regions import mark_dark_world_regions, mark_light_world_regions
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel
@@ -500,7 +501,7 @@ def shuffle_tiles(world, groups, result_list, player):
exist_dw_regions.extend(dw_regions) exist_dw_regions.extend(dw_regions)
# check whirlpool parity # check whirlpool parity
valid_whirlpool_parity = world.owCrossed[player] not in ['none', 'grouped'] or len(set(new_results[0]) & set({0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f})) % 2 == 0 valid_whirlpool_parity = world.owCrossed[player] not in ['none', 'grouped'] or len(OrderedDict.fromkeys(new_results[0] + [0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f])) % 2 == 0
(exist_owids, exist_lw_regions, exist_dw_regions) = result_list (exist_owids, exist_lw_regions, exist_dw_regions) = result_list
exist_owids.extend(new_results[0]) exist_owids.extend(new_results[0])
@@ -809,7 +810,7 @@ def can_reach_smith(world, player):
def explore_region(region_name, region=None): def explore_region(region_name, region=None):
nonlocal found nonlocal found
explored_regions.add(region_name) explored_regions.append(region_name)
if not found: if not found:
if not region: if not region:
region = world.get_region(region_name, player) region = world.get_region(region_name, player)
@@ -838,7 +839,7 @@ def can_reach_smith(world, player):
blank_state.collect(ItemFactory('Titans Mitts', player), True) blank_state.collect(ItemFactory('Titans Mitts', player), True)
found = False found = False
explored_regions = set() explored_regions = list()
explore_region('Links House') explore_region('Links House')
if not found: if not found:
if not invFlag: if not invFlag:
@@ -867,7 +868,7 @@ def build_sectors(world, player):
if (any(r in unique_regions for r in explored_regions)): if (any(r in unique_regions for r in explored_regions)):
for s in range(len(sectors)): for s in range(len(sectors)):
if (any(r in sectors[s] for r in explored_regions)): if (any(r in sectors[s] for r in explored_regions)):
sectors[s] = set(list(sectors[s]) + list(explored_regions)) sectors[s] = list(list(sectors[s]) + list(explored_regions))
break break
else: else:
sectors.append(explored_regions) sectors.append(explored_regions)
@@ -893,7 +894,7 @@ def build_sectors(world, player):
if (any(r in unique_regions for r in explored_regions)): if (any(r in unique_regions for r in explored_regions)):
for s2 in range(len(sectors2)): for s2 in range(len(sectors2)):
if (any(r in sectors2[s2] for r in explored_regions)): if (any(r in sectors2[s2] for r in explored_regions)):
sectors2[s2] = set(list(sectors2[s2]) + list(explored_regions)) sectors2[s2] = list(sectors2[s2] + explored_regions)
break break
else: else:
sectors2.append(explored_regions) sectors2.append(explored_regions)
@@ -907,7 +908,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F
from Items import ItemFactory from Items import ItemFactory
def explore_region(region_name, region=None): def explore_region(region_name, region=None):
explored_regions.add(region_name) explored_regions.append(region_name)
if not region: if not region:
region = base_world.get_region(region_name, player) region = base_world.get_region(region_name, player)
for exit in region.exits: for exit in region.exits:
@@ -936,7 +937,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F
blank_state = CollectionState(base_world) blank_state = CollectionState(base_world)
if base_world.mode[player] == 'standard': if base_world.mode[player] == 'standard':
blank_state.collect(ItemFactory('Zelda Delivered', player), True) blank_state.collect(ItemFactory('Zelda Delivered', player), True)
explored_regions = set() explored_regions = list()
explore_region(start_region) explore_region(start_region)
return explored_regions return explored_regions