Generate: convert plando settings to an IntFlag with error reporting for unknown plando names (#735)

This commit is contained in:
Fabian Dill 2022-07-03 14:11:52 +02:00 committed by GitHub
parent b9fb4de878
commit 3205cbf932
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 23 deletions

View File

@ -1,3 +1,5 @@
from __future__ import annotations
import argparse import argparse
import logging import logging
import random import random
@ -7,6 +9,7 @@ from typing import Set, Dict, Tuple, Callable, Any, Union
import os import os
from collections import Counter from collections import Counter
import string import string
import enum
import ModuleUpdate import ModuleUpdate
@ -25,7 +28,38 @@ from worlds.alttp.Text import TextTable
from worlds.AutoWorld import AutoWorldRegister from worlds.AutoWorld import AutoWorldRegister
import copy import copy
categories = set(AutoWorldRegister.world_types)
class PlandoSettings(enum.IntFlag):
items = 0b0001
connections = 0b0010
texts = 0b0100
bosses = 0b1000
@classmethod
def from_option_string(cls, option_string: str) -> PlandoSettings:
result = cls(0)
for part in option_string.split(","):
part = part.strip().lower()
if part:
result = cls._handle_part(part, result)
return result
@classmethod
def from_set(cls, option_set: Set[str]) -> PlandoSettings:
result = cls(0)
for part in option_set:
result = cls._handle_part(part, result)
return result
@classmethod
def _handle_part(cls, part: str, base: PlandoSettings) -> PlandoSettings:
try:
part = cls[part]
except Exception as e:
raise KeyError(f"{part} is not a recognized name for a plando module. "
f"Known options: {', '.join(flag.name for flag in cls)}") from e
else:
return base | part
def mystery_argparse(): def mystery_argparse():
@ -64,7 +98,7 @@ def mystery_argparse():
args.weights_file_path = os.path.join(args.player_files_path, args.weights_file_path) args.weights_file_path = os.path.join(args.player_files_path, args.weights_file_path)
if not os.path.isabs(args.meta_file_path): if not os.path.isabs(args.meta_file_path):
args.meta_file_path = os.path.join(args.player_files_path, args.meta_file_path) args.meta_file_path = os.path.join(args.player_files_path, args.meta_file_path)
args.plando: Set[str] = {arg.strip().lower() for arg in args.plando.split(",")} args.plando: PlandoSettings = PlandoSettings.from_option_string(args.plando)
return args, options return args, options
@ -127,7 +161,7 @@ def main(args=None, callback=ERmain):
args.multi = max(player_id-1, args.multi) args.multi = max(player_id-1, args.multi)
print(f"Generating for {args.multi} player{'s' if args.multi > 1 else ''}, {seed_name} Seed {seed} with plando: " print(f"Generating for {args.multi} player{'s' if args.multi > 1 else ''}, {seed_name} Seed {seed} with plando: "
f"{', '.join(args.plando)}") f"{args.plando}")
if not weights_cache: if not weights_cache:
raise Exception(f"No weights found. Provide a general weights file ({args.weights_file_path}) or individual player files. " raise Exception(f"No weights found. Provide a general weights file ({args.weights_file_path}) or individual player files. "
@ -403,7 +437,7 @@ def roll_triggers(weights: dict, triggers: list) -> dict:
def get_plando_bosses(boss_shuffle: str, plando_options: Set[str]) -> str: def get_plando_bosses(boss_shuffle: str, plando_options: Set[str]) -> str:
if boss_shuffle in boss_shuffle_options: if boss_shuffle in boss_shuffle_options:
return boss_shuffle_options[boss_shuffle] return boss_shuffle_options[boss_shuffle]
elif "bosses" in plando_options: elif PlandoSettings.bosses in plando_options:
options = boss_shuffle.lower().split(";") options = boss_shuffle.lower().split(";")
remainder_shuffle = "none" # vanilla remainder_shuffle = "none" # vanilla
bosses = [] bosses = []
@ -452,7 +486,7 @@ def handle_option(ret: argparse.Namespace, game_weights: dict, option_key: str,
setattr(ret, option_key, option(option.default)) setattr(ret, option_key, option(option.default))
def roll_settings(weights: dict, plando_options: Set[str] = frozenset(("bosses",))): def roll_settings(weights: dict, plando_options: PlandoSettings = PlandoSettings.bosses):
if "linked_options" in weights: if "linked_options" in weights:
weights = roll_linked_options(weights) weights = roll_linked_options(weights)
@ -465,17 +499,11 @@ def roll_settings(weights: dict, plando_options: Set[str] = frozenset(("bosses",
if tuplize_version(version) > version_tuple: if tuplize_version(version) > version_tuple:
raise Exception(f"Settings reports required version of generator is at least {version}, " raise Exception(f"Settings reports required version of generator is at least {version}, "
f"however generator is of version {__version__}") f"however generator is of version {__version__}")
required_plando_options = requirements.get("plando", "") required_plando_options = PlandoSettings.from_option_string(requirements.get("plando", ""))
if required_plando_options not in plando_options:
if required_plando_options: if required_plando_options:
required_plando_options = set(option.strip() for option in required_plando_options.split(",")) raise Exception(f"Settings reports required plando module {str(required_plando_options)}, "
required_plando_options -= plando_options
if required_plando_options:
if len(required_plando_options) == 1:
raise Exception(f"Settings reports required plando module {', '.join(required_plando_options)}, "
f"which is not enabled.") f"which is not enabled.")
else:
raise Exception(f"Settings reports required plando modules {', '.join(required_plando_options)}, "
f"which are not enabled.")
ret = argparse.Namespace() ret = argparse.Namespace()
for option_key in Options.per_game_common_options: for option_key in Options.per_game_common_options:
@ -504,12 +532,12 @@ def roll_settings(weights: dict, plando_options: Set[str] = frozenset(("bosses",
# skip setting this option if already set from common_options, defaulting to root option # skip setting this option if already set from common_options, defaulting to root option
if not (option_key in Options.common_options and option_key not in game_weights): if not (option_key in Options.common_options and option_key not in game_weights):
handle_option(ret, game_weights, option_key, option) handle_option(ret, game_weights, option_key, option)
if "items" in plando_options: if PlandoSettings.items in plando_options:
ret.plando_items = game_weights.get("plando_items", []) ret.plando_items = game_weights.get("plando_items", [])
if ret.game == "Minecraft" or ret.game == "Ocarina of Time": if ret.game == "Minecraft" or ret.game == "Ocarina of Time":
# bad hardcoded behavior to make this work for now # bad hardcoded behavior to make this work for now
ret.plando_connections = [] ret.plando_connections = []
if "connections" in plando_options: if PlandoSettings.connections in plando_options:
options = game_weights.get("plando_connections", []) options = game_weights.get("plando_connections", [])
for placement in options: for placement in options:
if roll_percentage(get_choice("percentage", placement, 100)): if roll_percentage(get_choice("percentage", placement, 100)):
@ -629,7 +657,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}") raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}")
ret.plando_texts = {} ret.plando_texts = {}
if "texts" in plando_options: if PlandoSettings.texts in plando_options:
tt = TextTable() tt = TextTable()
tt.removeUnwantedText() tt.removeUnwantedText()
options = weights.get("plando_texts", []) options = weights.get("plando_texts", [])
@ -641,7 +669,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
ret.plando_texts[at] = str(get_choice_legacy("text", placement)) ret.plando_texts[at] = str(get_choice_legacy("text", placement))
ret.plando_connections = [] ret.plando_connections = []
if "connections" in plando_options: if PlandoSettings.connections in plando_options:
options = weights.get("plando_connections", []) options = weights.get("plando_connections", [])
for placement in options: for placement in options:
if roll_percentage(get_choice_legacy("percentage", placement, 100)): if roll_percentage(get_choice_legacy("percentage", placement, 100)):

View File

@ -12,7 +12,7 @@ def allowed_file(filename):
return filename.endswith(('.txt', ".yaml", ".zip")) return filename.endswith(('.txt', ".yaml", ".zip"))
from Generate import roll_settings from Generate import roll_settings, PlandoSettings
from Utils import parse_yamls from Utils import parse_yamls
@ -65,7 +65,7 @@ def get_yaml_data(file) -> Union[Dict[str, str], str]:
def roll_options(options: Dict[str, Union[dict, str]], def roll_options(options: Dict[str, Union[dict, str]],
plando_options: Set[str] = frozenset({"bosses", "items", "connections", "texts"})) -> \ plando_options: Set[str] = frozenset({"bosses", "items", "connections", "texts"})) -> \
Tuple[Dict[str, Union[str, bool]], Dict[str, dict]]: Tuple[Dict[str, Union[str, bool]], Dict[str, dict]]:
plando_options = set(plando_options) plando_options = PlandoSettings.from_set(set(plando_options))
results = {} results = {}
rolled_results = {} rolled_results = {}
for filename, text in options.items(): for filename, text in options.items():

View File

@ -12,7 +12,7 @@ from flask import request, flash, redirect, url_for, session, render_template
from worlds.alttp.EntranceRandomizer import parse_arguments from worlds.alttp.EntranceRandomizer import parse_arguments
from Main import main as ERmain from Main import main as ERmain
from BaseClasses import seeddigits, get_seed from BaseClasses import seeddigits, get_seed
from Generate import handle_name from Generate import handle_name, PlandoSettings
import pickle import pickle
from .models import * from .models import *
@ -120,7 +120,7 @@ def gen_game(gen_options, meta: TypeOptional[Dict[str, object]] = None, owner=No
erargs.outputname = seedname erargs.outputname = seedname
erargs.outputpath = target.name erargs.outputpath = target.name
erargs.teams = 1 erargs.teams = 1
erargs.plando_options = ", ".join(plando_options) erargs.plando_options = PlandoSettings.from_set(plando_options)
name_counter = Counter() name_counter = Counter()
for player, (playerfile, settings) in enumerate(gen_options.items(), 1): for player, (playerfile, settings) in enumerate(gen_options.items(), 1):