Core: Attribute per slot random directly to the World and discourage using MultiWorld's random (#1649)

* add a random object to the World

* use it in The Messenger

* the worlds don't exist until the end of set options

* set seed in lttp tests

* use world.random for shop shuffle
This commit is contained in:
Aaron Wagener 2023-07-02 05:50:14 -05:00 committed by GitHub
parent 6a88d5aa79
commit a6ba185c55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 17 additions and 5 deletions

View File

@ -81,6 +81,7 @@ class MultiWorld():
random: random.Random random: random.Random
per_slot_randoms: Dict[int, random.Random] per_slot_randoms: Dict[int, random.Random]
"""Deprecated. Please use `self.random` instead."""
class AttributeProxy(): class AttributeProxy():
def __init__(self, rule): def __init__(self, rule):
@ -242,6 +243,7 @@ class MultiWorld():
setattr(self, option_key, getattr(args, option_key, {})) setattr(self, option_key, getattr(args, option_key, {}))
self.worlds[player] = world_type(self, player) self.worlds[player] = world_type(self, player)
self.worlds[player].random = self.per_slot_randoms[player]
def set_item_links(self): def set_item_links(self):
item_links = {} item_links = {}

View File

@ -203,6 +203,9 @@ class World(metaclass=AutoWorldRegister):
location_names: ClassVar[Set[str]] location_names: ClassVar[Set[str]]
"""set of all potential location names""" """set of all potential location names"""
random: random.Random
"""This world's random object. Should be used for any randomization needed in world for this player slot."""
zip_path: ClassVar[Optional[pathlib.Path]] = None zip_path: ClassVar[Optional[pathlib.Path]] = None
"""If loaded from a .apworld, this is the Path to it.""" """If loaded from a .apworld, this is the Path to it."""
__file__: ClassVar[str] __file__: ClassVar[str]

View File

@ -14,6 +14,7 @@ from worlds import AutoWorld
class TestDungeon(unittest.TestCase): class TestDungeon(unittest.TestCase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -15,6 +15,7 @@ from worlds import AutoWorld
class TestInverted(TestBase): class TestInverted(TestBase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -15,6 +15,7 @@ class TestInvertedBombRules(unittest.TestCase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
self.multiworld.mode[1] = "inverted" self.multiworld.mode[1] = "inverted"
args = Namespace args = Namespace
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():

View File

@ -16,6 +16,7 @@ from worlds import AutoWorld
class TestInvertedMinor(TestBase): class TestInvertedMinor(TestBase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -17,6 +17,7 @@ from worlds import AutoWorld
class TestInvertedOWG(TestBase): class TestInvertedOWG(TestBase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -16,6 +16,7 @@ from worlds import AutoWorld
class TestMinor(TestBase): class TestMinor(TestBase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -17,6 +17,7 @@ from worlds import AutoWorld
class TestVanillaOWG(TestBase): class TestVanillaOWG(TestBase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -15,6 +15,7 @@ from worlds import AutoWorld
class TestVanilla(TestBase): class TestVanilla(TestBase):
def setUp(self): def setUp(self):
self.multiworld = MultiWorld(1) self.multiworld = MultiWorld(1)
self.multiworld.set_seed(None)
args = Namespace() args = Namespace()
for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items():
setattr(args, name, {1: option.from_any(option.default)}) setattr(args, name, {1: option.from_any(option.default)})

View File

@ -75,13 +75,12 @@ FIGURINES: Dict[str, ShopData] = {
def shuffle_shop_prices(world: MessengerWorld) -> Tuple[Dict[str, int], Dict[str, int]]: def shuffle_shop_prices(world: MessengerWorld) -> Tuple[Dict[str, int], Dict[str, int]]:
shop_price_mod = world.multiworld.shop_price[world.player].value shop_price_mod = world.multiworld.shop_price[world.player].value
shop_price_planned = world.multiworld.shop_price_plan[world.player] shop_price_planned = world.multiworld.shop_price_plan[world.player]
local_random: Random = world.multiworld.per_slot_randoms[world.player]
shop_prices: Dict[str, int] = {} shop_prices: Dict[str, int] = {}
figurine_prices: Dict[str, int] = {} figurine_prices: Dict[str, int] = {}
for item, price in shop_price_planned.value.items(): for item, price in shop_price_planned.value.items():
if not isinstance(price, int): if not isinstance(price, int):
price = local_random.choices(list(price.keys()), weights=list(price.values()))[0] price = world.random.choices(list(price.keys()), weights=list(price.values()))[0]
if "Figurine" in item: if "Figurine" in item:
figurine_prices[item] = price figurine_prices[item] = price
else: else:
@ -90,7 +89,7 @@ def shuffle_shop_prices(world: MessengerWorld) -> Tuple[Dict[str, int], Dict[str
remaining_slots = [item for item in [*SHOP_ITEMS, *FIGURINES] if item not in shop_price_planned.value] remaining_slots = [item for item in [*SHOP_ITEMS, *FIGURINES] if item not in shop_price_planned.value]
for shop_item in remaining_slots: for shop_item in remaining_slots:
shop_data = SHOP_ITEMS.get(shop_item, FIGURINES.get(shop_item)) shop_data = SHOP_ITEMS.get(shop_item, FIGURINES.get(shop_item))
price = local_random.randint(shop_data.min_price, shop_data.max_price) price = world.random.randint(shop_data.min_price, shop_data.max_price)
adjusted_price = min(int(price * shop_price_mod / 100), 5000) adjusted_price = min(int(price * shop_price_mod / 100), 5000)
if "Figurine" in shop_item: if "Figurine" in shop_item:
figurine_prices[shop_item] = adjusted_price figurine_prices[shop_item] = adjusted_price

View File

@ -102,7 +102,7 @@ class MessengerWorld(World):
# make a list of all notes except those in the player's defined starting inventory, and adjust the # make a list of all notes except those in the player's defined starting inventory, and adjust the
# amount we need to put in the itempool and precollect based on that # amount we need to put in the itempool and precollect based on that
notes = [note for note in NOTES if note not in self.multiworld.precollected_items[self.player]] notes = [note for note in NOTES if note not in self.multiworld.precollected_items[self.player]]
self.multiworld.per_slot_randoms[self.player].shuffle(notes) self.random.shuffle(notes)
precollected_notes_amount = NotesNeeded.range_end - \ precollected_notes_amount = NotesNeeded.range_end - \
self.multiworld.notes_needed[self.player] - \ self.multiworld.notes_needed[self.player] - \
(len(NOTES) - len(notes)) (len(NOTES) - len(notes))
@ -129,7 +129,7 @@ class MessengerWorld(World):
filler_pool = dict(list(FILLER.items())[2:]) if remaining_fill < 10 else FILLER filler_pool = dict(list(FILLER.items())[2:]) if remaining_fill < 10 else FILLER
itempool += [self.create_item(filler_item) itempool += [self.create_item(filler_item)
for filler_item in for filler_item in
self.multiworld.random.choices( self.random.choices(
list(filler_pool), list(filler_pool),
weights=list(filler_pool.values()), weights=list(filler_pool.values()),
k=remaining_fill k=remaining_fill