From fa41f358fd6cf5c3c1e6f4d55ecec42d2f0c6e92 Mon Sep 17 00:00:00 2001
From: Fabian Dill <fabian.dill@web.de>
Date: Sun, 19 Jan 2020 23:30:22 +0100
Subject: [PATCH] introduce PyYAML and make associated changes

---
 ModuleUpdate.py  | 10 +++++--
 Mystery.py       | 78 ++++++++++++++++++++++++------------------------
 requirements.txt |  1 +
 3 files changed, 48 insertions(+), 41 deletions(-)

diff --git a/ModuleUpdate.py b/ModuleUpdate.py
index 48f54d13..6e002afa 100644
--- a/ModuleUpdate.py
+++ b/ModuleUpdate.py
@@ -3,18 +3,24 @@ import sys
 import subprocess
 import importlib
 
-update_ran = False
+update_ran = hasattr(sys, "frozen") and getattr(sys, "frozen")  # don't run update if environment is frozen/compiled
+
 
 def update_command():
     subprocess.call([sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt', '--upgrade'])
 
+
+naming_specialties = {"PyYAML": "yaml"}  # PyYAML is imported as the name yaml
+
+
 def update():
     global update_ran
     if not update_ran:
         update_ran = True
-        with open(os.path.join(os.path.dirname(__file__),'requirements.txt')) as requirementsfile:
+        with open(os.path.join(os.path.dirname(__file__), 'requirements.txt')) as requirementsfile:
             for line in requirementsfile.readlines():
                 module, remoteversion = line.split(">=")
+                module = naming_specialties.get(module, module)
                 try:
                     module = importlib.import_module(module)
                 except:
diff --git a/Mystery.py b/Mystery.py
index 1ac7f740..83a57889 100644
--- a/Mystery.py
+++ b/Mystery.py
@@ -3,33 +3,24 @@ import logging
 import random
 import urllib.request
 import urllib.parse
+import functools
+
+import ModuleUpdate
+
+ModuleUpdate.update()
+
+from yaml import load
+
+try:
+    from yaml import CLoader as Loader
+except ImportError:
+    from yaml import Loader
 
 from EntranceRandomizer import parse_arguments
 from Main import main as ERmain
 
-def parse_yaml(txt):
-    def strip(s):
-        s = s.strip()
-        return '' if not s else s.strip('"') if s[0] == '"' else s.strip("'") if s[0] == "'" else s
-    ret = {}
-    indents = {len(txt) - len(txt.lstrip(' ')): ret}
-    for line in txt.splitlines():
-        if not line:
-            continue
-        line = line.split('#')[0]
-        if not line:
-            continue
-        name, val = line.split(':', 1)
-        val = strip(val)
-        spaces = len(name) - len(name.lstrip(' '))
-        name = strip(name)
-        if val:
-            indents[spaces][name] = val
-        else:
-            newdict = {}
-            indents[spaces][name] = newdict
-            indents[spaces+2] = newdict
-    return ret
+parse_yaml = functools.partial(load, Loader=Loader)
+
 
 def main():
     parser = argparse.ArgumentParser(add_help=False)
@@ -37,8 +28,10 @@ def main():
     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('--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='')
@@ -103,7 +96,6 @@ def main():
                 raise ValueError(f"File {path} is destroyed. Please fix your yaml.")
         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)
@@ -123,15 +115,21 @@ def get_weights(path):
 
     return parse_yaml(yaml)
 
+
+def interpret_on_off(value):
+    return {"on": True, "off": False}.get(value, value)
+
+
 def roll_settings(weights):
     def get_choice(option, root=weights):
         if option not in root:
             return None
         if type(root[option]) is not dict:
-            return root[option]
+            return interpret_on_off(root[option])
         if not root[option]:
             return None
-        return random.choices(list(root[option].keys()), weights=list(map(int,root[option].values())))[0]
+        return interpret_on_off(
+            random.choices(list(root[option].keys()), weights=list(map(int, root[option].values())))[0])
 
     ret = argparse.Namespace()
 
@@ -141,14 +139,16 @@ def roll_settings(weights):
         glitches_required = 'none'
     ret.logic = {'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required]
 
-    item_placement = get_choice('item_placement')
+    # item_placement = get_choice('item_placement')
     # not supported in ER
 
     dungeon_items = get_choice('dungeon_items')
-    ret.mapshuffle = get_choice('map_shuffle') == 'on' if 'map_shuffle' in weights else dungeon_items in ['mc', 'mcs', 'full']
-    ret.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else dungeon_items in ['mc', 'mcs', 'full']
-    ret.keyshuffle = get_choice('smallkey_shuffle') == 'on' if 'smallkey_shuffle' in weights else dungeon_items in ['mcs', 'full']
-    ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else dungeon_items in ['full']
+    if dungeon_items == 'full':
+        dungeon_items = 'mcsb'
+    ret.mapshuffle = get_choice('map_shuffle') if 'map_shuffle' in weights else 'm' in dungeon_items
+    ret.compassshuffle = get_choice('compass_shuffle') if 'compass_shuffle' in weights else 'c' in dungeon_items
+    ret.keyshuffle = get_choice('smallkey_shuffle') if 'smallkey_shuffle' in weights else 's' in dungeon_items
+    ret.bigkeyshuffle = get_choice('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items
 
     ret.accessibility = get_choice('accessibility')
 
@@ -166,14 +166,14 @@ def roll_settings(weights):
 
     ret.crystals_gt = get_choice('tower_open')
 
-    ret.crystals_ganon =  get_choice('ganon_open')
+    ret.crystals_ganon = get_choice('ganon_open')
 
     ret.mode = get_choice('world_state')
     if ret.mode == 'retro':
         ret.mode = 'open'
         ret.retro = True
 
-    ret.hints = get_choice('hints') == 'on'
+    ret.hints = get_choice('hints')
 
     ret.swords = {'randomized': 'random',
                   'assured': 'assured',
@@ -203,22 +203,22 @@ def roll_settings(weights):
 
     ret.enemy_health = get_choice('enemy_health')
 
-    ret.shufflepots = get_choice('pot_shuffle') == 'on'
+    ret.shufflepots = get_choice('pot_shuffle')
 
     ret.beemizer = int(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':
+        if get_choice(item, inventoryweights):
             startitems.append(item)
     ret.startinventory = ','.join(startitems)
 
     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.disablemusic = get_choice('disablemusic', romweights)
+        ret.quickswap = get_choice('quickswap', romweights)
         ret.fastmenu = get_choice('menuspeed', romweights)
         ret.heartcolor = get_choice('heartcolor', romweights)
         ret.heartbeep = get_choice('heartbeep', romweights)
diff --git a/requirements.txt b/requirements.txt
index e91084cc..385648dd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
 aioconsole>=0.1.15
 colorama>=0.4.3
 websockets>=8.1
+PyYAML>=5.3
\ No newline at end of file