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