From 93ecf5988b612496c3a2b04045f8265d83fc4b26 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 14 Jul 2020 07:01:51 +0200 Subject: [PATCH] implement secrets.SystemRandom() for --race --- BaseClasses.py | 9 ++ Bosses.py | 8 +- Dungeons.py | 4 +- EntranceRandomizer.py | 1 - EntranceShuffle.py | 328 ++++++++++++++++++++++-------------------- Fill.py | 39 +++-- ItemList.py | 64 +++++---- Main.py | 18 ++- MultiServer.py | 2 +- Mystery.py | 3 + Rom.py | 86 ++++++----- 11 files changed, 312 insertions(+), 250 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index a5c52678..09e8d02e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -10,6 +10,10 @@ from EntranceShuffle import door_addresses, indirect_connections from Utils import int16_as_bytes from typing import Union +import secrets +import random + + class World(object): debug_types = False player_names: list @@ -28,6 +32,8 @@ class World(object): setattr(self, name[7:], method) logging.debug(f"Set {self}.{name[7:]} to {method}") self.get_location = self._debug_get_location + self.random = random.Random() # world-local random state is saved in case of future use a + # persistently running program with multiple worlds rolling concurrently self.players = players self.teams = 1 self.shuffle = shuffle.copy() @@ -116,6 +122,9 @@ class World(object): set_player_attr('triforce_pieces_available', 30) set_player_attr('triforce_pieces_required', 20) + def secure(self): + self.random = secrets.SystemRandom() + @property def player_ids(self): yield from range(1, self.players + 1) diff --git a/Bosses.py b/Bosses.py index 2c5ae3fe..70464da8 100644 --- a/Bosses.py +++ b/Bosses.py @@ -1,5 +1,4 @@ import logging -import random from BaseClasses import Boss from Fill import FillError @@ -193,11 +192,11 @@ def place_bosses(world, player): if world.boss_shuffle[player] == "basic": # vanilla bosses shuffled bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm'] else: # all bosses present, the three duplicates chosen at random - bosses = all_bosses + [random.choice(placeable_bosses) for _ in range(3)] + bosses = all_bosses + [world.random.choice(placeable_bosses) for _ in range(3)] logging.getLogger('').debug('Bosses chosen %s', bosses) - random.shuffle(bosses) + world.random.shuffle(bosses) for [loc, level] in boss_locations: loc_text = loc + (' ('+level+')' if level else '') boss = next((b for b in bosses if can_place_boss(world, player, b, loc, level)), None) @@ -211,7 +210,8 @@ def place_bosses(world, player): for [loc, level] in boss_locations: loc_text = loc + (' ('+level+')' if level else '') try: - boss = random.choice([b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)]) + boss = world.random.choice( + [b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)]) except IndexError: raise FillError('Could not place boss for location %s' % loc_text) diff --git a/Dungeons.py b/Dungeons.py index d7e245da..25d34fee 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -1,5 +1,3 @@ -import random - from BaseClasses import Dungeon from Bosses import BossFactory from Fill import fill_restrictive @@ -58,7 +56,7 @@ def fill_dungeons(world): dungeon_regions, big_key, small_keys, dungeon_items = dungeons.pop(0) # this is what we need to fill dungeon_locations = [location for location in world.get_unfilled_locations() if location.parent_region.name in dungeon_regions] - random.shuffle(dungeon_locations) + world.random.shuffle(dungeon_locations) all_state = all_state_base.copy() diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 862d1c70..ef2f53aa 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -3,7 +3,6 @@ import argparse import copy import os import logging -import random import textwrap import shlex import sys diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 25963fb9..5e81829f 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1,5 +1,3 @@ -import random - # ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave. from collections import defaultdict @@ -13,7 +11,7 @@ def link_entrances(world, player): Old_Man_House = Old_Man_House_Base.copy() Cave_Three_Exits = Cave_Three_Exits_Base.copy() - unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) + unbias_some_entrances(world, Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) # setup mandatory connections for exitname, regionname in mandatory_connections: @@ -75,8 +73,8 @@ def link_entrances(world, player): # we shuffle all 2 entrance caves as pairs as a start # start with the ones that need to be directed two_door_caves = list(Two_Door_Caves_Directional) - random.shuffle(two_door_caves) - random.shuffle(caves) + world.random.shuffle(two_door_caves) + world.random.shuffle(caves) while two_door_caves: entrance1, entrance2 = two_door_caves.pop() exit1, exit2 = caves.pop() @@ -85,7 +83,7 @@ def link_entrances(world, player): # now the remaining pairs two_door_caves = list(Two_Door_Caves) - random.shuffle(two_door_caves) + world.random.shuffle(two_door_caves) while two_door_caves: entrance1, entrance2 = two_door_caves.pop() exit1, exit2 = caves.pop() @@ -96,10 +94,10 @@ def link_entrances(world, player): # place old man, has limited options remaining_entrances = ['Old Man Cave (West)', 'Old Man House (Bottom)', 'Death Mountain Return Cave (West)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', 'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Spiral Cave', 'Spiral Cave (Bottom)'] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() remaining_entrances.extend(old_man_entrances) - random.shuffle(remaining_entrances) + world.random.shuffle(remaining_entrances) old_man_entrance = remaining_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) @@ -115,13 +113,13 @@ def link_entrances(world, player): scramble_holes(world, player) # place blacksmith, has limited options - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place bomb shop, has limited options - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) @@ -153,7 +151,7 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in lw_entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) lw_entrances.remove(old_man_exit) @@ -162,7 +160,7 @@ def link_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligable for placement) blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) if blacksmith_hut in lw_entrances: @@ -175,7 +173,7 @@ def link_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligable for placement) bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) if bomb_shop in lw_entrances: @@ -184,7 +182,7 @@ def link_entrances(world, player): dw_entrances.remove(bomb_shop) # place the old man cave's entrance somewhere in the light world - random.shuffle(lw_entrances) + world.random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) @@ -223,10 +221,10 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() lw_entrances.extend(old_man_entrances) - random.shuffle(lw_entrances) + world.random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) @@ -241,13 +239,13 @@ def link_entrances(world, player): scramble_holes(world, player) # place blacksmith, has limited options - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place dam and pyramid fairy, have limited options - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) @@ -278,7 +276,8 @@ def link_entrances(world, player): # must connect front of hyrule castle to do escape connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: - caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + caves.append(tuple(world.random.sample( + ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) lw_entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: @@ -290,14 +289,14 @@ def link_entrances(world, player): # we randomize which world requirements we fulfill first so we get better dungeon distribution #we also places the Old Man House at this time to make sure he can be connected to the desert one way - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: caves += old_man_house connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) try: caves.remove(old_man_house[0]) - except ValueError: + except ValueError: pass - else: #if the cave wasn't placed we get here + else: # if the cave wasn't placed we get here connect_caves(world, lw_entrances, [], old_man_house, player) connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) else: @@ -317,7 +316,7 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in lw_entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) lw_entrances.remove(old_man_exit) @@ -326,7 +325,7 @@ def link_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligable for placement) blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) if blacksmith_hut in lw_entrances: @@ -339,7 +338,7 @@ def link_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligable for placement) bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) if bomb_shop in lw_entrances: @@ -381,7 +380,8 @@ def link_entrances(world, player): # must connect front of hyrule castle to do escape connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: - caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + caves.append(tuple(world.random.sample( + ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: @@ -400,7 +400,7 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) entrances.remove(old_man_exit) @@ -408,7 +408,7 @@ def link_entrances(world, player): # place blacksmith, has limited options # cannot place it anywhere already taken (or that are otherwise not eligable for placement) blacksmith_doors = [door for door in blacksmith_doors if door in entrances] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) entrances.remove(blacksmith_hut) @@ -418,14 +418,14 @@ def link_entrances(world, player): # cannot place it anywhere already taken (or that are otherwise not eligable for placement) bomb_shop_doors = [door for door in bomb_shop_doors if door in entrances] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) entrances.remove(bomb_shop) # place the old man cave's entrance somewhere - random.shuffle(entrances) + world.random.shuffle(entrances) old_man_entrance = entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) @@ -456,7 +456,8 @@ def link_entrances(world, player): # must connect front of hyrule castle to do escape connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) else: - caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + caves.append(tuple(world.random.sample( + ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) lw_entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: @@ -466,7 +467,7 @@ def link_entrances(world, player): caves.append('Ganons Tower Exit') # we randomize which world requirements we fulfill first so we get better dungeon distribution - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player) else: @@ -479,11 +480,11 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in lw_entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() lw_entrances.remove(old_man_exit) - random.shuffle(lw_entrances) + world.random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) @@ -498,13 +499,13 @@ def link_entrances(world, player): scramble_holes(world, player) # place blacksmith, has limited options - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place bomb shop, has limited options - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) @@ -520,18 +521,28 @@ def link_entrances(world, player): dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances) dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) - lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', - 'Lumberjack Tree Cave'] + list(Old_Man_Entrances) - dw_doors = list(DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] + lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave', + 'Bat Cave Cave', + 'North Fairy Cave', + 'Sanctuary', + 'Lost Woods Hideout Stump', + 'Lumberjack Tree Cave'] + list( + Old_Man_Entrances) + dw_doors = list( + DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + [ + 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', + 'Skull Woods Second Section Door (West)'] - random.shuffle(lw_doors) - random.shuffle(dw_doors) + world.random.shuffle(lw_doors) + world.random.shuffle(dw_doors) dw_entrances_must_exits.append('Skull Woods Second Section Door (West)') dw_entrances.append('Skull Woods Second Section Door (East)') dw_entrances.append('Skull Woods First Section Door') - lw_entrances.extend(['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave']) + lw_entrances.extend( + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', + 'Lumberjack Tree Cave']) lw_entrances_must_exits = list(LW_Dungeon_Entrances_Must_Exit) @@ -576,19 +587,20 @@ def link_entrances(world, player): dw_entrances_must_exits.append('Pyramid Entrance') dw_doors.extend(['Ganons Tower', 'Pyramid Entrance']) - random.shuffle(lw_hole_entrances) - random.shuffle(dw_hole_entrances) - random.shuffle(hole_targets) + world.random.shuffle(lw_hole_entrances) + world.random.shuffle(dw_hole_entrances) + world.random.shuffle(hole_targets) # decide if skull woods first section should be in light or dark world - sw_light = random.randint(0, 1) == 0 + sw_light = world.random.randint(0, 1) == 0 if sw_light: sw_hole_pool = lw_hole_entrances mandatory_light_world.append('Skull Woods First Section Exit') else: sw_hole_pool = dw_hole_entrances mandatory_dark_world.append('Skull Woods First Section Exit') - for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']: + for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', + 'Skull Woods First Section (Top)']: connect_entrance(world, sw_hole_pool.pop(), target, player) # sanctuary has to be in light world @@ -619,7 +631,7 @@ def link_entrances(world, player): # now let's deal with mandatory reachable stuff def extract_reachable_exit(cavelist): - random.shuffle(cavelist) + world.random.shuffle(cavelist) candidate = None for cave in cavelist: if isinstance(cave, tuple) and len(cave) > 1: @@ -636,7 +648,7 @@ def link_entrances(world, player): def connect_reachable_exit(entrance, general, worldspecific, worldoors): # select which one is the primary option - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: primary = general secondary = worldspecific else: @@ -656,7 +668,7 @@ def link_entrances(world, player): worldspecific.append(cave) # we randomize which world requirements we fulfill first so we get better dungeon distribution - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: for entrance in lw_entrances_must_exits: connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors) for entrance in dw_entrances_must_exits: @@ -670,7 +682,7 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [entrance for entrance in old_man_entrances if entrance in lw_entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() lw_entrances.remove(old_man_exit) @@ -708,7 +720,7 @@ def link_entrances(world, player): cave_candidate = (i, len(cave)) cave = caves.pop(cave_candidate[0]) - place_lightworld = random.randint(0, 1) == 0 + place_lightworld = world.random.randint(0, 1) == 0 if place_lightworld: target_doors = lw_doors target_entrances = lw_entrances @@ -740,13 +752,13 @@ def link_entrances(world, player): door_targets = list(Single_Cave_Targets) # place blacksmith, has limited options - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place dam and pyramid fairy, have limited options - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) @@ -774,7 +786,7 @@ def link_entrances(world, player): blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors) door_targets = list(Single_Cave_Targets) - random.shuffle(doors) + world.random.shuffle(doors) old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera'] @@ -817,9 +829,9 @@ def link_entrances(world, player): entrances_must_exits.append('Pyramid Entrance') doors.extend(['Ganons Tower', 'Pyramid Entrance']) - random.shuffle(hole_entrances) - random.shuffle(hole_targets) - random.shuffle(entrances) + world.random.shuffle(hole_entrances) + world.random.shuffle(hole_targets) + world.random.shuffle(entrances) # fill up holes for hole in hole_entrances: @@ -838,7 +850,7 @@ def link_entrances(world, player): # now let's deal with mandatory reachable stuff def extract_reachable_exit(cavelist): - random.shuffle(cavelist) + world.random.shuffle(cavelist) candidate = None for cave in cavelist: if isinstance(cave, tuple) and len(cave) > 1: @@ -870,7 +882,7 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() entrances.remove(old_man_exit) @@ -880,14 +892,14 @@ def link_entrances(world, player): # place blacksmith, has limited options blacksmith_doors = [door for door in blacksmith_doors if door in doors] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) doors.remove(blacksmith_hut) # place dam and pyramid fairy, have limited options bomb_shop_doors = [door for door in bomb_shop_doors if door in doors] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) doors.remove(bomb_shop) @@ -913,7 +925,7 @@ def link_entrances(world, player): doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\ DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] - random.shuffle(doors) + world.random.shuffle(doors) old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera'] @@ -952,9 +964,9 @@ def link_entrances(world, player): entrances_must_exits.append('Pyramid Entrance') doors.extend(['Ganons Tower', 'Pyramid Entrance']) - random.shuffle(hole_entrances) - random.shuffle(hole_targets) - random.shuffle(entrances) + world.random.shuffle(hole_entrances) + world.random.shuffle(hole_targets) + world.random.shuffle(entrances) # fill up holes for hole in hole_entrances: @@ -973,7 +985,7 @@ def link_entrances(world, player): # now let's deal with mandatory reachable stuff def extract_reachable_exit(cavelist): - random.shuffle(cavelist) + world.random.shuffle(cavelist) candidate = None for cave in cavelist: if isinstance(cave, tuple) and len(cave) > 1: @@ -1005,7 +1017,7 @@ def link_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() entrances.remove(old_man_exit) @@ -1030,13 +1042,13 @@ def link_entrances(world, player): door_targets = list(Single_Cave_Targets) # place blacksmith, has limited options - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place dam and pyramid fairy, have limited options - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) @@ -1073,7 +1085,7 @@ def link_inverted_entrances(world, player): Old_Man_House = Old_Man_House_Base.copy() Cave_Three_Exits = Cave_Three_Exits_Base.copy() - unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) + unbias_some_entrances(world, Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) # setup mandatory connections for exitname, regionname in inverted_mandatory_connections: @@ -1102,7 +1114,7 @@ def link_inverted_entrances(world, player): dw_entrances = list(Inverted_DW_Dungeon_Entrances) # randomize which desert ledge door is a must-exit - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: lw_dungeon_entrances_must_exit.append('Desert Palace Entrance (North)') lw_entrances.append('Desert Palace Entrance (West)') else: @@ -1123,14 +1135,14 @@ def link_inverted_entrances(world, player): # shuffle aga door first. If it's on HC ledge, remaining HC ledge door must be must-exit all_entrances_aga = lw_entrances + dw_entrances aga_doors = [i for i in all_entrances_aga] - random.shuffle(aga_doors) + world.random.shuffle(aga_doors) aga_door = aga_doors.pop() if aga_door in hc_ledge_entrances: lw_entrances.remove(aga_door) hc_ledge_entrances.remove(aga_door) - - random.shuffle(hc_ledge_entrances) + + world.random.shuffle(hc_ledge_entrances) hc_ledge_must_exit = hc_ledge_entrances.pop() lw_entrances.remove(hc_ledge_must_exit) lw_dungeon_entrances_must_exit.append(hc_ledge_must_exit) @@ -1162,9 +1174,9 @@ def link_inverted_entrances(world, player): # we shuffle all 2 entrance caves as pairs as a start # start with the ones that need to be directed two_door_caves = list(Inverted_Two_Door_Caves_Directional) - random.shuffle(two_door_caves) - random.shuffle(caves) - while two_door_caves: + world.random.shuffle(two_door_caves) + world.random.shuffle(caves) + while two_door_caves: entrance1, entrance2 = two_door_caves.pop() exit1, exit2 = caves.pop() connect_two_way(world, entrance1, exit1, player) @@ -1172,7 +1184,7 @@ def link_inverted_entrances(world, player): # now the remaining pairs two_door_caves = list(Inverted_Two_Door_Caves) - random.shuffle(two_door_caves) + world.random.shuffle(two_door_caves) while two_door_caves: entrance1, entrance2 = two_door_caves.pop() exit1, exit2 = caves.pop() @@ -1180,8 +1192,9 @@ def link_inverted_entrances(world, player): connect_two_way(world, entrance2, exit2, player) # place links house - links_house_doors = [i for i in bomb_shop_doors + blacksmith_doors if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] - links_house = random.choice(list(links_house_doors)) + links_house_doors = [i for i in bomb_shop_doors + blacksmith_doors if + i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] + links_house = world.random.choice(list(links_house_doors)) connect_two_way(world, links_house, 'Inverted Links House Exit', player) if links_house in bomb_shop_doors: bomb_shop_doors.remove(links_house) @@ -1192,7 +1205,7 @@ def link_inverted_entrances(world, player): # place dark sanc sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in bomb_shop_doors] - sanc_door = random.choice(sanc_doors) + sanc_door = world.random.choice(sanc_doors) bomb_shop_doors.remove(sanc_door) connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player) @@ -1205,7 +1218,7 @@ def link_inverted_entrances(world, player): # place old man, bumper cave bottom to DDM entrances not in east bottom - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) @@ -1225,14 +1238,14 @@ def link_inverted_entrances(world, player): # place blacksmith, has limited options blacksmith_doors = [door for door in blacksmith_doors[:]] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) bomb_shop_doors.extend(blacksmith_doors) # place bomb shop, has limited options bomb_shop_doors = [door for door in bomb_shop_doors[:]] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) single_doors.extend(bomb_shop_doors) @@ -1257,8 +1270,9 @@ def link_inverted_entrances(world, player): door_targets = list(Inverted_Single_Cave_Targets) # place links house - links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] - links_house = random.choice(list(links_house_doors)) + links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if + i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] + links_house = world.random.choice(list(links_house_doors)) connect_two_way(world, links_house, 'Inverted Links House Exit', player) if links_house in lw_entrances: lw_entrances.remove(links_house) @@ -1269,7 +1283,7 @@ def link_inverted_entrances(world, player): # place dark sanc sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances] - sanc_door = random.choice(sanc_doors) + sanc_door = world.random.choice(sanc_doors) dw_entrances.remove(sanc_door) connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player) world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region) @@ -1283,7 +1297,7 @@ def link_inverted_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in dw_entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) dw_entrances.remove(old_man_exit) @@ -1292,7 +1306,7 @@ def link_inverted_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligible for placement) blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) if blacksmith_hut in lw_entrances: @@ -1305,7 +1319,7 @@ def link_inverted_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligible for placement) bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) if bomb_shop in lw_entrances: @@ -1314,7 +1328,7 @@ def link_inverted_entrances(world, player): dw_entrances.remove(bomb_shop) # place the old man cave's entrance somewhere in the dark world - random.shuffle(dw_entrances) + world.random.shuffle(dw_entrances) old_man_entrance = dw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) @@ -1341,7 +1355,7 @@ def link_inverted_entrances(world, player): old_man_house = list(Old_Man_House) # randomize which desert ledge door is a must-exit - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: lw_must_exits.append('Desert Palace Entrance (North)') lw_entrances.append('Desert Palace Entrance (West)') else: @@ -1351,7 +1365,8 @@ def link_inverted_entrances(world, player): # tavern back door cannot be shuffled yet connect_doors(world, ['Tavern North'], ['Tavern'], player) - caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + caves.append(tuple(world.random.sample( + ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) lw_entrances.append('Hyrule Castle Entrance (South)') @@ -1366,14 +1381,14 @@ def link_inverted_entrances(world, player): # shuffle aga door first. if it's on hc ledge, then one other hc ledge door has to be must_exit all_entrances_aga = lw_entrances + dw_entrances aga_doors = [i for i in all_entrances_aga] - random.shuffle(aga_doors) + world.random.shuffle(aga_doors) aga_door = aga_doors.pop() if aga_door in hc_ledge_entrances: lw_entrances.remove(aga_door) hc_ledge_entrances.remove(aga_door) - - random.shuffle(hc_ledge_entrances) + + world.random.shuffle(hc_ledge_entrances) hc_ledge_must_exit = hc_ledge_entrances.pop() lw_entrances.remove(hc_ledge_must_exit) lw_must_exits.append(hc_ledge_must_exit) @@ -1387,8 +1402,9 @@ def link_inverted_entrances(world, player): caves.remove('Inverted Agahnims Tower Exit') # place links house - links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] - links_house = random.choice(list(links_house_doors)) + links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if + i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] + links_house = world.random.choice(list(links_house_doors)) connect_two_way(world, links_house, 'Inverted Links House Exit', player) if links_house in lw_entrances: lw_entrances.remove(links_house) @@ -1399,21 +1415,21 @@ def link_inverted_entrances(world, player): # place dark sanc sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances] - sanc_door = random.choice(sanc_doors) + sanc_door = world.random.choice(sanc_doors) dw_entrances.remove(sanc_door) connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player) world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region) # place old man house # no dw must exits in inverted, but we randomize whether cave is in light or dark world - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: caves += old_man_house connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player) try: caves.remove(old_man_house[0]) - except ValueError: + except ValueError: pass - else: #if the cave wasn't placed we get here + else: # if the cave wasn't placed we get here connect_caves(world, lw_entrances, [], old_man_house, player) else: connect_caves(world, dw_entrances, [], old_man_house, player) @@ -1422,7 +1438,7 @@ def link_inverted_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in dw_entrances + lw_entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) if old_man_exit in dw_entrances: @@ -1436,7 +1452,7 @@ def link_inverted_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligible for placement) blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) if blacksmith_hut in lw_entrances: @@ -1449,7 +1465,7 @@ def link_inverted_entrances(world, player): all_entrances = lw_entrances + dw_entrances # cannot place it anywhere already taken (or that are otherwise not eligible for placement) bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) if bomb_shop in lw_entrances: @@ -1459,11 +1475,11 @@ def link_inverted_entrances(world, player): # place the old man cave's entrance somewhere in the same world he'll exit from if old_man_world == 'light': - random.shuffle(lw_entrances) + world.random.shuffle(lw_entrances) old_man_entrance = lw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) elif old_man_world == 'dark': - random.shuffle(dw_entrances) + world.random.shuffle(dw_entrances) old_man_entrance = dw_entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) @@ -1490,14 +1506,15 @@ def link_inverted_entrances(world, player): door_targets = list(Inverted_Single_Cave_Targets) # randomize which desert ledge door is a must-exit - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: must_exits.append('Desert Palace Entrance (North)') entrances.append('Desert Palace Entrance (West)') else: must_exits.append('Desert Palace Entrance (West)') entrances.append('Desert Palace Entrance (North)') - caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) + caves.append(tuple(world.random.sample( + ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: @@ -1509,12 +1526,12 @@ def link_inverted_entrances(world, player): hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Inverted Ganons Tower'] # shuffle aga door. if it's on hc ledge, then one other hc ledge door has to be must_exit - aga_door = random.choice(list(entrances)) + aga_door = world.random.choice(list(entrances)) if aga_door in hc_ledge_entrances: hc_ledge_entrances.remove(aga_door) - - random.shuffle(hc_ledge_entrances) + + world.random.shuffle(hc_ledge_entrances) hc_ledge_must_exit = hc_ledge_entrances.pop() entrances.remove(hc_ledge_must_exit) must_exits.append(hc_ledge_must_exit) @@ -1525,8 +1542,9 @@ def link_inverted_entrances(world, player): # place links house - links_house_doors = [i for i in entrances + must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] - links_house = random.choice(list(links_house_doors)) + links_house_doors = [i for i in entrances + must_exits if + i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] + links_house = world.random.choice(list(links_house_doors)) connect_two_way(world, links_house, 'Inverted Links House Exit', player) if links_house in entrances: entrances.remove(links_house) @@ -1535,7 +1553,7 @@ def link_inverted_entrances(world, player): # place dark sanc sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances] - sanc_door = random.choice(sanc_doors) + sanc_door = world.random.choice(sanc_doors) entrances.remove(sanc_door) connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player) world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region) @@ -1551,7 +1569,7 @@ def link_inverted_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [door for door in old_man_entrances if door in entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) entrances.remove(old_man_exit) @@ -1559,7 +1577,7 @@ def link_inverted_entrances(world, player): # place blacksmith, has limited options # cannot place it anywhere already taken (or that are otherwise not eligible for placement) blacksmith_doors = [door for door in blacksmith_doors if door in entrances] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) entrances.remove(blacksmith_hut) @@ -1568,13 +1586,13 @@ def link_inverted_entrances(world, player): # cannot place it anywhere already taken (or that are otherwise not eligible for placement) bomb_shop_doors = [door for door in bomb_shop_doors if door in entrances] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) entrances.remove(bomb_shop) # place the old man cave's entrance somewhere - random.shuffle(entrances) + world.random.shuffle(entrances) old_man_entrance = entrances.pop() connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) @@ -1597,7 +1615,7 @@ def link_inverted_entrances(world, player): Inverted_LW_Single_Cave_Doors + Inverted_DW_Single_Cave_Doors + ['Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'] # randomize which desert ledge door is a must-exit - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: entrances_must_exits.append('Desert Palace Entrance (North)') entrances.append('Desert Palace Entrance (West)') else: @@ -1612,7 +1630,7 @@ def link_inverted_entrances(world, player): blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Inverted_Blacksmith_Multi_Cave_Doors) door_targets = list(Inverted_Single_Cave_Targets) - random.shuffle(doors) + world.random.shuffle(doors) old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances) + ['Tower of Hera', 'Inverted Agahnims Tower'] @@ -1646,9 +1664,9 @@ def link_inverted_entrances(world, player): hole_targets.append('Pyramid') doors.extend(['Inverted Ganons Tower', 'Inverted Pyramid Entrance']) - random.shuffle(hole_entrances) - random.shuffle(hole_targets) - random.shuffle(entrances) + world.random.shuffle(hole_entrances) + world.random.shuffle(hole_targets) + world.random.shuffle(entrances) # fill up holes for hole in hole_entrances: @@ -1656,10 +1674,11 @@ def link_inverted_entrances(world, player): doors.append('Hyrule Castle Entrance (South)') caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) - + # place links house and dark sanc - links_house_doors = [i for i in entrances + entrances_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] - links_house = random.choice(list(links_house_doors)) + links_house_doors = [i for i in entrances + entrances_must_exits if + i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] + links_house = world.random.choice(list(links_house_doors)) connect_two_way(world, links_house, 'Inverted Links House Exit', player) if links_house in entrances: entrances.remove(links_house) @@ -1668,7 +1687,7 @@ def link_inverted_entrances(world, player): doors.remove(links_house) sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances] - sanc_door = random.choice(sanc_doors) + sanc_door = world.random.choice(sanc_doors) entrances.remove(sanc_door) doors.remove(sanc_door) connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player) @@ -1676,7 +1695,7 @@ def link_inverted_entrances(world, player): # now let's deal with mandatory reachable stuff def extract_reachable_exit(cavelist): - random.shuffle(cavelist) + world.random.shuffle(cavelist) candidate = None for cave in cavelist: if isinstance(cave, tuple) and len(cave) > 1: @@ -1708,7 +1727,7 @@ def link_inverted_entrances(world, player): # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances] - random.shuffle(old_man_entrances) + world.random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() entrances.remove(old_man_exit) @@ -1718,14 +1737,14 @@ def link_inverted_entrances(world, player): # place blacksmith, has limited options blacksmith_doors = [door for door in blacksmith_doors if door in doors] - random.shuffle(blacksmith_doors) + world.random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) doors.remove(blacksmith_hut) # place dam and pyramid fairy, have limited options bomb_shop_doors = [door for door in bomb_shop_doors if door in doors] - random.shuffle(bomb_shop_doors) + world.random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) doors.remove(bomb_shop) @@ -1843,14 +1862,14 @@ def scramble_holes(world, player): if world.shuffle[player] == 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) if world.shuffle_ganon: - random.shuffle(hole_targets) + world.random.shuffle(hole_targets) exit, target = hole_targets.pop() connect_two_way(world, 'Pyramid Entrance', exit, player) connect_entrance(world, 'Pyramid Hole', target, player) if world.shuffle[player] != 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) - random.shuffle(hole_targets) + world.random.shuffle(hole_targets) for entrance, drop in hole_entrances: exit, target = hole_targets.pop() connect_two_way(world, entrance, exit, player) @@ -1885,14 +1904,14 @@ def scramble_inverted_holes(world, player): if world.shuffle[player] == 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) if world.shuffle_ganon: - random.shuffle(hole_targets) + world.random.shuffle(hole_targets) exit, target = hole_targets.pop() connect_two_way(world, 'Inverted Pyramid Entrance', exit, player) connect_entrance(world, 'Inverted Pyramid Hole', target, player) if world.shuffle[player] != 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) - random.shuffle(hole_targets) + world.random.shuffle(hole_targets) for entrance, drop in hole_entrances: exit, target = hole_targets.pop() connect_two_way(world, entrance, exit, player) @@ -1900,7 +1919,7 @@ def scramble_inverted_holes(world, player): def connect_random(world, exitlist, targetlist, player, two_way=False): targetlist = list(targetlist) - random.shuffle(targetlist) + world.random.shuffle(targetlist) for exit, target in zip(exitlist, targetlist): if two_way: @@ -1927,15 +1946,17 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): entrances.append(entrance) """This works inplace""" - random.shuffle(entrances) - random.shuffle(caves) + world.random.shuffle(entrances) + world.random.shuffle(caves) # Handle inverted Aga Tower - if it depends on connections, then so does Hyrule Castle Ledge if world.mode[player] == 'inverted': for entrance in invalid_connections: - if world.get_entrance(entrance, player).connected_region == world.get_region('Inverted Agahnims Tower', player): + if world.get_entrance(entrance, player).connected_region == world.get_region('Inverted Agahnims Tower', + player): for exit in invalid_connections[entrance]: - invalid_connections[exit] = invalid_connections[exit].union({'Inverted Ganons Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'}) + invalid_connections[exit] = invalid_connections[exit].union( + {'Inverted Ganons Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'}) break used_caves = [] @@ -1984,7 +2005,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): else: required_entrances += len(cave)-1 caves.append(cave[0:-1]) - random.shuffle(caves) + world.random.shuffle(caves) used_caves.append(cave[0:-1]) invalid_cave_connections[tuple(cave[0:-1])] = invalid_cave_connections[tuple(cave)].union(invalid_connections[exit]) caves.remove(cave) @@ -2000,9 +2021,9 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): def connect_caves(world, lw_entrances, dw_entrances, caves, player): """This works inplace""" - random.shuffle(lw_entrances) - random.shuffle(dw_entrances) - random.shuffle(caves) + world.random.shuffle(lw_entrances) + world.random.shuffle(dw_entrances) + world.random.shuffle(caves) while caves: # connect highest exit count caves first, prevent issue where we have 2 or 3 exits accross worlds left to fill cave_candidate = (None, 0) @@ -2013,7 +2034,7 @@ def connect_caves(world, lw_entrances, dw_entrances, caves, player): cave_candidate = (i, len(cave)) cave = caves.pop(cave_candidate[0]) - target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances + target = lw_entrances if world.random.randint(0, 1) == 0 else dw_entrances if isinstance(cave, str): cave = (cave,) @@ -2028,8 +2049,8 @@ def connect_caves(world, lw_entrances, dw_entrances, caves, player): def connect_doors(world, doors, targets, player): """This works inplace""" - random.shuffle(doors) - random.shuffle(targets) + world.random.shuffle(doors) + world.random.shuffle(targets) while doors: door = doors.pop() target = targets.pop() @@ -2066,7 +2087,7 @@ def simple_shuffle_dungeons(world, player): multi_dungeons = ['Desert', 'Turtle Rock'] if world.mode[player] == 'open' or (world.mode[player] == 'inverted' and world.shuffle_ganon): multi_dungeons.append('Hyrule Castle') - random.shuffle(multi_dungeons) + world.random.shuffle(multi_dungeons) dp_target = multi_dungeons[0] tr_target = multi_dungeons[1] @@ -2175,11 +2196,12 @@ def simple_shuffle_dungeons(world, player): connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)', player) connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)', player) -def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits): + +def unbias_some_entrances(world, Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits): def shuffle_lists_in_list(ls): for i, item in enumerate(ls): if isinstance(item, list): - ls[i] = random.sample(item, len(item)) + ls[i] = world.random.sample(item, len(item)) def tuplize_lists_in_list(ls): for i, item in enumerate(ls): @@ -2193,7 +2215,7 @@ def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_E # paradox fixup if Cave_Three_Exits[1][0] == "Paradox Cave Exit (Bottom)": - i = random.randint(1,2) + i = world.random.randint(1, 2) Cave_Three_Exits[1][0] = Cave_Three_Exits[1][i] Cave_Three_Exits[1][i] = "Paradox Cave Exit (Bottom)" diff --git a/Fill.py b/Fill.py index 249abf16..6d94946c 100644 --- a/Fill.py +++ b/Fill.py @@ -1,4 +1,3 @@ -import random import logging from BaseClasses import CollectionState @@ -10,10 +9,10 @@ class FillError(RuntimeError): def distribute_items_cutoff(world, cutoffrate=0.33): # get list of locations to fill in fill_locations = world.get_unfilled_locations() - random.shuffle(fill_locations) + world.random.shuffle(fill_locations) # get items to distribute - random.shuffle(world.itempool) + world.random.shuffle(world.itempool) itempool = world.itempool total_advancement_items = len([item for item in itempool if item.advancement]) @@ -83,10 +82,10 @@ def distribute_items_cutoff(world, cutoffrate=0.33): def distribute_items_staleness(world): # get list of locations to fill in fill_locations = world.get_unfilled_locations() - random.shuffle(fill_locations) + world.random.shuffle(fill_locations) # get items to distribute - random.shuffle(world.itempool) + world.random.shuffle(world.itempool) itempool = world.itempool progress_done = False @@ -131,7 +130,7 @@ def distribute_items_staleness(world): spot_to_fill = None for location in fill_locations: # increase likelyhood of skipping a location if it has been found stale - if not progress_done and random.randint(0, location.staleness_count) > 2: + if not progress_done and world.random.randint(0, location.staleness_count) > 2: continue if location.can_fill(world.state, item_to_place): @@ -215,10 +214,10 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # If not passed in, then get a shuffled list of locations to fill in if not fill_locations: fill_locations = world.get_unfilled_locations() - random.shuffle(fill_locations) + world.random.shuffle(fill_locations) # get items to distribute - random.shuffle(world.itempool) + world.random.shuffle(world.itempool) progitempool = [] localprioitempool = {player: [] for player in range(1, world.players + 1)} localrestitempool = {player: [] for player in range(1, world.players + 1)} @@ -244,17 +243,17 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None continue gftower_trash_count = ( - random.randint(15, 50) if 'triforcehunt' in world.goal[player] - else random.randint(0, 15)) + world.random.randint(15, 50) if 'triforcehunt' in world.goal[player] + else world.random.randint(0, 15)) gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player] - random.shuffle(gtower_locations) + world.random.shuffle(gtower_locations) trashcnt = 0 localrest = localrestitempool[player] if localrest: gt_item_pool = restitempool + localrest - random.shuffle(gt_item_pool) + world.random.shuffle(gt_item_pool) else: gt_item_pool = restitempool.copy() @@ -269,7 +268,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None fill_locations.remove(spot_to_fill) trashcnt += 1 - random.shuffle(fill_locations) + world.random.shuffle(fill_locations) fill_locations.reverse() # Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots @@ -283,20 +282,20 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None localprioitempool.values() or localrestitempool.values()): # we need to make sure some fills are limited to certain worlds for player, items in localprioitempool.items(): # items already shuffled local_locations = [location for location in fill_locations if location.player == player] - random.shuffle(local_locations) + world.random.shuffle(local_locations) for item_to_place in items: spot_to_fill = local_locations.pop() world.push_item(spot_to_fill, item_to_place, False) fill_locations.remove(spot_to_fill) for player, items in localrestitempool.items(): # items already shuffled local_locations = [location for location in fill_locations if location.player == player] - random.shuffle(local_locations) + world.random.shuffle(local_locations) for item_to_place in items: spot_to_fill = local_locations.pop() world.push_item(spot_to_fill, item_to_place, False) fill_locations.remove(spot_to_fill) - random.shuffle(fill_locations) + world.random.shuffle(fill_locations) fast_fill(world, prioitempool, fill_locations) @@ -314,7 +313,7 @@ def fast_fill(world, item_pool, fill_locations): def flood_items(world): # get items to distribute - random.shuffle(world.itempool) + world.random.shuffle(world.itempool) itempool = world.itempool progress_done = False @@ -324,7 +323,7 @@ def flood_items(world): # fill world from top of itempool while we can while not progress_done: location_list = world.get_unfilled_locations() - random.shuffle(location_list) + world.random.shuffle(location_list) spot_to_fill = None for location in location_list: if location.can_fill(world.state, itempool[0]): @@ -360,7 +359,7 @@ def flood_items(world): # find item to replace with progress item location_list = world.get_reachable_locations() - random.shuffle(location_list) + world.random.shuffle(location_list) for location in location_list: if location.item is not None and not location.item.advancement and not location.item.priority and not location.item.smallkey and not location.item.bigkey: # safe to replace @@ -380,7 +379,7 @@ def balance_multiworld_progression(world): state = CollectionState(world) checked_locations = [] unchecked_locations = world.get_locations().copy() - random.shuffle(unchecked_locations) + world.random.shuffle(unchecked_locations) reachable_locations_count = {player: 0 for player in range(1, world.players + 1)} diff --git a/ItemList.py b/ItemList.py index c6922287..b9e52e5b 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1,6 +1,5 @@ from collections import namedtuple import logging -import random from BaseClasses import Region, RegionType, Shop, ShopType, Location from Bosses import place_bosses @@ -181,10 +180,7 @@ def generate_itempool(world, player): # set up item pool if world.custom: (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, - lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive[player], world.shuffle[player], - world.difficulty[player], world.timer[player], world.goal[player], - world.mode[player], world.swords[player], - world.retro[player], world.customitemarray) + lamps_needed_for_dark_rooms) = make_custom_item_pool(world, player) world.rupoor_cost = min(world.customitemarray[69], 9999) else: (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, @@ -209,7 +205,7 @@ def generate_itempool(world, player): if item in ['Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']: if item not in possible_weapons: possible_weapons.append(item) - starting_weapon = random.choice(possible_weapons) + starting_weapon = world.random.choice(possible_weapons) placed_items["Link's Uncle"] = starting_weapon pool.remove(starting_weapon) if placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria', 'Cane of Byrna'] and world.enemy_health[player] not in ['default', 'easy']: @@ -255,24 +251,25 @@ def generate_itempool(world, player): 4: {'trap': 100}} def beemizer(item): if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type: - choice = random.choices(list(beeweights[world.beemizer[item.player]].keys()), weights=list(beeweights[world.beemizer[item.player]].values()))[0] + choice = world.random.choices(list(beeweights[world.beemizer[item.player]].keys()), + weights=list(beeweights[world.beemizer[item.player]].values()))[0] return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player) return item progressionitems = [item for item in items if item.advancement or item.priority or item.type] nonprogressionitems = [beemizer(item) for item in items if not item.advancement and not item.priority and not item.type] - random.shuffle(nonprogressionitems) + world.random.shuffle(nonprogressionitems) triforce_pieces = world.triforce_pieces_available[player] if 'triforcehunt' in world.goal[player] and triforce_pieces > 30: progressionitems += [ItemFactory("Triforce Piece", player)] * (triforce_pieces - 30) - nonprogressionitems = nonprogressionitems[(triforce_pieces-30):] + nonprogressionitems = nonprogressionitems[(triforce_pieces - 30):] world.itempool += progressionitems + nonprogressionitems # shuffle medallions - mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] - tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] + mm_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)] + tr_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)] world.required_medallions[player] = (mm_medallion, tr_medallion) place_bosses(world, player) @@ -297,7 +294,7 @@ def set_up_take_anys(world, player): if world.mode[player] == 'inverted' and 'Dark Sanctuary Hint' in take_any_locations: take_any_locations.remove('Dark Sanctuary Hint') - regions = random.sample(take_any_locations, 5) + regions = world.random.sample(take_any_locations, 5) old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player) world.regions.append(old_man_take_any) @@ -312,7 +309,7 @@ def set_up_take_anys(world, player): swords = [item for item in world.itempool if item.type == 'Sword' and item.player == player] if swords: - sword = random.choice(swords) + sword = world.random.choice(swords) world.itempool.remove(sword) world.itempool.append(ItemFactory('Rupees (20)', player)) old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True) @@ -324,7 +321,7 @@ def set_up_take_anys(world, player): world.regions.append(take_any) world.dynamic_regions.append(take_any) - target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) + target, room_id = world.random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) reg = regions.pop() entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance.name, take_any.name, player) @@ -368,8 +365,8 @@ def fill_prizes(world, attempts=15): try: prizepool = list(unplaced_prizes) prize_locs = list(empty_crystal_locations) - random.shuffle(prizepool) - random.shuffle(prize_locs) + world.random.shuffle(prizepool) + world.random.shuffle(prize_locs) fill_restrictive(world, all_state, prize_locs, prizepool, True) except FillError as e: logging.getLogger('').exception("Failed to place dungeon prizes (%s). Will retry %s more times", e, @@ -389,7 +386,9 @@ 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.type == ShopType.Shop and s.region.player == player], 5): + for shop in world.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) @@ -422,7 +421,7 @@ def get_pool_core(world, player: int): placed_items[loc] = item def want_progressives(): - return random.choice([True, False]) if progressive == 'random' else progressive == 'on' + return world.random.choice([True, False]) if progressive == 'random' else progressive == 'on' # provide boots to major glitch dependent seeds if logic in {'owglitches', 'nologic'} and world.glitch_boots[player]: @@ -455,10 +454,10 @@ def get_pool_core(world, player: int): # expert+ difficulties produce the same contents for # all bottles, since only one bottle is available if diff.same_bottle: - thisbottle = random.choice(diff.bottles) + thisbottle = world.random.choice(diff.bottles) for _ in range(diff.bottle_count): if not diff.same_bottle: - thisbottle = random.choice(diff.bottles) + thisbottle = world.random.choice(diff.bottles) pool.append(thisbottle) if want_progressives(): @@ -482,7 +481,7 @@ def get_pool_core(world, player: int): pool.extend(diff.swordless) elif swords == 'vanilla': swords_to_use = diff.progressivesword.copy() if want_progressives() else diff.basicsword.copy() - random.shuffle(swords_to_use) + world.random.shuffle(swords_to_use) place_item('Link\'s Uncle', swords_to_use.pop()) place_item('Blacksmith', swords_to_use.pop()) @@ -535,13 +534,24 @@ def get_pool_core(world, player: int): pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool] pool.extend(diff.retro) if mode == 'standard': - key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) + key_location = world.random.choice( + ['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', + 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) place_item(key_location, 'Small Key (Universal)') else: pool.extend(['Small Key (Universal)']) return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) -def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray): + +def make_custom_item_pool(world, player): + shuffle = world.shuffle[player] + difficulty = world.difficulty[player] + timer = world.timer[player] + goal = world.goal[player] + mode = world.mode[player] + retro = world.retro[player] + customitemarray = world.customitemarray[player] + pool = [] placed_items = {} precollected_items = [] @@ -636,10 +646,10 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s # expert+ difficulties produce the same contents for # all bottles, since only one bottle is available if diff.same_bottle: - thisbottle = random.choice(diff.bottles) + thisbottle = world.random.choice(diff.bottles) for _ in range(customitemarray[18]): if not diff.same_bottle: - thisbottle = random.choice(diff.bottles) + thisbottle = world.random.choice(diff.bottles) pool.append(thisbottle) if customitemarray[66] > 0 or customitemarray[67] > 0: @@ -664,7 +674,9 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s if mode == 'standard': if retro: - key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) + key_location = world.random.choice( + ['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', + 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) place_item(key_location, 'Small Key (Universal)') pool.extend(['Small Key (Universal)'] * max((customitemarray[70] - 1), 0)) else: diff --git a/Main.py b/Main.py index 415807a5..9ef746f8 100644 --- a/Main.py +++ b/Main.py @@ -43,17 +43,25 @@ def main(args, seed=None): world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive.copy(), args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) + logger = logging.getLogger('') world.seed = get_seed(seed) - random.seed(world.seed) + if args.race: + world.secure() + else: + world.random.seed(world.seed) world.remote_items = args.remote_items.copy() world.mapshuffle = args.mapshuffle.copy() world.compassshuffle = args.compassshuffle.copy() world.keyshuffle = args.keyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy() - world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)} - world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)} + world.crystals_needed_for_ganon = { + player: world.random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int( + args.crystals_ganon[player]) for player in range(1, world.players + 1)} + world.crystals_needed_for_gt = { + player: world.random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) + for player in range(1, world.players + 1)} world.open_pyramid = args.openpyramid.copy() world.boss_shuffle = args.shufflebosses.copy() world.enemy_shuffle = args.shuffleenemies.copy() @@ -69,7 +77,7 @@ def main(args, seed=None): world.triforce_pieces_required = args.triforce_pieces_required.copy() world.progression_balancing = {player: not balance for player, balance in args.skip_progression_balancing.items()} - world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} + world.rom_seeds = {player: world.random.randint(0, 999999999) for player in range(1, world.players + 1)} logger.info('ALttP Berserker\'s Multiworld Version %s - Seed: %s\n', __version__, world.seed) @@ -134,7 +142,7 @@ def main(args, seed=None): if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) + list(args.keyshuffle.values()) + list(args.bigkeyshuffle.values())): shuffled_locations = world.get_unfilled_locations() - random.shuffle(shuffled_locations) + world.random.shuffle(shuffled_locations) fill_dungeons_restrictive(world, shuffled_locations) else: fill_dungeons(world) diff --git a/MultiServer.py b/MultiServer.py index 5ed4defa..4fe93d09 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -12,6 +12,7 @@ import inspect import weakref import datetime import threading +import random import ModuleUpdate @@ -830,7 +831,6 @@ class ClientMessageProcessor(CommonCommandProcessor): can_pay = points_available // self.ctx.hint_cost else: can_pay = 1000 - import random random.shuffle(not_found_hints) diff --git a/Mystery.py b/Mystery.py index 2b1e597c..972ed92c 100644 --- a/Mystery.py +++ b/Mystery.py @@ -56,6 +56,9 @@ def main(args=None): seed = get_seed(args.seed) random.seed(seed) + if args.race: + random.seed() # reset to time-based random source + seedname = "M" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits)) print(f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}") diff --git a/Rom.py b/Rom.py index a7f0d498..b752e16f 100644 --- a/Rom.py +++ b/Rom.py @@ -236,8 +236,9 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite 'BeesLevel': 0, 'RandomizeTileTrapPattern': world.enemy_shuffle[player] == 'chaos', 'RandomizeTileTrapFloorTile': False, - 'AllowKillableThief': bool(random.randint(0, 1)) if 'thieves' in world.enemy_shuffle[player] else + 'AllowKillableThief': bool(world.random.randint(0, 1)) if 'thieves' in world.enemy_shuffle[player] else world.enemy_shuffle[player] != 'none', + # TODO: this is currently non-deterministic due to being called in a thread 'RandomizeSpriteOnHit': random_sprite_on_hit, 'DebugMode': False, 'DebugForceEnemy': False, @@ -289,7 +290,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite if sprites: while len(sprites) < 32: sprites.extend(sprites) - random.shuffle(sprites) + world.random.shuffle(sprites) for i, path in enumerate(sprites[:32]): sprite = Sprite(path) @@ -1606,9 +1607,9 @@ def write_strings(rom, world, player, team): if world.hints[player]: tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!' hint_locations = HintLocations.copy() - random.shuffle(hint_locations) + world.random.shuffle(hint_locations) all_entrances = [entrance for entrance in world.get_entrances() if entrance.player == player] - random.shuffle(all_entrances) + world.random.shuffle(all_entrances) #First we take care of the one inconvenient dungeon in the appropriately simple shuffles. entrances_to_hint = {} @@ -1683,12 +1684,12 @@ def write_strings(rom, world, player, team): locations_to_hint = InconvenientLocations.copy() if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: locations_to_hint.extend(InconvenientVanillaLocations) - random.shuffle(locations_to_hint) + world.random.shuffle(locations_to_hint) hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 5 del locations_to_hint[hint_count:] for location in locations_to_hint: if location == 'Swamp Left': - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: first_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item) second_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item) else: @@ -1697,7 +1698,7 @@ def write_strings(rom, world, player, team): this_hint = ('The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + '.') tt[hint_locations.pop(0)] = this_hint elif location == 'Mire Left': - if random.randint(0, 1) == 0: + if world.random.randint(0, 1) == 0: first_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item) second_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item) else: @@ -1736,12 +1737,12 @@ def write_strings(rom, world, player, team): items_to_hint.extend(SmallKeys) if world.bigkeyshuffle[player]: items_to_hint.extend(BigKeys) - random.shuffle(items_to_hint) + world.random.shuffle(items_to_hint) hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8 while hint_count > 0: this_item = items_to_hint.pop(0) this_location = world.find_items(this_item, player) - random.shuffle(this_location) + world.random.shuffle(this_location) #This looks dumb but prevents hints for Skull Woods Pinball Room's key safely with any item pool. if this_location: if this_location[0].name == 'Skull Woods - Pinball Room': @@ -1753,7 +1754,7 @@ def write_strings(rom, world, player, team): # All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint isn't selected twice. junk_hints = junk_texts.copy() - random.shuffle(junk_hints) + world.random.shuffle(junk_hints) for location in hint_locations: tt[location] = junk_hints.pop(0) @@ -1761,7 +1762,7 @@ def write_strings(rom, world, player, team): silverarrows = world.find_items('Silver Bow', player) - random.shuffle(silverarrows) + world.random.shuffle(silverarrows) silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint @@ -1775,7 +1776,8 @@ def write_strings(rom, world, player, team): 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!' + silverarrow_hint = (' %s?' % hint_text(world.random.choice(prog_bow_locs)).replace('Ganon\'s', + 'my')) if progressive_silvers else '?\nI think not!' tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint @@ -1786,19 +1788,23 @@ def write_strings(rom, world, player, team): greenpendant = world.find_items('Green Pendant', player)[0] tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text - tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt[player] == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt[player] - tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon[player] == 1 else 'You need %d crystals to beat Ganon.') % world.crystals_needed_for_ganon[player] + tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt[ + player] == 1 else 'You need %d crystals to enter.') % \ + world.crystals_needed_for_gt[player] + tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon[ + player] == 1 else 'You need %d crystals to beat Ganon.') % \ + world.crystals_needed_for_ganon[player] if world.goal[player] in ['dungeons']: tt['sign_ganon'] = 'You need to complete all the dungeons.' - tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)] - tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)] - tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)] + tt['uncle_leaving_text'] = Uncle_texts[world.random.randint(0, len(Uncle_texts) - 1)] + tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[world.random.randint(0, len(Triforce_texts) - 1)] + tt['bomb_shop_big_bomb'] = BombShop2_texts[world.random.randint(0, len(BombShop2_texts) - 1)] # this is what shows after getting the green pendant item in rando - tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[random.randint(0, len(Sahasrahla2_texts) - 1)] - tt['blind_by_the_light'] = Blind_texts[random.randint(0, len(Blind_texts) - 1)] + tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[world.random.randint(0, len(Sahasrahla2_texts) - 1)] + tt['blind_by_the_light'] = Blind_texts[world.random.randint(0, len(Blind_texts) - 1)] if world.goal[player] in ['triforcehunt', 'localtriforcehunt']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' @@ -1807,14 +1813,15 @@ def write_strings(rom, world, player, team): tt['sign_ganon'] = 'Go find the Triforce pieces with your friends... Ganon is invincible!' else: tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' - tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % \ + tt[ + 'murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % \ world.treasure_hunt_count[player] elif world.goal[player] in ['pedestal']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' tt['sign_ganon'] = 'You need to get to the pedestal... Ganon is invincible!' else: - tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)] + tt['ganon_fall_in'] = Ganon1_texts[world.random.randint(0, len(Ganon1_texts) - 1)] tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!' tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!' if world.goal[player] == 'ganontriforcehunt' and world.players > 1: @@ -1822,7 +1829,7 @@ def write_strings(rom, world, player, team): elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: tt['sign_ganon'] = 'You need to find %d Triforce pieces to defeat Ganon.' % world.treasure_hunt_count[player] - tt['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)] + tt['kakariko_tavern_fisherman'] = TavernMan_texts[world.random.randint(0, len(TavernMan_texts) - 1)] pedestalitem = world.get_location('Master Sword Pedestal', player).item pedestal_text = 'Some Hot Air' if pedestalitem is None else hint_text(pedestalitem, True) if pedestalitem.pedestal_hint_text is not None else 'Unknown Item' @@ -1853,33 +1860,38 @@ def write_strings(rom, world, player, team): credits = Credits() sickkiditem = world.get_location('Sick Kid', player).item - sickkiditem_text = random.choice(SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text + sickkiditem_text = world.random.choice( + SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text zoraitem = world.get_location('King Zora', player).item - zoraitem_text = random.choice(Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text + zoraitem_text = world.random.choice( + Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text magicshopitem = world.get_location('Potion Shop', player).item - magicshopitem_text = random.choice(MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text + magicshopitem_text = world.random.choice( + MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text fluteboyitem = world.get_location('Flute Spot', player).item - fluteboyitem_text = random.choice(FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text + fluteboyitem_text = world.random.choice( + FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text - credits.update_credits_line('castle', 0, random.choice(KingsReturn_texts)) - credits.update_credits_line('sanctuary', 0, random.choice(Sanctuary_texts)) + credits.update_credits_line('castle', 0, world.random.choice(KingsReturn_texts)) + credits.update_credits_line('sanctuary', 0, world.random.choice(Sanctuary_texts)) - credits.update_credits_line('kakariko', 0, random.choice(Kakariko_texts).format(random.choice(Sahasrahla_names))) - credits.update_credits_line('desert', 0, random.choice(DesertPalace_texts)) - credits.update_credits_line('hera', 0, random.choice(MountainTower_texts)) - credits.update_credits_line('house', 0, random.choice(LinksHouse_texts)) + credits.update_credits_line('kakariko', 0, + world.random.choice(Kakariko_texts).format(world.random.choice(Sahasrahla_names))) + credits.update_credits_line('desert', 0, world.random.choice(DesertPalace_texts)) + credits.update_credits_line('hera', 0, world.random.choice(MountainTower_texts)) + credits.update_credits_line('house', 0, world.random.choice(LinksHouse_texts)) credits.update_credits_line('zora', 0, zoraitem_text) credits.update_credits_line('witch', 0, magicshopitem_text) - credits.update_credits_line('lumberjacks', 0, random.choice(Lumberjacks_texts)) + credits.update_credits_line('lumberjacks', 0, world.random.choice(Lumberjacks_texts)) credits.update_credits_line('grove', 0, fluteboyitem_text) - credits.update_credits_line('well', 0, random.choice(WishingWell_texts)) - credits.update_credits_line('smithy', 0, random.choice(Blacksmiths_texts)) + credits.update_credits_line('well', 0, world.random.choice(WishingWell_texts)) + credits.update_credits_line('smithy', 0, world.random.choice(Blacksmiths_texts)) credits.update_credits_line('kakariko2', 0, sickkiditem_text) - credits.update_credits_line('bridge', 0, random.choice(DeathMountain_texts)) - credits.update_credits_line('woods', 0, random.choice(LostWoods_texts)) + credits.update_credits_line('bridge', 0, world.random.choice(DeathMountain_texts)) + credits.update_credits_line('woods', 0, world.random.choice(LostWoods_texts)) credits.update_credits_line('pedestal', 0, pedestal_credit_text) (pointers, data) = credits.get_bytes()