Core: create the per world random object in the world constructor (#2083)
* Core: create the per world random object in the world constructor * remove the check that multiworld exists * add a deprecation warning to per_slot_randoms * move random import and fix conflicts * assert worlds don't exist before setting the multiworld seed * fix the dlcq and sdv tests * actually use the seed
This commit is contained in:
parent
b8c24def8d
commit
2e1a5b0e3b
|
@ -85,7 +85,7 @@ class MultiWorld():
|
||||||
game: Dict[int, str]
|
game: Dict[int, str]
|
||||||
|
|
||||||
random: random.Random
|
random: random.Random
|
||||||
per_slot_randoms: Dict[int, random.Random]
|
per_slot_randoms: Utils.DeprecateDict[int, random.Random]
|
||||||
"""Deprecated. Please use `self.random` instead."""
|
"""Deprecated. Please use `self.random` instead."""
|
||||||
|
|
||||||
class AttributeProxy():
|
class AttributeProxy():
|
||||||
|
@ -217,7 +217,8 @@ class MultiWorld():
|
||||||
set_player_attr('game', "A Link to the Past")
|
set_player_attr('game', "A Link to the Past")
|
||||||
set_player_attr('completion_condition', lambda state: True)
|
set_player_attr('completion_condition', lambda state: True)
|
||||||
self.worlds = {}
|
self.worlds = {}
|
||||||
self.per_slot_randoms = {}
|
self.per_slot_randoms = Utils.DeprecateDict("Using per_slot_randoms is now deprecated. Please use the "
|
||||||
|
"world's random object instead (usually self.random)")
|
||||||
self.plando_options = PlandoOptions.none
|
self.plando_options = PlandoOptions.none
|
||||||
|
|
||||||
def get_all_ids(self) -> Tuple[int, ...]:
|
def get_all_ids(self) -> Tuple[int, ...]:
|
||||||
|
@ -251,14 +252,13 @@ class MultiWorld():
|
||||||
return {group_id for group_id, group in self.groups.items() if player in group["players"]}
|
return {group_id for group_id, group in self.groups.items() if player in group["players"]}
|
||||||
|
|
||||||
def set_seed(self, seed: Optional[int] = None, secure: bool = False, name: Optional[str] = None):
|
def set_seed(self, seed: Optional[int] = None, secure: bool = False, name: Optional[str] = None):
|
||||||
|
assert not self.worlds, "seed needs to be initialized before Worlds"
|
||||||
self.seed = get_seed(seed)
|
self.seed = get_seed(seed)
|
||||||
if secure:
|
if secure:
|
||||||
self.secure()
|
self.secure()
|
||||||
else:
|
else:
|
||||||
self.random.seed(self.seed)
|
self.random.seed(self.seed)
|
||||||
self.seed_name = name if name else str(self.seed)
|
self.seed_name = name if name else str(self.seed)
|
||||||
self.per_slot_randoms = {player: random.Random(self.random.getrandbits(64)) for player in
|
|
||||||
range(1, self.players + 1)}
|
|
||||||
|
|
||||||
def set_options(self, args: Namespace) -> None:
|
def set_options(self, args: Namespace) -> None:
|
||||||
# TODO - remove this section once all worlds use options dataclasses
|
# TODO - remove this section once all worlds use options dataclasses
|
||||||
|
@ -275,7 +275,6 @@ class MultiWorld():
|
||||||
for player in self.player_ids:
|
for player in self.player_ids:
|
||||||
world_type = AutoWorld.AutoWorldRegister.world_types[self.game[player]]
|
world_type = AutoWorld.AutoWorldRegister.world_types[self.game[player]]
|
||||||
self.worlds[player] = world_type(self, player)
|
self.worlds[player] = world_type(self, player)
|
||||||
self.worlds[player].random = self.per_slot_randoms[player]
|
|
||||||
options_dataclass: typing.Type[Options.PerGameCommonOptions] = world_type.options_dataclass
|
options_dataclass: typing.Type[Options.PerGameCommonOptions] = world_type.options_dataclass
|
||||||
self.worlds[player].options = options_dataclass(**{option_key: getattr(args, option_key)[player]
|
self.worlds[player].options = options_dataclass(**{option_key: getattr(args, option_key)[player]
|
||||||
for option_key in options_dataclass.type_hints})
|
for option_key in options_dataclass.type_hints})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from typing import Type, Tuple
|
from typing import Optional, Tuple, Type
|
||||||
|
|
||||||
from BaseClasses import MultiWorld, CollectionState
|
from BaseClasses import MultiWorld, CollectionState
|
||||||
from worlds.AutoWorld import call_all, World
|
from worlds.AutoWorld import call_all, World
|
||||||
|
@ -7,18 +7,21 @@ from worlds.AutoWorld import call_all, World
|
||||||
gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill")
|
gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill")
|
||||||
|
|
||||||
|
|
||||||
def setup_solo_multiworld(world_type: Type[World], steps: Tuple[str, ...] = gen_steps) -> MultiWorld:
|
def setup_solo_multiworld(
|
||||||
|
world_type: Type[World], steps: Tuple[str, ...] = gen_steps, seed: Optional[int] = None
|
||||||
|
) -> MultiWorld:
|
||||||
"""
|
"""
|
||||||
Creates a multiworld with a single player of `world_type`, sets default options, and calls provided gen steps.
|
Creates a multiworld with a single player of `world_type`, sets default options, and calls provided gen steps.
|
||||||
|
|
||||||
:param world_type: Type of the world to generate a multiworld for
|
:param world_type: Type of the world to generate a multiworld for
|
||||||
:param steps: The gen steps that should be called on the generated multiworld before returning. Default calls
|
:param steps: The gen steps that should be called on the generated multiworld before returning. Default calls
|
||||||
steps through pre_fill
|
steps through pre_fill
|
||||||
|
:param seed: The seed to be used when creating this multiworld
|
||||||
"""
|
"""
|
||||||
multiworld = MultiWorld(1)
|
multiworld = MultiWorld(1)
|
||||||
multiworld.game[1] = world_type.game
|
multiworld.game[1] = world_type.game
|
||||||
multiworld.player_name = {1: "Tester"}
|
multiworld.player_name = {1: "Tester"}
|
||||||
multiworld.set_seed()
|
multiworld.set_seed(seed)
|
||||||
multiworld.state = CollectionState(multiworld)
|
multiworld.state = CollectionState(multiworld)
|
||||||
args = Namespace()
|
args = Namespace()
|
||||||
for name, option in world_type.options_dataclass.type_hints.items():
|
for name, option in world_type.options_dataclass.type_hints.items():
|
||||||
|
|
|
@ -13,6 +13,7 @@ from worlds.generic.Rules import CollectionRule, add_item_rule, locality_rules,
|
||||||
|
|
||||||
def generate_multiworld(players: int = 1) -> MultiWorld:
|
def generate_multiworld(players: int = 1) -> MultiWorld:
|
||||||
multiworld = MultiWorld(players)
|
multiworld = MultiWorld(players)
|
||||||
|
multiworld.set_seed(0)
|
||||||
multiworld.player_name = {}
|
multiworld.player_name = {}
|
||||||
multiworld.state = CollectionState(multiworld)
|
multiworld.state = CollectionState(multiworld)
|
||||||
for i in range(players):
|
for i in range(players):
|
||||||
|
@ -32,8 +33,6 @@ def generate_multiworld(players: int = 1) -> MultiWorld:
|
||||||
world.options = world.options_dataclass(**{option_key: getattr(multiworld, option_key)[player_id]
|
world.options = world.options_dataclass(**{option_key: getattr(multiworld, option_key)[player_id]
|
||||||
for option_key in world.options_dataclass.type_hints})
|
for option_key in world.options_dataclass.type_hints})
|
||||||
|
|
||||||
multiworld.set_seed(0)
|
|
||||||
|
|
||||||
return multiworld
|
return multiworld
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
@ -299,6 +300,8 @@ class World(metaclass=AutoWorldRegister):
|
||||||
assert multiworld is not None
|
assert multiworld is not None
|
||||||
self.multiworld = multiworld
|
self.multiworld = multiworld
|
||||||
self.player = player
|
self.player = player
|
||||||
|
self.random = random.Random(multiworld.random.getrandbits(64))
|
||||||
|
multiworld.per_slot_randoms[player] = self.random
|
||||||
|
|
||||||
def __getattr__(self, item: str) -> Any:
|
def __getattr__(self, item: str) -> Any:
|
||||||
if item == "settings":
|
if item == "settings":
|
||||||
|
|
|
@ -37,8 +37,7 @@ def setup_dlc_quest_solo_multiworld(test_options=None, seed=None, _cache: Dict[F
|
||||||
if frozen_options in _cache:
|
if frozen_options in _cache:
|
||||||
return _cache[frozen_options]
|
return _cache[frozen_options]
|
||||||
|
|
||||||
multiworld = setup_base_solo_multiworld(DLCqworld, ())
|
multiworld = setup_base_solo_multiworld(DLCqworld, (), seed=seed)
|
||||||
multiworld.set_seed(seed)
|
|
||||||
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
|
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
|
||||||
args = Namespace()
|
args = Namespace()
|
||||||
for name, option in DLCqworld.options_dataclass.type_hints.items():
|
for name, option in DLCqworld.options_dataclass.type_hints.items():
|
||||||
|
|
|
@ -124,8 +124,7 @@ def setup_solo_multiworld(test_options=None, seed=None,
|
||||||
if frozen_options in _cache:
|
if frozen_options in _cache:
|
||||||
return _cache[frozen_options]
|
return _cache[frozen_options]
|
||||||
|
|
||||||
multiworld = setup_base_solo_multiworld(StardewValleyWorld, ())
|
multiworld = setup_base_solo_multiworld(StardewValleyWorld, (), seed=seed)
|
||||||
multiworld.set_seed(seed)
|
|
||||||
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
|
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
|
||||||
args = Namespace()
|
args = Namespace()
|
||||||
for name, option in StardewValleyWorld.options_dataclass.type_hints.items():
|
for name, option in StardewValleyWorld.options_dataclass.type_hints.items():
|
||||||
|
|
Loading…
Reference in New Issue