Customizer main work
This commit is contained in:
329
source/classes/CustomSettings.py
Normal file
329
source/classes/CustomSettings.py
Normal file
@@ -0,0 +1,329 @@
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import yaml
|
||||
from yaml.representer import Representer
|
||||
from collections import defaultdict
|
||||
|
||||
import RaceRandom as random
|
||||
from BaseClasses import LocationType, DoorType
|
||||
from source.tools.MysteryUtils import roll_settings, get_weights
|
||||
|
||||
|
||||
class CustomSettings(object):
|
||||
|
||||
def __init__(self):
|
||||
self.file_source = None
|
||||
self.relative_dir = None
|
||||
self.world_rep = {}
|
||||
self.player_range = None
|
||||
|
||||
def load_yaml(self, file):
|
||||
self.file_source = load_yaml(file)
|
||||
head, filename = os.path.split(file)
|
||||
self.relative_dir = head
|
||||
|
||||
def determine_seed(self):
|
||||
if 'meta' not in self.file_source:
|
||||
return None
|
||||
meta = defaultdict(lambda: None, self.file_source['meta'])
|
||||
seed = meta['seed']
|
||||
if seed:
|
||||
random.seed(seed)
|
||||
return seed
|
||||
return None
|
||||
|
||||
def determine_players(self):
|
||||
if 'meta' not in self.file_source:
|
||||
return None
|
||||
meta = defaultdict(lambda: None, self.file_source['meta'])
|
||||
return meta['players']
|
||||
|
||||
def adjust_args(self, args):
|
||||
def get_setting(value, default):
|
||||
if value:
|
||||
return value
|
||||
return default
|
||||
if 'meta' in self.file_source:
|
||||
meta = defaultdict(lambda: None, self.file_source['meta'])
|
||||
args.multi = get_setting(meta['players'], args.multi)
|
||||
args.algorithm = get_setting(meta['algorithm'], args.algorithm)
|
||||
args.outputname = get_setting(meta['name'], args.outputname)
|
||||
args.bps = get_setting(meta['bps'], args.bps)
|
||||
args.suppress_rom = get_setting(meta['suppress_rom'], args.suppress_rom)
|
||||
args.names = get_setting(meta['names'], args.names)
|
||||
self.player_range = range(1, args.multi + 1)
|
||||
if 'settings' in self.file_source:
|
||||
for p in self.player_range:
|
||||
player_setting = self.file_source['settings'][p]
|
||||
if isinstance(player_setting, str):
|
||||
weights = get_weights(os.path.join(self.relative_dir, player_setting))
|
||||
settings = defaultdict(lambda: None, vars(roll_settings(weights)))
|
||||
else:
|
||||
settings = defaultdict(lambda: None, player_setting)
|
||||
args.shuffle[p] = get_setting(settings['shuffle'], args.shuffle[p])
|
||||
args.door_shuffle[p] = get_setting(settings['door_shuffle'], args.door_shuffle[p])
|
||||
args.logic[p] = get_setting(settings['logic'], args.logic[p])
|
||||
args.mode[p] = get_setting(settings['mode'], args.mode[p])
|
||||
args.swords[p] = get_setting(settings['swords'], args.swords[p])
|
||||
args.item_functionality[p] = get_setting(settings['item_functionality'], args.item_functionality[p])
|
||||
args.goal[p] = get_setting(settings['goal'], args.goal[p])
|
||||
args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p])
|
||||
args.accessibility[p] = get_setting(settings['accessibility'], args.accessibility[p])
|
||||
args.retro[p] = get_setting(settings['retro'], args.retro[p])
|
||||
args.hints[p] = get_setting(settings['hints'], args.hints[p])
|
||||
args.shopsanity[p] = get_setting(settings['shopsanity'], args.shopsanity[p])
|
||||
args.dropshuffle[p] = get_setting(settings['dropshuffle'], args.dropshuffle[p])
|
||||
args.pottery[p] = get_setting(settings['pottery'], args.pottery[p])
|
||||
args.mixed_travel[p] = get_setting(settings['mixed_travel'], args.mixed_travel[p])
|
||||
args.standardize_palettes[p] = get_setting(settings['standardize_palettes'],
|
||||
args.standardize_palettes[p])
|
||||
args.intensity[p] = get_setting(settings['intensity'], args.intensity[p])
|
||||
args.dungeon_counters[p] = get_setting(settings['dungeon_counters'], args.dungeon_counters[p])
|
||||
args.crystals_gt[p] = get_setting(settings['crystals_gt'], args.crystals_gt[p])
|
||||
args.crystals_ganon[p] = get_setting(settings['crystals_ganon'], args.crystals_ganon[p])
|
||||
args.experimental[p] = get_setting(settings['experimental'], args.experimental[p])
|
||||
args.openpyramid[p] = get_setting(settings['openpyramid'], args.openpyramid[p])
|
||||
args.bigkeyshuffle[p] = get_setting(settings['bigkeyshuffle'], args.bigkeyshuffle[p])
|
||||
args.keyshuffle[p] = get_setting(settings['keyshuffle'], args.keyshuffle[p])
|
||||
args.mapshuffle[p] = get_setting(settings['mapshuffle'], args.mapshuffle[p])
|
||||
args.compassshuffle[p] = get_setting(settings['compassshuffle'], args.compassshuffle[p])
|
||||
args.shufflebosses[p] = get_setting(settings['shufflebosses'], args.shufflebosses[p])
|
||||
args.shuffleenemies[p] = get_setting(settings['shuffleenemies'], args.shuffleenemies[p])
|
||||
args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p])
|
||||
args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p])
|
||||
args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p])
|
||||
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
|
||||
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
|
||||
args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p])
|
||||
args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p])
|
||||
args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p])
|
||||
args.triforce_goal[p] = get_setting(settings['triforce_goal'], args.triforce_goal[p])
|
||||
args.triforce_pool[p] = get_setting(settings['triforce_pool'], args.triforce_pool[p])
|
||||
args.beemizer[p] = get_setting(settings['beemizer'], args.beemizer[p])
|
||||
|
||||
# mystery usage
|
||||
args.usestartinventory[p] = get_setting(settings['usestartinventory'], args.usestartinventory[p])
|
||||
args.startinventory[p] = get_setting(settings['startinventory'], args.startinventory[p])
|
||||
|
||||
# rom adjust stuff
|
||||
args.sprite[p] = get_setting(settings['sprite'], args.sprite[p])
|
||||
args.disablemusic[p] = get_setting(settings['disablemusic'], args.disablemusic[p])
|
||||
args.quickswap[p] = get_setting(settings['quickswap'], args.quickswap[p])
|
||||
args.reduce_flashing[p] = get_setting(settings['reduce_flashing'], args.reduce_flashing[p])
|
||||
args.fastmenu[p] = get_setting(settings['fastmenu'], args.fastmenu[p])
|
||||
args.heartcolor[p] = get_setting(settings['heartcolor'], args.heartcolor[p])
|
||||
args.heartbeep[p] = get_setting(settings['heartbeep'], args.heartbeep[p])
|
||||
args.ow_palettes[p] = get_setting(settings['ow_palettes'], args.ow_palettes[p])
|
||||
args.uw_palettes[p] = get_setting(settings['uw_palettes'], args.uw_palettes[p])
|
||||
args.shuffle_sfx[p] = get_setting(settings['shuffle_sfx'], args.shuffle_sfx[p])
|
||||
|
||||
def get_item_pool(self):
|
||||
if 'item_pool' in self.file_source:
|
||||
return self.file_source['item_pool']
|
||||
return None
|
||||
|
||||
def get_placements(self):
|
||||
if 'placements' in self.file_source:
|
||||
return self.file_source['placements']
|
||||
return None
|
||||
|
||||
def get_entrances(self):
|
||||
if 'entrances' in self.file_source:
|
||||
return self.file_source['entrances']
|
||||
return None
|
||||
|
||||
def get_doors(self):
|
||||
if 'doors' in self.file_source:
|
||||
return self.file_source['doors']
|
||||
return None
|
||||
|
||||
def get_bosses(self):
|
||||
if 'bosses' in self.file_source:
|
||||
return self.file_source['bosses']
|
||||
return None
|
||||
|
||||
def get_start_inventory(self):
|
||||
if 'start_inventory' in self.file_source:
|
||||
return self.file_source['start_inventory']
|
||||
return None
|
||||
|
||||
def get_medallions(self):
|
||||
if 'medallions' in self.file_source:
|
||||
return self.file_source['medallions']
|
||||
return None
|
||||
|
||||
def create_from_world(self, world):
|
||||
self.player_range = range(1, world.players + 1)
|
||||
settings_dict, meta_dict = {}, {}
|
||||
self.world_rep['meta'] = meta_dict
|
||||
meta_dict['players'] = world.players
|
||||
meta_dict['algorithm'] = world.algorithm
|
||||
meta_dict['seed'] = world.seed
|
||||
self.world_rep['settings'] = settings_dict
|
||||
for p in self.player_range:
|
||||
settings_dict[p] = {}
|
||||
settings_dict[p]['shuffle'] = world.shuffle[p]
|
||||
settings_dict[p]['door_shuffle'] = world.doorShuffle[p]
|
||||
settings_dict[p]['intensity'] = world.intensity[p]
|
||||
settings_dict[p]['logic'] = world.logic[p]
|
||||
settings_dict[p]['mode'] = world.mode[p]
|
||||
settings_dict[p]['swords'] = world.swords[p]
|
||||
settings_dict[p]['difficulty'] = world.difficulty[p]
|
||||
settings_dict[p]['goal'] = world.goal[p]
|
||||
settings_dict[p]['accessibility'] = world.accessibility[p]
|
||||
settings_dict[p]['item_functionality'] = world.difficulty_adjustments[p]
|
||||
settings_dict[p]['retro'] = world.retro[p]
|
||||
settings_dict[p]['hints'] = world.hints[p]
|
||||
settings_dict[p]['shopsanity'] = world.shopsanity[p]
|
||||
settings_dict[p]['dropshuffle'] = world.dropshuffle[p]
|
||||
settings_dict[p]['pottery'] = world.pottery[p]
|
||||
settings_dict[p]['mixed_travel'] = world.mixed_travel[p]
|
||||
settings_dict[p]['standardize_palettes'] = world.standardize_palettes[p]
|
||||
settings_dict[p]['dungeon_counters'] = world.dungeon_counters[p]
|
||||
settings_dict[p]['crystals_gt'] = world.crystals_gt_orig[p]
|
||||
settings_dict[p]['crystals_ganon'] = world.crystals_ganon_orig[p]
|
||||
settings_dict[p]['experimental'] = world.experimental[p]
|
||||
settings_dict[p]['openpyramid'] = world.open_pyramid[p]
|
||||
settings_dict[p]['bigkeyshuffle'] = world.bigkeyshuffle[p]
|
||||
settings_dict[p]['keyshuffle'] = world.keyshuffle[p]
|
||||
settings_dict[p]['mapshuffle'] = world.mapshuffle[p]
|
||||
settings_dict[p]['compassshuffle'] = world.compassshuffle[p]
|
||||
settings_dict[p]['shufflebosses'] = world.boss_shuffle[p]
|
||||
settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p]
|
||||
settings_dict[p]['enemy_health'] = world.enemy_health[p]
|
||||
settings_dict[p]['enemy_damage'] = world.enemy_damage[p]
|
||||
settings_dict[p]['shufflepots'] = world.potshuffle[p]
|
||||
settings_dict[p]['bombbag'] = world.bombbag[p]
|
||||
settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
|
||||
settings_dict[p]['overworld_map'] = world.overworld_map[p]
|
||||
settings_dict[p]['pseudoboots'] = world.pseudoboots[p]
|
||||
settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p]
|
||||
settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p]
|
||||
settings_dict[p]['beemizer'] = world.beemizer[p]
|
||||
|
||||
# rom adjust stuff
|
||||
# settings_dict[p]['sprite'] = world.sprite[p]
|
||||
# settings_dict[p]['disablemusic'] = world.disablemusic[p]
|
||||
# settings_dict[p]['quickswap'] = world.quickswap[p]
|
||||
# settings_dict[p]['reduce_flashing'] = world.reduce_flashing[p]
|
||||
# settings_dict[p]['fastmenu'] = world.fastmenu[p]
|
||||
# settings_dict[p]['heartcolor'] = world.heartcolor[p]
|
||||
# settings_dict[p]['heartbeep'] = world.heartbeep[p]
|
||||
# settings_dict[p]['ow_palettes'] = world.ow_palettes[p]
|
||||
# settings_dict[p]['uw_palettes'] = world.uw_palettes[p]
|
||||
# settings_dict[p]['shuffle_sfx'] = world.shuffle_sfx[p]
|
||||
# more settings?
|
||||
|
||||
def record_info(self, world):
|
||||
self.world_rep['bosses'] = bosses = {}
|
||||
self.world_rep['start_inventory'] = start_inv = {}
|
||||
for p in self.player_range:
|
||||
bosses[p] = {}
|
||||
start_inv[p] = []
|
||||
for dungeon in world.dungeons:
|
||||
for level, boss in dungeon.bosses.items():
|
||||
location = dungeon.name if level is None else f'{dungeon.name} ({level})'
|
||||
if boss and 'Agahnim' not in boss.name:
|
||||
bosses[dungeon.player][location] = boss.name
|
||||
for item in world.precollected_items:
|
||||
start_inv[item.player].append(item.name)
|
||||
|
||||
def record_item_pool(self, world):
|
||||
self.world_rep['item_pool'] = item_pool = {}
|
||||
self.world_rep['medallions'] = medallions = {}
|
||||
for p in self.player_range:
|
||||
item_pool[p] = defaultdict(int)
|
||||
medallions[p] = {}
|
||||
for item in world.itempool:
|
||||
item_pool[item.player][item.name] += 1
|
||||
for p, req_medals in world.required_medallions.items():
|
||||
medallions[p]['Misery Mire'] = req_medals[0]
|
||||
medallions[p]['Turtle Rock'] = req_medals[1]
|
||||
|
||||
def record_item_placements(self, world):
|
||||
self.world_rep['placements'] = placements = {}
|
||||
for p in self.player_range:
|
||||
placements[p] = {}
|
||||
for location in world.get_locations():
|
||||
if location.type != LocationType.Logical and not location.skip:
|
||||
if location.player != location.item.player:
|
||||
placements[location.player][location.name] = f'{location.item.name}#{location.item.player}'
|
||||
else:
|
||||
placements[location.player][location.name] = location.item.name
|
||||
|
||||
def record_entrances(self, world):
|
||||
self.world_rep['entrances'] = entrances = {}
|
||||
world.custom_entrances = {}
|
||||
for p in self.player_range:
|
||||
connections = entrances[p] = {}
|
||||
connections['entrances'] = {}
|
||||
connections['exits'] = {}
|
||||
connections['two-way'] = {}
|
||||
for key, data in world.spoiler.entrances.items():
|
||||
player = data['player'] if 'player' in data else 1
|
||||
connections = entrances[player]
|
||||
sub = 'two-way' if data['direction'] == 'both' else 'exits' if data['direction'] == 'exit' else 'entrances'
|
||||
connections[sub][data['entrance']] = data['exit']
|
||||
|
||||
def record_doors(self, world):
|
||||
self.world_rep['doors'] = doors = {}
|
||||
for p in self.player_range:
|
||||
meta_doors = doors[p] = {}
|
||||
lobbies = meta_doors['lobbies'] = {}
|
||||
door_map = meta_doors['doors'] = {}
|
||||
for portal in world.dungeon_portals[p]:
|
||||
lobbies[portal.name] = portal.door.name
|
||||
door_types = {DoorType.Normal, DoorType.SpiralStairs, DoorType.Interior}
|
||||
if world.intensity[p] > 1:
|
||||
door_types.update([DoorType.Open, DoorType.StraightStairs, DoorType.Ladder])
|
||||
door_kinds, skip = {}, set()
|
||||
for key, info in world.spoiler.doorTypes.items():
|
||||
if key[1] == p:
|
||||
if ' <-> ' in info['doorNames']:
|
||||
dns = info['doorNames'].split(' <-> ')
|
||||
for dn in dns:
|
||||
door_kinds[dn] = info['type'] # Key Door, Bomb Door, Dash Door
|
||||
else:
|
||||
door_kinds[info['doorNames']] = info['type']
|
||||
for door in world.doors:
|
||||
if door.player == p and not door.entranceFlag and door.type in door_types and door not in skip:
|
||||
if door.type == DoorType.Interior:
|
||||
if door.name in door_types:
|
||||
door_value = {'type': door_kinds[door.name]}
|
||||
door_map[door.name] = door_value # intra-tile note
|
||||
skip.add(door.dest)
|
||||
elif door.dest:
|
||||
if door.dest.dest == door:
|
||||
door_value = door.dest.name
|
||||
skip.add(door.dest)
|
||||
if door.name in door_kinds:
|
||||
door_value = {'dest': door_value, 'type': door_kinds[door.name]}
|
||||
if door.name not in door_kinds and door.dest.name in door_kinds:
|
||||
# tricky swap thing
|
||||
door_value = {'dest': door.name, 'type': door_kinds[door.dest.name]}
|
||||
door = door.dest # this is weird
|
||||
elif door.name in door_kinds:
|
||||
door_value = {'dest': door.dest.name, 'one-way': True, 'type': door_kinds[door.name]}
|
||||
else:
|
||||
door_value = {'dest': door.dest.name, 'one-way': True}
|
||||
door_map[door.name] = door_value
|
||||
|
||||
def record_medallions(self):
|
||||
pass
|
||||
|
||||
def write_to_file(self, destination):
|
||||
yaml.add_representer(defaultdict, Representer.represent_dict)
|
||||
with open(destination, 'w') as file:
|
||||
yaml.dump(self.world_rep, file)
|
||||
|
||||
|
||||
def load_yaml(path):
|
||||
try:
|
||||
if urllib.parse.urlparse(path).scheme:
|
||||
return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||
except Exception as e:
|
||||
raise Exception(f'Failed to read customizer file: {e}')
|
||||
|
||||
@@ -122,6 +122,7 @@ SETTINGSTOPROCESS = {
|
||||
"createspoiler": "create_spoiler",
|
||||
"createrom": "create_rom",
|
||||
"calcplaythrough": "calc_playthrough",
|
||||
"print_custom_yaml": "print_custom_yaml",
|
||||
"usestartinventory": "usestartinventory",
|
||||
"usecustompool": "custom",
|
||||
"saveonexit": "saveonexit"
|
||||
|
||||
@@ -79,6 +79,48 @@ def generation_page(parent,settings):
|
||||
# frame: pack
|
||||
self.widgets[widget].pieces["frame"].pack(fill=X)
|
||||
|
||||
|
||||
self.frames["customizer"] = Frame(self)
|
||||
self.frames["customizer"].pack(anchor=W, fill=X)
|
||||
## Customizer file
|
||||
# This one's more-complicated, build it and stuff it
|
||||
# widget ID
|
||||
widget = "customizer"
|
||||
|
||||
# Empty object
|
||||
self.widgets[widget] = Empty()
|
||||
# pieces
|
||||
self.widgets[widget].pieces = {}
|
||||
|
||||
# frame
|
||||
self.widgets[widget].pieces["frame"] = Frame(self.frames["customizer"])
|
||||
# frame: label
|
||||
self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text='Customizer File: ')
|
||||
# storage var
|
||||
self.widgets[widget].storageVar = StringVar()
|
||||
# textbox
|
||||
self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"],
|
||||
textvariable=self.widgets[widget].storageVar)
|
||||
self.widgets[widget].storageVar.set(settings["customizer"])
|
||||
|
||||
# FIXME: Translate these
|
||||
def FileSelect():
|
||||
file = filedialog.askopenfilename(filetypes=[("Yaml Files", (".yaml", ".yml")), ("All Files", "*")],
|
||||
initialdir=os.path.join("."))
|
||||
self.widgets["customizer"].storageVar.set(file)
|
||||
# dialog button
|
||||
self.widgets[widget].pieces["button"] = Button(self.widgets[widget].pieces["frame"],
|
||||
text='Select File', command=FileSelect)
|
||||
|
||||
# frame label: pack
|
||||
self.widgets[widget].pieces["frame"].label.pack(side=LEFT)
|
||||
# textbox: pack
|
||||
self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True)
|
||||
# button: pack
|
||||
self.widgets[widget].pieces["button"].pack(side=LEFT)
|
||||
# frame: pack
|
||||
self.widgets[widget].pieces["frame"].pack(fill=X)
|
||||
|
||||
## Run Diagnostics
|
||||
# This one's more-complicated, build it and stuff it
|
||||
# widget ID
|
||||
|
||||
3003
source/overworld/EntranceShuffle2.py
Normal file
3003
source/overworld/EntranceShuffle2.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,124 +0,0 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import multiprocessing
|
||||
import concurrent.futures
|
||||
import argparse
|
||||
from collections import OrderedDict
|
||||
|
||||
cpu_threads = multiprocessing.cpu_count()
|
||||
py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||||
|
||||
|
||||
def main(args=None):
|
||||
successes = []
|
||||
errors = []
|
||||
task_mapping = []
|
||||
tests = OrderedDict()
|
||||
|
||||
successes.append(f"Testing {args.dr} DR with {args.count} Tests" + (f" (intensity={args.tense})" if args.dr in ['basic', 'crossed'] else ""))
|
||||
print(successes[0])
|
||||
|
||||
max_attempts = args.count
|
||||
pool = concurrent.futures.ThreadPoolExecutor(max_workers=cpu_threads)
|
||||
dead_or_alive = 0
|
||||
alive = 0
|
||||
|
||||
def test(testname: str, command: str):
|
||||
tests[testname] = [command]
|
||||
basecommand = f"python3.8 Mystery.py --suppress_rom"
|
||||
|
||||
def gen_seed():
|
||||
taskcommand = basecommand + " " + command
|
||||
return subprocess.run(taskcommand, capture_output=True, shell=True, text=True)
|
||||
|
||||
for x in range(1, max_attempts + 1):
|
||||
task = pool.submit(gen_seed)
|
||||
task.success = False
|
||||
task.name = testname
|
||||
task.mode = "Mystery"
|
||||
task.cmd = basecommand + " " + command
|
||||
task_mapping.append(task)
|
||||
|
||||
for i in range(0, 100):
|
||||
test("Mystery", "--weights mystery_testsuite.yml")
|
||||
|
||||
from tqdm import tqdm
|
||||
with tqdm(concurrent.futures.as_completed(task_mapping),
|
||||
total=len(task_mapping), unit="seed(s)",
|
||||
desc=f"Success rate: 0.00%") as progressbar:
|
||||
for task in progressbar:
|
||||
dead_or_alive += 1
|
||||
try:
|
||||
result = task.result()
|
||||
if result.returncode:
|
||||
errors.append([task.name, task.cmd, result.stderr])
|
||||
else:
|
||||
alive += 1
|
||||
task.success = True
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
progressbar.set_description(f"Success rate: {(alive/dead_or_alive)*100:.2f}% - {task.name}")
|
||||
|
||||
def get_results(testname: str):
|
||||
result = ""
|
||||
for mode in ['Mystery']:
|
||||
dead_or_alive = [task.success for task in task_mapping if task.name == testname and task.mode == mode]
|
||||
alive = [x for x in dead_or_alive if x]
|
||||
success = f"{testname} Rate: {(len(alive) / len(dead_or_alive)) * 100:.2f}%"
|
||||
successes.append(success)
|
||||
print(success)
|
||||
result += f"{(len(alive)/len(dead_or_alive))*100:.2f}%\t"
|
||||
return result.strip()
|
||||
|
||||
results = []
|
||||
for t in tests.keys():
|
||||
results.append(get_results(t))
|
||||
|
||||
for result in results:
|
||||
print(result)
|
||||
successes.append(result)
|
||||
|
||||
return successes, errors
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
successes = []
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument('--count', default=0, type=lambda value: max(int(value), 0))
|
||||
parser.add_argument('--cpu_threads', default=cpu_threads, type=lambda value: max(int(value), 1))
|
||||
parser.add_argument('--help', default=False, action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.help:
|
||||
parser.print_help()
|
||||
exit(0)
|
||||
|
||||
cpu_threads = args.cpu_threads
|
||||
|
||||
for dr in [['mystery', args.count if args.count else 1, 1]]:
|
||||
|
||||
for tense in range(1, dr[2] + 1):
|
||||
args = argparse.Namespace()
|
||||
args.dr = dr[0]
|
||||
args.tense = tense
|
||||
args.count = dr[1]
|
||||
s, errors = main(args=args)
|
||||
if successes:
|
||||
successes += [""] * 2
|
||||
successes += s
|
||||
print()
|
||||
|
||||
if errors:
|
||||
with open(f"{dr[0]}{(f'-{tense}' if dr[0] in ['basic', 'crossed'] else '')}-errors.txt", 'w') as stream:
|
||||
for error in errors:
|
||||
stream.write(error[0] + "\n")
|
||||
stream.write(error[1] + "\n")
|
||||
stream.write(error[2] + "\n\n")
|
||||
|
||||
with open("success.txt", "w") as stream:
|
||||
stream.write(str.join("\n", successes))
|
||||
|
||||
input("Press enter to continue")
|
||||
190
source/tools/MysteryUtils.py
Normal file
190
source/tools/MysteryUtils.py
Normal file
@@ -0,0 +1,190 @@
|
||||
import argparse
|
||||
import RaceRandom as random
|
||||
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import yaml
|
||||
|
||||
|
||||
def get_weights(path):
|
||||
try:
|
||||
if urllib.parse.urlparse(path).scheme:
|
||||
return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||
except Exception as e:
|
||||
raise Exception(f'Failed to read weights file: {e}')
|
||||
|
||||
|
||||
def roll_settings(weights):
|
||||
def get_choice(option, root=None):
|
||||
root = weights if root is None else root
|
||||
if option not in root:
|
||||
return None
|
||||
if type(root[option]) is not dict:
|
||||
return root[option]
|
||||
if not root[option]:
|
||||
return None
|
||||
return random.choices(list(root[option].keys()), weights=list(map(int, root[option].values())))[0]
|
||||
|
||||
def get_choice_default(option, root=weights, default=None):
|
||||
choice = get_choice(option, root)
|
||||
if choice is None and default is not None:
|
||||
return default
|
||||
return choice
|
||||
|
||||
while True:
|
||||
subweights = weights.get('subweights', {})
|
||||
if len(subweights) == 0:
|
||||
break
|
||||
chances = ({k: int(v['chance']) for (k, v) in subweights.items()})
|
||||
subweight_name = random.choices(list(chances.keys()), weights=list(chances.values()))[0]
|
||||
subweights = weights.get('subweights', {}).get(subweight_name, {}).get('weights', {})
|
||||
subweights['subweights'] = subweights.get('subweights', {})
|
||||
weights = {**weights, **subweights}
|
||||
|
||||
ret = argparse.Namespace()
|
||||
|
||||
ret.algorithm = get_choice('algorithm')
|
||||
ret.mystery = get_choice_default('mystery', default=True)
|
||||
|
||||
glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owglitches': 'owglitches',
|
||||
'minorglitches': 'minorglitches'}
|
||||
glitches_required = get_choice('glitches_required')
|
||||
if glitches_required is not None:
|
||||
if glitches_required not in glitch_map.keys():
|
||||
print(f'Logic did not match one of: {", ".join(glitch_map.keys())}')
|
||||
glitches_required = 'none'
|
||||
ret.logic = glitch_map[glitches_required]
|
||||
|
||||
# item_placement = get_choice('item_placement')
|
||||
# not supported in ER
|
||||
|
||||
dungeon_items = get_choice('dungeon_items')
|
||||
dungeon_items = '' if dungeon_items == 'standard' or dungeon_items is None else dungeon_items
|
||||
dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items
|
||||
ret.mapshuffle = get_choice('map_shuffle') == 'on' if 'map_shuffle' in weights else 'm' in dungeon_items
|
||||
ret.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else 'c' in dungeon_items
|
||||
ret.keyshuffle = get_choice('smallkey_shuffle') == 'on' if 'smallkey_shuffle' in weights else 's' in dungeon_items
|
||||
ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else 'b' in dungeon_items
|
||||
|
||||
ret.accessibility = get_choice('accessibility')
|
||||
ret.restrict_boss_items = get_choice('restrict_boss_items')
|
||||
|
||||
entrance_shuffle = get_choice('entrance_shuffle')
|
||||
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
|
||||
overworld_map = get_choice('overworld_map')
|
||||
ret.overworld_map = overworld_map if overworld_map != 'default' else 'default'
|
||||
door_shuffle = get_choice('door_shuffle')
|
||||
ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla'
|
||||
ret.intensity = get_choice('intensity')
|
||||
ret.experimental = get_choice('experimental') == 'on'
|
||||
ret.collection_rate = get_choice('collection_rate') == 'on'
|
||||
|
||||
ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default'
|
||||
if ret.dungeon_counters == 'default':
|
||||
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off'
|
||||
|
||||
ret.shufflelinks = get_choice('shufflelinks') == 'on'
|
||||
ret.pseudoboots = get_choice('pseudoboots') == 'on'
|
||||
ret.shopsanity = get_choice('shopsanity') == 'on'
|
||||
keydropshuffle = get_choice('keydropshuffle') == 'on'
|
||||
ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle
|
||||
ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none'
|
||||
ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery
|
||||
ret.shufflepots = get_choice('pot_shuffle') == 'on'
|
||||
ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent'
|
||||
ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights
|
||||
else 'standardize')
|
||||
|
||||
goal = get_choice('goals')
|
||||
if goal is not None:
|
||||
ret.goal = {'ganon': 'ganon',
|
||||
'fast_ganon': 'crystals',
|
||||
'dungeons': 'dungeons',
|
||||
'pedestal': 'pedestal',
|
||||
'triforce-hunt': 'triforcehunt'
|
||||
}[goal]
|
||||
ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
|
||||
|
||||
ret.crystals_gt = get_choice('tower_open')
|
||||
|
||||
ret.crystals_ganon = get_choice('ganon_open')
|
||||
|
||||
goal_min = get_choice_default('triforce_goal_min', default=20)
|
||||
goal_max = get_choice_default('triforce_goal_max', default=20)
|
||||
pool_min = get_choice_default('triforce_pool_min', default=30)
|
||||
pool_max = get_choice_default('triforce_pool_max', default=30)
|
||||
ret.triforce_goal = random.randint(int(goal_min), int(goal_max))
|
||||
min_diff = get_choice_default('triforce_min_difference', default=10)
|
||||
ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max))
|
||||
|
||||
ret.mode = get_choice('world_state')
|
||||
if ret.mode == 'retro':
|
||||
ret.mode = 'open'
|
||||
ret.retro = True
|
||||
ret.retro = get_choice('retro') == 'on' # this overrides world_state if used
|
||||
|
||||
ret.bombbag = get_choice('bombbag') == 'on'
|
||||
|
||||
ret.hints = get_choice('hints') == 'on'
|
||||
|
||||
swords = get_choice('weapons')
|
||||
if swords is not None:
|
||||
ret.swords = {'randomized': 'random',
|
||||
'assured': 'assured',
|
||||
'vanilla': 'vanilla',
|
||||
'swordless': 'swordless'
|
||||
}[swords]
|
||||
|
||||
ret.difficulty = get_choice('item_pool')
|
||||
|
||||
ret.item_functionality = get_choice('item_functionality')
|
||||
|
||||
old_style_bosses = {'basic': 'simple',
|
||||
'normal': 'full',
|
||||
'chaos': 'random'}
|
||||
boss_choice = get_choice('boss_shuffle')
|
||||
if boss_choice in old_style_bosses.keys():
|
||||
boss_choice = old_style_bosses[boss_choice]
|
||||
ret.shufflebosses = boss_choice
|
||||
|
||||
enemy_choice = get_choice('enemy_shuffle')
|
||||
if enemy_choice == 'chaos':
|
||||
enemy_choice = 'random'
|
||||
ret.shuffleenemies = enemy_choice
|
||||
|
||||
old_style_damage = {'none': 'default',
|
||||
'chaos': 'random'}
|
||||
damage_choice = get_choice('enemy_damage')
|
||||
if damage_choice in old_style_damage:
|
||||
damage_choice = old_style_damage[damage_choice]
|
||||
ret.enemy_damage = damage_choice
|
||||
|
||||
ret.enemy_health = get_choice('enemy_health')
|
||||
|
||||
ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0'
|
||||
|
||||
inventoryweights = weights.get('startinventory', {})
|
||||
startitems = []
|
||||
for item in inventoryweights.keys():
|
||||
if get_choice(item, inventoryweights) == 'on':
|
||||
startitems.append(item)
|
||||
ret.startinventory = ','.join(startitems)
|
||||
if len(startitems) > 0:
|
||||
ret.usestartinventory = True
|
||||
|
||||
if 'rom' in weights:
|
||||
romweights = weights['rom']
|
||||
ret.sprite = get_choice('sprite', romweights)
|
||||
ret.disablemusic = get_choice('disablemusic', romweights) == 'on'
|
||||
ret.quickswap = get_choice('quickswap', romweights) == 'on'
|
||||
ret.reduce_flashing = get_choice('reduce_flashing', romweights) == 'on'
|
||||
ret.fastmenu = get_choice('menuspeed', romweights)
|
||||
ret.heartcolor = get_choice('heartcolor', romweights)
|
||||
ret.heartbeep = get_choice('heartbeep', romweights)
|
||||
ret.ow_palettes = get_choice('ow_palettes', romweights)
|
||||
ret.uw_palettes = get_choice('uw_palettes', romweights)
|
||||
ret.shuffle_sfx = get_choice('shuffle_sfx', romweights) == 'on'
|
||||
|
||||
return ret
|
||||
Reference in New Issue
Block a user