From adda0eff4a2a061379f9d387411953217b5ce4b9 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 8 Jun 2021 14:15:23 +0200 Subject: [PATCH] implement Range option type --- Main.py | 8 ++---- Mystery.py | 16 ++++------- Options.py | 45 ++++++++++++++++++++++-------- worlds/alttp/EntranceRandomizer.py | 3 +- 4 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Main.py b/Main.py index 41cdd7b3..0eeee312 100644 --- a/Main.py +++ b/Main.py @@ -94,12 +94,8 @@ def main(args, seed=None): world.compassshuffle = args.compassshuffle.copy() world.keyshuffle = args.keyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy() - 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.crystals_needed_for_ganon = args.crystals_ganon.copy() + world.crystals_needed_for_gt = args.crystals_gt.copy() world.open_pyramid = args.open_pyramid.copy() world.boss_shuffle = args.shufflebosses.copy() world.enemy_shuffle = args.enemy_shuffle.copy() diff --git a/Mystery.py b/Mystery.py index a80872b8..a4fd82f2 100644 --- a/Mystery.py +++ b/Mystery.py @@ -632,14 +632,12 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): # fast ganon + ganon at hole ret.open_pyramid = get_choice('open_pyramid', weights, 'goal') - ret.crystals_gt = prefer_int(get_choice('tower_open', weights)) - - ret.crystals_ganon = prefer_int(get_choice('ganon_open', weights)) + ret.crystals_gt = Options.Crystals.from_any(get_choice('tower_open', weights)).value + ret.crystals_ganon = Options.Crystals.from_any(get_choice('ganon_open', weights)).value extra_pieces = get_choice('triforce_pieces_mode', weights, 'available') - ret.triforce_pieces_required = int(get_choice('triforce_pieces_required', weights, 20)) - ret.triforce_pieces_required = min(max(1, int(ret.triforce_pieces_required)), 90) + ret.triforce_pieces_required = Options.TriforcePieces.from_any(get_choice('triforce_pieces_required', weights)).value # sum a percentage to required if extra_pieces == 'percentage': @@ -647,7 +645,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): ret.triforce_pieces_available = int(round(ret.triforce_pieces_required * percentage, 0)) # vanilla mode (specify how many pieces are) elif extra_pieces == 'available': - ret.triforce_pieces_available = int(get_choice('triforce_pieces_available', weights, 30)) + ret.triforce_pieces_available = Options.TriforcePieces.from_any(get_choice('triforce_pieces_available', weights)).value # required pieces + fixed extra elif extra_pieces == 'extra': extra_pieces = max(0, int(get_choice('triforce_pieces_extra', weights, 10))) @@ -655,11 +653,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): # change minimum to required pieces to avoid problems ret.triforce_pieces_available = min(max(ret.triforce_pieces_required, int(ret.triforce_pieces_available)), 90) - shuffle_slots = get_choice('shop_shuffle_slots', weights, '0') - if str(shuffle_slots).lower() == "random": - ret.shop_shuffle_slots = random.randint(0, 30) - else: - ret.shop_shuffle_slots = int(shuffle_slots) + ret.shop_shuffle_slots = Options.TriforcePieces.from_any(get_choice('shop_shuffle_slots', weights)).value ret.shop_shuffle = get_choice('shop_shuffle', weights, '') if not ret.shop_shuffle: diff --git a/Options.py b/Options.py index 3795b817..2a63b969 100644 --- a/Options.py +++ b/Options.py @@ -1,5 +1,6 @@ from __future__ import annotations import typing +import random class AssembleOptions(type): @@ -108,6 +109,29 @@ class Choice(Option): return cls(data) return cls.from_text(str(data)) +class Range(Option): + range_start = 0 + range_end = 1 + def __init__(self, value: typing.Union[str, int]): + self.value: typing.Union[str, int] = value + + @classmethod + def from_text(cls, text: str) -> Range: + if text.lower() == "random": + return cls(random.randint(cls.range_start, cls.range_end)) + number = int(text) + if number < cls.range_start: + raise Exception(f"{number} is lower than minimum {cls.range_start} for option {cls.__name__}") + elif number > cls.range_end: + raise Exception(f"{number} is higher than maximum {cls.range_end} for option {cls.__name__}") + else: + return cls(number) + + @classmethod + def from_any(cls, data: typing.Any) -> Range: + if type(data) == int: + return cls(data) + return cls.from_text(str(data)) class OptionNameSet(Option): default = frozenset() @@ -173,18 +197,17 @@ class Accessibility(Choice): option_beatable = 2 -class Crystals(Choice): - # can't use IntEnum since there's also random - option_0 = 0 - option_1 = 1 - option_2 = 2 - option_3 = 3 - option_4 = 4 - option_5 = 5 - option_6 = 6 - option_7 = 7 - option_random = -1 +class Crystals(Range): + range_start = 0 + range_end = 7 +class TriforcePieces(Range): + range_start = 1 + range_end = 90 + +class ShopShuffleSlots(Range): + range_start = 0 + range_end = 30 class WorldState(Choice): option_standard = 1 diff --git a/worlds/alttp/EntranceRandomizer.py b/worlds/alttp/EntranceRandomizer.py index 0a62a2c3..2673a41a 100644 --- a/worlds/alttp/EntranceRandomizer.py +++ b/worlds/alttp/EntranceRandomizer.py @@ -206,11 +206,10 @@ def parse_arguments(argv, no_defaults=False): 0-7: Number of crystals needed ''') parser.add_argument('--crystals_gt', default=defval('7'), const='7', nargs='?', - choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'], + choices=['0', '1', '2', '3', '4', '5', '6', '7'], help='''\ How many crystals are needed to open GT. For inverted mode this applies to the castle tower door instead. (default: %(default)s) - Random: Picks a random value between 0 and 7 (inclusive). 0-7: Number of crystals needed ''') parser.add_argument('--open_pyramid', default=defval('auto'), help='''\