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:
Aaron Wagener 2024-03-10 12:47:45 -05:00 committed by GitHub
parent b8c24def8d
commit 2e1a5b0e3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 16 additions and 14 deletions

View File

@ -85,7 +85,7 @@ class MultiWorld():
game: Dict[int, str]
random: random.Random
per_slot_randoms: Dict[int, random.Random]
per_slot_randoms: Utils.DeprecateDict[int, random.Random]
"""Deprecated. Please use `self.random` instead."""
class AttributeProxy():
@ -217,7 +217,8 @@ class MultiWorld():
set_player_attr('game', "A Link to the Past")
set_player_attr('completion_condition', lambda state: True)
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
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"]}
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)
if secure:
self.secure()
else:
self.random.seed(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:
# TODO - remove this section once all worlds use options dataclasses
@ -275,7 +275,6 @@ class MultiWorld():
for player in self.player_ids:
world_type = AutoWorld.AutoWorldRegister.world_types[self.game[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
self.worlds[player].options = options_dataclass(**{option_key: getattr(args, option_key)[player]
for option_key in options_dataclass.type_hints})

View File

@ -1,5 +1,5 @@
from argparse import Namespace
from typing import Type, Tuple
from typing import Optional, Tuple, Type
from BaseClasses import MultiWorld, CollectionState
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")
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.
: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
steps through pre_fill
:param seed: The seed to be used when creating this multiworld
"""
multiworld = MultiWorld(1)
multiworld.game[1] = world_type.game
multiworld.player_name = {1: "Tester"}
multiworld.set_seed()
multiworld.set_seed(seed)
multiworld.state = CollectionState(multiworld)
args = Namespace()
for name, option in world_type.options_dataclass.type_hints.items():

View File

@ -13,6 +13,7 @@ from worlds.generic.Rules import CollectionRule, add_item_rule, locality_rules,
def generate_multiworld(players: int = 1) -> MultiWorld:
multiworld = MultiWorld(players)
multiworld.set_seed(0)
multiworld.player_name = {}
multiworld.state = CollectionState(multiworld)
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]
for option_key in world.options_dataclass.type_hints})
multiworld.set_seed(0)
return multiworld

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import hashlib
import logging
import pathlib
import random
import re
import sys
import time
@ -299,6 +300,8 @@ class World(metaclass=AutoWorldRegister):
assert multiworld is not None
self.multiworld = multiworld
self.player = player
self.random = random.Random(multiworld.random.getrandbits(64))
multiworld.per_slot_randoms[player] = self.random
def __getattr__(self, item: str) -> Any:
if item == "settings":

View File

@ -37,8 +37,7 @@ def setup_dlc_quest_solo_multiworld(test_options=None, seed=None, _cache: Dict[F
if frozen_options in _cache:
return _cache[frozen_options]
multiworld = setup_base_solo_multiworld(DLCqworld, ())
multiworld.set_seed(seed)
multiworld = setup_base_solo_multiworld(DLCqworld, (), seed=seed)
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
args = Namespace()
for name, option in DLCqworld.options_dataclass.type_hints.items():

View File

@ -124,8 +124,7 @@ def setup_solo_multiworld(test_options=None, seed=None,
if frozen_options in _cache:
return _cache[frozen_options]
multiworld = setup_base_solo_multiworld(StardewValleyWorld, ())
multiworld.set_seed(seed)
multiworld = setup_base_solo_multiworld(StardewValleyWorld, (), seed=seed)
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
args = Namespace()
for name, option in StardewValleyWorld.options_dataclass.type_hints.items():