def gen_game(gen_options, race=False, owner=None, sid=None):
    try:
        target = tempfile.TemporaryDirectory()
        playercount = len(gen_options)
        seed = get_seed()
        random.seed(seed)

        if race:
            random.seed()  # reset to time-based random source

        seedname = "M" + (
            f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))

        erargs = parse_arguments(['--multi', str(playercount)])
        erargs.seed = seed
        erargs.name = {x: ""
                       for x in range(1, playercount + 1)
                       }  # only so it can be overwrittin in mystery
        erargs.create_spoiler = not race
        erargs.race = race
        erargs.skip_playthrough = race
        erargs.outputname = seedname
        erargs.outputpath = target.name
        erargs.teams = 1
        erargs.progression_balancing = {}
        erargs.create_diff = True

        name_counter = Counter()
        for player, (playerfile,
                     settings) in enumerate(gen_options.items(), 1):
            for k, v in settings.items():
                if v is not None:
                    getattr(erargs, k)[player] = v

            if not erargs.name[player]:
                erargs.name[player] = os.path.splitext(
                    os.path.split(playerfile)[-1])[0]
            erargs.name[player] = handle_name(erargs.name[player], player,
                                              name_counter)

        erargs.names = ",".join(erargs.name[i]
                                for i in range(1, playercount + 1))
        del (erargs.name)

        erargs.skip_progression_balancing = {
            player: not balanced
            for player, balanced in erargs.progression_balancing.items()
        }
        del (erargs.progression_balancing)
        ERmain(erargs, seed)

        return upload_to_db(target.name, owner, sid, race)
    except BaseException as e:
        if sid:
            with db_session:
                gen = Generation.get(id=sid)
                if gen is not None:
                    gen.state = STATE_ERROR
                    gen.meta = (e.__class__.__name__ + ": " + str(e)).encode()
        raise
Esempio n. 2
0
def gen(gen_options, race=False):
    target = tempfile.TemporaryDirectory()
    with target:
        playercount = len(gen_options)
        seed = get_seed()
        random.seed(seed)

        if race:
            random.seed()  # reset to time-based random source

        seedname = "M" + (
            f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))

        erargs = parse_arguments(['--multi', str(playercount)])
        erargs.seed = seed
        erargs.name = {x: ""
                       for x in range(1, playercount + 1)
                       }  # only so it can be overwrittin in mystery
        erargs.create_spoiler = not race
        erargs.race = race
        erargs.skip_playthrough = race
        erargs.outputname = seedname
        erargs.outputpath = target.name
        erargs.teams = 1
        erargs.progression_balancing = {}
        erargs.create_diff = True

        for player, (playerfile,
                     settings) in enumerate(gen_options.items(), 1):
            for k, v in vars(settings).items():
                if v is not None:
                    getattr(erargs, k)[player] = v

            if not erargs.name[player]:
                erargs.name[player] = os.path.split(playerfile)[-1].split(
                    ".")[0]

        erargs.names = ",".join(erargs.name[i]
                                for i in range(1, playercount + 1))
        del (erargs.name)

        erargs.skip_progression_balancing = {
            player: not balanced
            for player, balanced in erargs.progression_balancing.items()
        }
        del (erargs.progression_balancing)
        ERmain(erargs, seed)
        return upload_to_db(target.name)
Esempio n. 3
0
def main(args=None, callback=ERmain):
    if not args:
        args = mystery_argparse()

    seed = get_seed(args.seed)
    random.seed(seed)

    seedname = "M" + (
        f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))
    print(
        f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}"
    )

    if args.race:
        random.seed()  # reset to time-based random source

    weights_cache = {}
    if args.weights:
        try:
            weights_cache[args.weights] = get_weights(args.weights)
        except Exception as e:
            raise ValueError(
                f"File {args.weights} is destroyed. Please fix your yaml."
            ) from e
        print(
            f"Weights: {args.weights} >> "
            f"{get_choice('description', weights_cache[args.weights], 'No description specified')}"
        )
    if args.meta:
        try:
            weights_cache[args.meta] = get_weights(args.meta)
        except Exception as e:
            raise ValueError(
                f"File {args.meta} is destroyed. Please fix your yaml.") from e
        meta_weights = weights_cache[args.meta]
        print(
            f"Meta: {args.meta} >> {get_choice('meta_description', meta_weights, 'No description specified')}"
        )
        if args.samesettings:
            raise Exception("Cannot mix --samesettings with --meta")

    for player in range(1, args.multi + 1):
        path = getattr(args, f'p{player}')
        if path:
            try:
                if path not in weights_cache:
                    weights_cache[path] = get_weights(path)
                print(
                    f"P{player} Weights: {path} >> "
                    f"{get_choice('description', weights_cache[path], 'No description specified')}"
                )

            except Exception as e:
                raise ValueError(
                    f"File {path} is destroyed. Please fix your yaml.") from e
    erargs = parse_arguments(['--multi', str(args.multi)])
    erargs.seed = seed
    erargs.name = {x: ""
                   for x in range(1, args.multi + 1)
                   }  # only so it can be overwrittin in mystery
    erargs.create_spoiler = args.create_spoiler
    erargs.create_diff = args.create_diff
    erargs.glitch_triforce = args.glitch_triforce
    erargs.race = args.race
    erargs.skip_playthrough = args.skip_playthrough
    erargs.outputname = seedname
    erargs.outputpath = args.outputpath
    erargs.teams = args.teams
    erargs.progression_balancing = {}

    # set up logger
    if args.loglevel:
        erargs.loglevel = args.loglevel
    loglevel = {
        'error': logging.ERROR,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'debug': logging.DEBUG
    }[erargs.loglevel]

    if args.log_output_path:
        import sys

        class LoggerWriter(object):
            def __init__(self, writer):
                self._writer = writer
                self._msg = ''

            def write(self, message):
                self._msg = self._msg + message
                while '\n' in self._msg:
                    pos = self._msg.find('\n')
                    self._writer(self._msg[:pos])
                    self._msg = self._msg[pos + 1:]

            def flush(self):
                if self._msg != '':
                    self._writer(self._msg)
                    self._msg = ''

        log = logging.getLogger("stderr")
        log.addHandler(logging.StreamHandler())
        sys.stderr = LoggerWriter(log.error)
        os.makedirs(args.log_output_path, exist_ok=True)
        logging.basicConfig(format='%(message)s',
                            level=loglevel,
                            filename=os.path.join(args.log_output_path,
                                                  f"{seed}.log"))
    else:
        logging.basicConfig(format='%(message)s', level=loglevel)
    if args.rom:
        erargs.rom = args.rom

    if args.enemizercli:
        erargs.enemizercli = args.enemizercli

    settings_cache = {
        k: (roll_settings(v, args.plando) if args.samesettings else None)
        for k, v in weights_cache.items()
    }
    player_path_cache = {}
    for player in range(1, args.multi + 1):
        player_path_cache[player] = getattr(args, f'p{player}') if getattr(
            args, f'p{player}') else args.weights

    if args.meta:
        for player, path in player_path_cache.items():
            weights_cache[path].setdefault("meta_ignore", [])
        meta_weights = weights_cache[args.meta]
        for key in meta_weights:
            option = get_choice(key, meta_weights)
            if option is not None:
                for player, path in player_path_cache.items():
                    players_meta = weights_cache[path].get("meta_ignore", [])
                    if key not in players_meta:
                        weights_cache[path][key] = option
                    elif type(players_meta) == dict and players_meta[
                            key] and option not in players_meta[key]:
                        weights_cache[path][key] = option

    name_counter = Counter()
    for player in range(1, args.multi + 1):
        path = player_path_cache[player]
        if path:
            try:
                settings = settings_cache[path] if settings_cache[path] else \
                    roll_settings(weights_cache[path], args.plando)
                if settings.sprite and not os.path.isfile(
                        settings.sprite) and not Sprite.get_sprite_from_name(
                            settings.sprite):
                    logging.warning(
                        f"Warning: The chosen sprite, \"{settings.sprite}\", for yaml \"{path}\", does not exist."
                    )
                if args.pre_roll:
                    import yaml
                    if path == args.weights:
                        settings.name = f"Player{player}"
                    elif not settings.name:
                        settings.name = os.path.splitext(
                            os.path.split(path)[-1])[0]

                    if "-" not in settings.shuffle and settings.shuffle != "vanilla":
                        settings.shuffle += f"-{random.randint(0, 2 ** 64)}"

                    pre_rolled = dict()
                    pre_rolled["original_seed_number"] = seed
                    pre_rolled["original_seed_name"] = seedname
                    pre_rolled["pre_rolled"] = vars(settings).copy()
                    if "plando_items" in pre_rolled["pre_rolled"]:
                        pre_rolled["pre_rolled"]["plando_items"] = [
                            item.to_dict() for item in pre_rolled["pre_rolled"]
                            ["plando_items"]
                        ]
                    if "plando_connections" in pre_rolled["pre_rolled"]:
                        pre_rolled["pre_rolled"]["plando_connections"] = [
                            connection.to_dict() for connection in
                            pre_rolled["pre_rolled"]["plando_connections"]
                        ]

                    with open(
                            os.path.join(
                                args.outputpath if args.outputpath else ".",
                                f"{os.path.split(path)[-1].split('.')[0]}_pre_rolled.yaml"
                            ), "wt") as f:
                        yaml.dump(pre_rolled, f)
                for k, v in vars(settings).items():
                    if v is not None:
                        getattr(erargs, k)[player] = v
            except Exception as e:
                raise ValueError(
                    f"File {path} is destroyed. Please fix your yaml.") from e
        else:
            raise RuntimeError(f'No weights specified for player {player}')
        if path == args.weights:  # if name came from the weights file, just use base player name
            erargs.name[player] = f"Player{player}"
        elif not erargs.name[
                player]:  # if name was not specified, generate it from filename
            erargs.name[player] = os.path.splitext(os.path.split(path)[-1])[0]
        erargs.name[player] = handle_name(erargs.name[player], player,
                                          name_counter)
        logging.info(erargs.name[player])
    erargs.names = ",".join(erargs.name[i] for i in range(1, args.multi + 1))
    del (erargs.name)
    if args.yaml_output:
        import yaml
        important = {}
        for option, player_settings in vars(erargs).items():
            if type(player_settings) == dict:
                if all(
                        type(value) != list
                        for value in player_settings.values()):
                    if len(player_settings.values()) > 1:
                        important[option] = {
                            player: value
                            for player, value in player_settings.items()
                            if player <= args.yaml_output
                        }
                    elif len(player_settings.values()) > 0:
                        important[option] = player_settings[1]
                    else:
                        logging.debug(
                            f"No player settings defined for option '{option}'"
                        )

            else:
                if player_settings != "":  # is not empty name
                    important[option] = player_settings
                else:
                    logging.debug(
                        f"No player settings defined for option '{option}'")
        if args.outputpath:
            os.makedirs(args.outputpath, exist_ok=True)
        with open(
                os.path.join(args.outputpath if args.outputpath else ".",
                             f"mystery_result_{seed}.yaml"), "wt") as f:
            yaml.dump(important, f)

    erargs.skip_progression_balancing = {
        player: not balanced
        for player, balanced in erargs.progression_balancing.items()
    }
    del (erargs.progression_balancing)
    callback(erargs, seed)
Esempio n. 4
0
def main():
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('--multi',
                        default=1,
                        type=lambda value: min(max(int(value), 1), 255))
    multiargs, _ = parser.parse_known_args()

    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--weights',
        help=
        'Path to the weights file to use for rolling game settings, urls are also valid'
    )
    parser.add_argument(
        '--samesettings',
        help='Rolls settings per weights file rather than per player',
        action='store_true')
    parser.add_argument('--seed',
                        help='Define seed number to generate.',
                        type=int)
    parser.add_argument('--multi',
                        default=1,
                        type=lambda value: min(max(int(value), 1), 255))
    parser.add_argument('--teams',
                        default=1,
                        type=lambda value: max(int(value), 1))
    parser.add_argument('--create_spoiler', action='store_true')
    parser.add_argument('--skip_playthrough', action='store_true')
    parser.add_argument('--rom')
    parser.add_argument('--enemizercli')
    parser.add_argument('--outputpath')
    parser.add_argument('--race', action='store_true')
    parser.add_argument('--meta', default=None)
    parser.add_argument('--log_output_path', help='Path to store output log')
    parser.add_argument('--loglevel', default='info', help='Sets log level')
    parser.add_argument(
        '--yaml_output',
        default=0,
        type=lambda value: min(max(int(value), 0), 255),
        help=
        'Output rolled mystery results to yaml up to specified number (made for async multiworld)'
    )

    for player in range(1, multiargs.multi + 1):
        parser.add_argument(f'--p{player}', help=argparse.SUPPRESS)
    args = parser.parse_args()

    if args.seed is None:
        random.seed(None)
        seed = random.randint(0, 999999999)
    else:
        seed = args.seed
    random.seed(seed)

    seedname = "M" + (f"{random.randint(0, 999999999)}".zfill(9))
    print(
        f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}"
    )

    weights_cache = {}
    if args.weights:
        try:
            weights_cache[args.weights] = get_weights(args.weights)
        except Exception as e:
            raise ValueError(
                f"File {args.weights} is destroyed. Please fix your yaml."
            ) from e
        print(
            f"Weights: {args.weights} >> {weights_cache[args.weights]['description']}"
        )
    if args.meta:
        try:
            weights_cache[args.meta] = get_weights(args.meta)
        except Exception as e:
            raise ValueError(
                f"File {args.meta} is destroyed. Please fix your yaml.") from e
        meta_weights = weights_cache[args.meta]
        print(f"Meta: {args.meta} >> {meta_weights['meta_description']}")
        if args.samesettings:
            raise Exception("Cannot mix --samesettings with --meta")

    for player in range(1, args.multi + 1):
        path = getattr(args, f'p{player}')
        if path:
            try:
                if path not in weights_cache:
                    weights_cache[path] = get_weights(path)
                print(
                    f"P{player} Weights: {path} >> {weights_cache[path]['description']}"
                )

            except Exception as e:
                raise ValueError(
                    f"File {path} is destroyed. Please fix your yaml.") from e
    erargs = parse_arguments(['--multi', str(args.multi)])
    erargs.seed = seed
    erargs.name = {x: ""
                   for x in range(1, args.multi + 1)
                   }  # only so it can be overwrittin in mystery
    erargs.create_spoiler = args.create_spoiler
    erargs.race = args.race
    erargs.skip_playthrough = args.skip_playthrough
    erargs.outputname = seedname
    erargs.outputpath = args.outputpath
    erargs.teams = args.teams
    erargs.progression_balancing = {}

    # set up logger
    if args.loglevel:
        erargs.loglevel = args.loglevel
    loglevel = {
        'error': logging.ERROR,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'debug': logging.DEBUG
    }[erargs.loglevel]

    import sys

    class LoggerWriter(object):
        def __init__(self, writer):
            self._writer = writer
            self._msg = ''

        def write(self, message):
            self._msg = self._msg + message
            while '\n' in self._msg:
                pos = self._msg.find('\n')
                self._writer(self._msg[:pos])
                self._msg = self._msg[pos + 1:]

        def flush(self):
            if self._msg != '':
                self._writer(self._msg)
                self._msg = ''

    if args.log_output_path:
        log = logging.getLogger("stderr")
        log.addHandler(logging.StreamHandler())
        sys.stderr = LoggerWriter(log.error)
        os.makedirs(args.log_output_path, exist_ok=True)
        logging.basicConfig(format='%(message)s',
                            level=loglevel,
                            filename=os.path.join(args.log_output_path,
                                                  f"{seed}.log"))
    else:
        logging.basicConfig(format='%(message)s', level=loglevel)
    if args.rom:
        erargs.rom = args.rom

    if args.enemizercli:
        erargs.enemizercli = args.enemizercli

    settings_cache = {
        k: (roll_settings(v) if args.samesettings else None)
        for k, v in weights_cache.items()
    }
    player_path_cache = {}
    for player in range(1, args.multi + 1):
        player_path_cache[player] = getattr(args, f'p{player}') if getattr(
            args, f'p{player}') else args.weights

    if args.meta:
        for player, path in player_path_cache.items():
            weights_cache[path].setdefault("meta_ignore", [])
        meta_weights = weights_cache[args.meta]
        for key in meta_weights:
            option = get_choice(key, meta_weights)
            if option is not None:
                for player, path in player_path_cache.items():
                    players_meta = weights_cache[path].get("meta_ignore", [])
                    if key not in players_meta:
                        weights_cache[path][key] = option
                    elif type(players_meta) == dict and players_meta[
                            key] and option not in players_meta[key]:
                        weights_cache[path][key] = option

    for player in range(1, args.multi + 1):
        path = player_path_cache[player]
        if path:
            try:
                settings = settings_cache[path] if settings_cache[
                    path] else roll_settings(weights_cache[path])
                if settings.sprite is not None and not os.path.isfile(
                        settings.sprite) and not get_sprite_from_name(
                            settings.sprite):
                    logging.warning(
                        f"Warning: The chosen sprite, \"{settings.sprite}\", for yaml \"{path}\", does not exist."
                    )
                for k, v in vars(settings).items():
                    if v is not None:
                        getattr(erargs, k)[player] = v
            except Exception as e:
                raise ValueError(
                    f"File {path} is destroyed. Please fix your yaml.") from e
        else:
            raise RuntimeError(f'No weights specified for player {player}')
        if not erargs.name[player]:
            erargs.name[player] = os.path.split(path)[-1].split(".")[0]
    if args.weights:
        erargs.names = ""
    else:
        erargs.names = ",".join(erargs.name[i]
                                for i in range(1, args.multi + 1))
    del (erargs.name)
    if args.yaml_output:
        import yaml
        important = {}
        for option, player_settings in vars(erargs).items():
            if type(player_settings) == dict:
                if len(set(player_settings.values())) > 1:
                    important[option] = {
                        player: value
                        for player, value in player_settings.items()
                        if player <= args.yaml_output
                    }
                elif len(set(player_settings.values())) > 0:
                    important[option] = player_settings[1]
                else:
                    logging.debug(
                        f"No player settings defined for option '{option}'")
            else:
                if player_settings != "":  # is not empty name
                    important[option] = player_settings
                else:
                    logging.debug(
                        f"No player settings defined for option '{option}'")
        if args.outputpath:
            os.makedirs(args.outputpath, exist_ok=True)
        with open(
                os.path.join(args.outputpath if args.outputpath else ".",
                             f"mystery_result_{seed}.yaml"), "wt") as f:
            yaml.dump(important, f)

    erargs.skip_progression_balancing = {
        player: not balanced
        for player, balanced in erargs.progression_balancing.items()
    }
    del (erargs.progression_balancing)
    ERmain(erargs, seed)
Esempio n. 5
0
def main():
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('--multi', default=1, type=lambda value: min(max(int(value), 1), 255))
    multiargs, _ = parser.parse_known_args()

    parser = argparse.ArgumentParser()
    parser.add_argument('--weights',
                        help='Path to the weights file to use for rolling game settings, urls are also valid')
    parser.add_argument('--samesettings', help='Rolls settings per weights file rather than per player',
                        action='store_true')
    parser.add_argument('--seed', help='Define seed number to generate.', type=int)
    parser.add_argument('--multi', default=1, type=lambda value: min(max(int(value), 1), 255))
    parser.add_argument('--teams', default=1, type=lambda value: max(int(value), 1))
    parser.add_argument('--create_spoiler', action='store_true')
    parser.add_argument('--rom')
    parser.add_argument('--enemizercli')
    parser.add_argument('--outputpath')
    parser.add_argument('--race', action='store_true')
    parser.add_argument('--meta', default=None)

    for player in range(1, multiargs.multi + 1):
        parser.add_argument(f'--p{player}', help=argparse.SUPPRESS)
    args = parser.parse_args()

    if args.seed is None:
        random.seed(None)
        seed = random.randint(0, 999999999)
    else:
        seed = args.seed
    random.seed(seed)

    seedname = "M"+(f"{random.randint(0, 999999999)}".zfill(9))
    print(f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}")

    weights_cache = {}
    if args.weights:
        try:
            weights_cache[args.weights] = get_weights(args.weights)
        except Exception as e:
            raise ValueError(f"File {args.weights} is destroyed. Please fix your yaml.") from e
        print(f"Weights: {args.weights} >> {weights_cache[args.weights]['description']}")
    if args.meta:
        try:
            weights_cache[args.meta] = get_weights(args.meta)
        except Exception as e:
            raise ValueError(f"File {args.meta} is destroyed. Please fix your yaml.") from e
        print(f"Meta: {args.meta} >> {weights_cache[args.meta]['meta_description']}")
        if args.samesettings:
            raise Exception("Cannot mix --samesettings with --meta")

    for player in range(1, args.multi + 1):
        path = getattr(args, f'p{player}')
        if path:
            try:
                if path not in weights_cache:
                    weights_cache[path] = get_weights(path)
                print(f"P{player} Weights: {path} >> {weights_cache[path]['description']}")

            except Exception as e:
                raise ValueError(f"File {path} is destroyed. Please fix your yaml.") from e
    erargs = parse_arguments(['--multi', str(args.multi)])
    erargs.seed = seed
    erargs.name = {x: "" for x in range(1, args.multi + 1)} # only so it can be overwrittin in mystery
    erargs.create_spoiler = args.create_spoiler
    erargs.race = args.race
    erargs.outputname = seedname
    erargs.outputpath = args.outputpath
    erargs.teams = args.teams

    # set up logger
    loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[erargs.loglevel]
    logging.basicConfig(format='%(message)s', level=loglevel)

    if args.rom:
        erargs.rom = args.rom

    if args.enemizercli:
        erargs.enemizercli = args.enemizercli

    settings_cache = {k: (roll_settings(v) if args.samesettings else None) for k, v in weights_cache.items()}
    player_path_cache = {}
    for player in range(1, args.multi + 1):
        player_path_cache[player] = getattr(args, f'p{player}') if getattr(args, f'p{player}') else args.weights

    if args.meta:
        for player, path in player_path_cache.items():
            weights_cache[path].setdefault("meta_ignore", [])
        meta_weights = weights_cache[args.meta]
        for key in meta_weights:
            option = get_choice(key, meta_weights)
            if option is not None:
                for player, path in player_path_cache.items():
                    players_meta = weights_cache[path]["meta_ignore"]
                    if key not in players_meta:
                        weights_cache[path][key] = option
                    elif type(players_meta) == dict and option not in players_meta[key]:
                        weights_cache[path][key] = option

    for player in range(1, args.multi + 1):
        path = player_path_cache[player]
        if path:
            try:
                settings = settings_cache[path] if settings_cache[path] else roll_settings(weights_cache[path])
                if settings.sprite is not None and not os.path.isfile(settings.sprite) and not get_sprite_from_name(settings.sprite):
                    logging.warning(
                        f"Warning: The chosen sprite, \"{settings.sprite}\", for yaml \"{path}\", does not exist.")
                for k, v in vars(settings).items():
                    if v is not None:
                        getattr(erargs, k)[player] = v
            except Exception as e:
                raise ValueError(f"File {path} is destroyed. Please fix your yaml.") from e
        else:
            raise RuntimeError(f'No weights specified for player {player}')
        if not erargs.name[player]:
            erargs.name[player] = os.path.split(path)[-1].split(".")[0]
    if args.samesettings:
        erargs.names = ""
    else:
        erargs.names = ",".join(erargs.name[i] for i in range(1, args.multi + 1))
    del (erargs.name)

    ERmain(erargs, seed)
Esempio n. 6
0
def main():
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('--multi',
                        default=1,
                        type=lambda value: min(max(int(value), 1), 255))
    multiargs, _ = parser.parse_known_args()

    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--weights',
        help=
        'Path to the weights file to use for rolling game settings, urls are also valid'
    )
    parser.add_argument(
        '--samesettings',
        help='Rolls settings per weights file rather than per player',
        action='store_true')
    parser.add_argument('--seed',
                        help='Define seed number to generate.',
                        type=int)
    parser.add_argument('--multi',
                        default=1,
                        type=lambda value: min(max(int(value), 1), 255))
    parser.add_argument('--names', default='')
    parser.add_argument('--teams',
                        default=1,
                        type=lambda value: max(int(value), 1))
    parser.add_argument('--create_spoiler', action='store_true')
    parser.add_argument('--rom')
    parser.add_argument('--enemizercli')
    parser.add_argument('--outputpath')
    for player in range(1, multiargs.multi + 1):
        parser.add_argument(f'--p{player}', help=argparse.SUPPRESS)
    args = parser.parse_args()

    if args.seed is None:
        random.seed(None)
        seed = random.randint(0, 999999999)
    else:
        seed = args.seed
    random.seed(seed)

    seedname = f'M{random.randint(0, 999999999)}'
    print(
        f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}"
    )

    weights_cache = {}
    if args.weights:
        weights_cache[args.weights] = get_weights(args.weights)
        print(
            f"Weights: {args.weights} >> {weights_cache[args.weights]['description']}"
        )
    for player in range(1, args.multi + 1):
        path = getattr(args, f'p{player}')
        if path:
            if path not in weights_cache:
                weights_cache[path] = get_weights(path)
            print(
                f"P{player} Weights: {path} >> {weights_cache[path]['description']}"
            )

    erargs = parse_arguments(['--multi', str(args.multi)])
    erargs.seed = seed
    erargs.names = args.names
    erargs.create_spoiler = args.create_spoiler
    erargs.race = True
    erargs.outputname = seedname
    erargs.outputpath = args.outputpath

    if args.rom:
        erargs.rom = args.rom
    if args.enemizercli:
        erargs.enemizercli = args.enemizercli

    settings_cache = {
        k: (roll_settings(v) if args.samesettings else None)
        for k, v in weights_cache.items()
    }
    for player in range(1, args.multi + 1):
        path = getattr(args, f'p{player}') if getattr(
            args, f'p{player}') else args.weights
        if path:
            settings = settings_cache[path] if settings_cache[
                path] else roll_settings(weights_cache[path])
            for k, v in vars(settings).items():
                if v is not None:
                    getattr(erargs, k)[player] = v
        else:
            raise RuntimeError(f'No weights specified for player {player}')

    # set up logger
    loglevel = {
        'error': logging.ERROR,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'debug': logging.DEBUG
    }[erargs.loglevel]
    logging.basicConfig(format='%(message)s', level=loglevel)

    ERmain(erargs, seed)