Apply black to move Python style to standard form.
authorEric S. Raymond <esr@thyrsus.com>
Sun, 17 Sep 2023 20:17:30 +0000 (16:17 -0400)
committerEric S. Raymond <esr@thyrsus.com>
Sun, 17 Sep 2023 20:17:30 +0000 (16:17 -0400)
make_dungeon.py
make_graph.py
tests/coverage_dungeon.py

index bae97f210ad7a9759bb8b10c27a034a2f23840d0..12f39e777c6df91d1bbabc27df9f81988ffa7be2 100755 (executable)
@@ -24,6 +24,7 @@ DONOTEDIT_COMMENT = "/* Generated from adventure.yaml - do not hand-hack! */\n\n
 
 statedefines = ""
 
+
 def make_c_string(string):
     """Render a Python string into C string literal format."""
     if string is None:
@@ -35,14 +36,16 @@ def make_c_string(string):
     string = '"' + string + '"'
     return string
 
+
 def get_refs(l):
     reflist = [x[0] for x in l]
     ref_str = ""
     for ref in reflist:
         ref_str += "    {},\n".format(ref)
-    ref_str = ref_str[:-1] # trim trailing newline
+    ref_str = ref_str[:-1]  # trim trailing newline
     return ref_str
 
+
 def get_string_group(strings):
     template = """{{
             .strs = {},
@@ -51,20 +54,24 @@ def get_string_group(strings):
     if strings == []:
         strs = "NULL"
     else:
-        strs = "(const char* []) {" + ", ".join([make_c_string(s) for s in strings]) + "}"
+        strs = (
+            "(const char* []) {" + ", ".join([make_c_string(s) for s in strings]) + "}"
+        )
     n = len(strings)
     sg_str = template.format(strs, n)
     return sg_str
 
+
 def get_arbitrary_messages(arb):
     template = """    {},
 """
     arb_str = ""
     for item in arb:
         arb_str += template.format(make_c_string(item[1]))
-    arb_str = arb_str[:-1] # trim trailing newline
+    arb_str = arb_str[:-1]  # trim trailing newline
     return arb_str
 
+
 def get_class_messages(cls):
     template = """    {{
         .threshold = {},
@@ -76,9 +83,10 @@ def get_class_messages(cls):
         threshold = item["threshold"]
         message = make_c_string(item["message"])
         cls_str += template.format(threshold, message)
-    cls_str = cls_str[:-1] # trim trailing newline
+    cls_str = cls_str[:-1]  # trim trailing newline
     return cls_str
 
+
 def get_turn_thresholds(trn):
     template = """    {{
         .threshold = {},
@@ -92,9 +100,10 @@ def get_turn_thresholds(trn):
         point_loss = item["point_loss"]
         message = make_c_string(item["message"])
         trn_str += template.format(threshold, point_loss, message)
-    trn_str = trn_str[:-1] # trim trailing newline
+    trn_str = trn_str[:-1]  # trim trailing newline
     return trn_str
 
+
 def get_locations(loc):
     template = """    {{ // {}: {}
         .description = {{
@@ -112,9 +121,10 @@ def get_locations(loc):
         sound = item[1].get("sound", "SILENT")
         loud = "true" if item[1].get("loud") else "false"
         loc_str += template.format(i, item[0], short_d, long_d, sound, loud)
-    loc_str = loc_str[:-1] # trim trailing newline
+    loc_str = loc_str[:-1]  # trim trailing newline
     return loc_str
 
+
 def get_objects(obj):
     template = """    {{ // {}: {}
         .words = {},
@@ -154,7 +164,7 @@ def get_objects(obj):
                 descriptions_str += " " * 12 + make_c_string(l_msg) + ",\n"
             for label in attr.get("states", []):
                 labels.append(label)
-            descriptions_str = descriptions_str[:-1] # trim trailing newline
+            descriptions_str = descriptions_str[:-1]  # trim trailing newline
             if labels:
                 global statedefines
                 statedefines += "/* States for %s */\n" % item[0]
@@ -168,21 +178,21 @@ def get_objects(obj):
         else:
             for l_msg in attr["sounds"]:
                 sounds_str += " " * 12 + make_c_string(l_msg) + ",\n"
-            sounds_str = sounds_str[:-1] # trim trailing newline
+            sounds_str = sounds_str[:-1]  # trim trailing newline
         texts_str = ""
         if attr.get("texts") is None:
             texts_str = " " * 12 + "NULL,"
         else:
             for l_msg in attr["texts"]:
                 texts_str += " " * 12 + make_c_string(l_msg) + ",\n"
-            texts_str = texts_str[:-1] # trim trailing newline
+            texts_str = texts_str[:-1]  # trim trailing newline
         changes_str = ""
         if attr.get("changes") is None:
             changes_str = " " * 12 + "NULL,"
         else:
             for l_msg in attr["changes"]:
                 changes_str += " " * 12 + make_c_string(l_msg) + ",\n"
-            changes_str = changes_str[:-1] # trim trailing newline
+            changes_str = changes_str[:-1]  # trim trailing newline
         locs = attr.get("locations", ["LOC_NOWHERE", "LOC_NOWHERE"])
         immovable = attr.get("immovable", False)
         try:
@@ -192,11 +202,24 @@ def get_objects(obj):
             sys.stderr.write("dungeon: unknown object location in %s\n" % locs)
             sys.exit(1)
         treasure = "true" if attr.get("treasure") else "false"
-        obj_str += template.format(i, item[0], words_str, i_msg, locs[0], locs[1], treasure, descriptions_str, sounds_str, texts_str, changes_str)
-    obj_str = obj_str[:-1] # trim trailing newline
+        obj_str += template.format(
+            i,
+            item[0],
+            words_str,
+            i_msg,
+            locs[0],
+            locs[1],
+            treasure,
+            descriptions_str,
+            sounds_str,
+            texts_str,
+            changes_str,
+        )
+    obj_str = obj_str[:-1]  # trim trailing newline
     statedefines += "/* Maximum state value */\n#define MAX_STATE %d\n" % max_state
     return obj_str
 
+
 def get_obituaries(obit):
     template = """    {{
         .query = {},
@@ -208,9 +231,10 @@ def get_obituaries(obit):
         query = make_c_string(o["query"])
         yes = make_c_string(o["yes_response"])
         obit_str += template.format(query, yes)
-    obit_str = obit_str[:-1] # trim trailing newline
+    obit_str = obit_str[:-1]  # trim trailing newline
     return obit_str
 
+
 def get_hints(hnt):
     template = """    {{
         .number = {},
@@ -229,9 +253,10 @@ def get_hints(hnt):
         question = make_c_string(item["question"])
         hint = make_c_string(item["hint"])
         hnt_str += template.format(number, penalty, turns, question, hint)
-    hnt_str = hnt_str[:-1] # trim trailing newline
+    hnt_str = hnt_str[:-1]  # trim trailing newline
     return hnt_str
 
+
 def get_condbits(locations):
     cnd_str = ""
     for (name, loc) in locations:
@@ -242,7 +267,7 @@ def get_condbits(locations):
             if conditions[flag]:
                 flaglist.append(flag)
         line = "|".join([("(1<<COND_%s)" % f) for f in flaglist])
-        trail = "|".join([("(1<<COND_H%s)" % f['name']) for f in hints])
+        trail = "|".join([("(1<<COND_H%s)" % f["name"]) for f in hints])
         if trail:
             line += "|" + trail
         if line.startswith("|"):
@@ -252,6 +277,7 @@ def get_condbits(locations):
         cnd_str += "    " + line + ",\t// " + name + "\n"
     return cnd_str
 
+
 def get_motions(motions):
     template = """    {{
         .words = {},
@@ -272,6 +298,7 @@ def get_motions(motions):
                     ignore += word.upper()
     return mot_str
 
+
 def get_actions(actions):
     template = """    {{
         .words = {},
@@ -304,20 +331,22 @@ def get_actions(actions):
             for word in contents["words"]:
                 if len(word) == 1:
                     ignore += word.upper()
-    act_str = act_str[:-1] # trim trailing newline
+    act_str = act_str[:-1]  # trim trailing newline
     return act_str
 
+
 def bigdump(arr):
     out = ""
     for (i, _) in enumerate(arr):
         if i % 10 == 0:
-            if out and out[-1] == ' ':
+            if out and out[-1] == " ":
                 out = out[:-1]
             out += "\n    "
         out += str(arr[i]).lower() + ", "
     out = out[:-2] + "\n"
     return out
 
+
 def buildtravel(locs, objs):
     assert len(locs) <= 300
     assert len(objs) <= 100
@@ -337,31 +366,31 @@ def buildtravel(locs, objs):
     # location number (Y), and a list of motion numbers (see section 4).
     # each motion represents a verb which will go to Y if currently at X.
     # Y, in turn, is interpreted as follows.  Let M=Y/1000, N=Y mod 1000.
-    #          If N<=300       it is the location to go to.
-    #          If 300<N<=500   N-300 is used in a computed goto to
-    #                                  a section of special code.
-    #          If N>500        message N-500 from section 6 is printed,
-    #                                  and he stays wherever he is.
+    #          If N<=300       it is the location to go to.
+    #          If 300<N<=500   N-300 is used in a computed goto to
+    #                                  a section of special code.
+    #          If N>500        message N-500 from section 6 is printed,
+    #                                  and he stays wherever he is.
     # Meanwhile, M specifies the conditions on the motion.
-    #          If M=0          it's unconditional.
-    #          If 0<M<100      it is done with M% probability.
-    #          If M=100        unconditional, but forbidden to dwarves.
-    #          If 100<M<=200   he must be carrying object M-100.
-    #          If 200<M<=300   must be carrying or in same room as M-200.
-    #          If 300<M<=400   game.prop(M % 100) must *not* be 0.
-    #          If 400<M<=500   game.prop(M % 100) must *not* be 1.
-    #          If 500<M<=600   game.prop(M % 100) must *not* be 2, etc.
+    #          If M=0          it's unconditional.
+    #          If 0<M<100      it is done with M% probability.
+    #          If M=100        unconditional, but forbidden to dwarves.
+    #          If 100<M<=200   he must be carrying object M-100.
+    #          If 200<M<=300   must be carrying or in same room as M-200.
+    #          If 300<M<=400   game.prop(M % 100) must *not* be 0.
+    #          If 400<M<=500   game.prop(M % 100) must *not* be 1.
+    #          If 500<M<=600   game.prop(M % 100) must *not* be 2, etc.
     # If the condition (if any) is not met, then the next *different*
     # "destination" value is used (unless it fails to meet *its* conditions,
     # in which case the next is found, etc.).  Typically, the next dest will
     # be for one of the same verbs, so that its only use is as the alternate
     # destination for those verbs.  For instance:
-    #          15      110022  29      31      34      35      23      43
-    #          15      14      29
+    #          15      110022  29      31      34      35      23      43
+    #          15      14      29
     # This says that, from loc 15, any of the verbs 29, 31, etc., will take
     # him to 22 if he's carrying object 10, and otherwise will go to 14.
-    #          11      303008  49
-    #          11      9       50
+    #          11      303008  49
+    #          11      9       50
     # This says that, from 11, 49 takes him to 8 unless game.prop[3]=0, in which
     # case he goes to 9.  Verb 50 takes him to 9 regardless of game.prop[3].
     ltravel = []
@@ -372,13 +401,17 @@ def buildtravel(locs, objs):
                 verbmap[word.upper()] = i
         except TypeError:
             pass
+
     def dencode(action, name):
         "Decode a destination number"
         if action[0] == "goto":
             try:
                 return locnames.index(action[1])
             except ValueError:
-                sys.stderr.write("dungeon: unknown location %s in goto clause of %s\n" % (action[1], name))
+                sys.stderr.write(
+                    "dungeon: unknown location %s in goto clause of %s\n"
+                    % (action[1], name)
+                )
                 raise ValueError
         elif action[0] == "special":
             return 300 + action[1]
@@ -386,11 +419,15 @@ def buildtravel(locs, objs):
             try:
                 return 500 + msgnames.index(action[1])
             except ValueError:
-                sys.stderr.write("dungeon: unknown location %s in carry clause of %s\n" % (cond[1], name))
+                sys.stderr.write(
+                    "dungeon: unknown location %s in carry clause of %s\n"
+                    % (cond[1], name)
+                )
         else:
             print(cond)
             raise ValueError
-        return ''      # Pacify pylint
+        return ""  # Pacify pylint
+
     def cencode(cond, name):
         if cond is None:
             return 0
@@ -402,13 +439,19 @@ def buildtravel(locs, objs):
             try:
                 return 100 + objnames.index(cond[1])
             except ValueError:
-                sys.stderr.write("dungeon: unknown object name %s in carry clause of %s\n" % (cond[1], name))
+                sys.stderr.write(
+                    "dungeon: unknown object name %s in carry clause of %s\n"
+                    % (cond[1], name)
+                )
                 sys.exit(1)
         elif cond[0] == "with":
             try:
                 return 200 + objnames.index(cond[1])
             except IndexError:
-                sys.stderr.write("dungeon: unknown object name %s in with clause of %s\n" % (cond[1], name))
+                sys.stderr.write(
+                    "dungeon: unknown object name %s in with clause of %s\n"
+                    % (cond[1], name)
+                )
                 sys.exit(1)
         elif cond[0] == "not":
             try:
@@ -424,11 +467,17 @@ def buildtravel(locs, objs):
                                 state = i
                                 break
                     else:
-                        sys.stderr.write("dungeon: unmatched state symbol %s in not clause of %s\n" % (cond[2], name))
+                        sys.stderr.write(
+                            "dungeon: unmatched state symbol %s in not clause of %s\n"
+                            % (cond[2], name)
+                        )
                         sys.exit(0)
                 return 300 + obj + 100 * state
             except ValueError:
-                sys.stderr.write("dungeon: unknown object name %s in not clause of %s\n" % (cond[1], name))
+                sys.stderr.write(
+                    "dungeon: unknown object name %s in not clause of %s\n"
+                    % (cond[1], name)
+                )
                 sys.exit(1)
         else:
             print(cond)
@@ -438,11 +487,13 @@ def buildtravel(locs, objs):
         if "travel" in loc:
             for rule in loc["travel"]:
                 tt = [i]
-                dest = dencode(rule["action"], name) + 1000 * cencode(rule.get("cond"), name)
+                dest = dencode(rule["action"], name) + 1000 * cencode(
+                    rule.get("cond"), name
+                )
                 tt.append(dest)
                 tt += [motionnames[verbmap[e]].upper() for e in rule["verbs"]]
                 if not rule["verbs"]:
-                    tt.append(1)       # Magic dummy entry for null rules
+                    tt.append(1)  # Magic dummy entry for null rules
                 ltravel.append(tuple(tt))
 
     # At this point the ltravel data is in the Section 3
@@ -463,7 +514,7 @@ def buildtravel(locs, objs):
             travel[-1][-1] = "false" if travel[-1][-1] == "true" else "true"
         while rule:
             cond = newloc // 1000
-            nodwarves = (cond == 100)
+            nodwarves = cond == 100
             if cond == 0:
                 condtype = "cond_goto"
                 condarg1 = condarg2 = 0
@@ -486,7 +537,7 @@ def buildtravel(locs, objs):
             else:
                 condtype = "cond_not"
                 condarg1 = cond % 100
-                condarg2 = (cond - 300) // 100.
+                condarg2 = (cond - 300) // 100.0
             dest = newloc % 1000
             if dest <= 300:
                 desttype = "dest_goto"
@@ -497,19 +548,24 @@ def buildtravel(locs, objs):
             else:
                 desttype = "dest_special"
                 destval = locnames[dest - 300]
-            travel.append([len(tkey)-1,
-                           locnames[len(tkey)-1],
-                           rule.pop(0),
-                           condtype,
-                           condarg1,
-                           condarg2,
-                           desttype,
-                           destval,
-                           "true" if nodwarves else "false",
-                           "false"])
+            travel.append(
+                [
+                    len(tkey) - 1,
+                    locnames[len(tkey) - 1],
+                    rule.pop(0),
+                    condtype,
+                    condarg1,
+                    condarg2,
+                    desttype,
+                    destval,
+                    "true" if nodwarves else "false",
+                    "false",
+                ]
+            )
         travel[-1][-1] = "true"
     return (travel, tkey)
 
+
 def get_travel(travel):
     template = """    {{ // from {}: {}
         .motion = {},
@@ -525,11 +581,12 @@ def get_travel(travel):
     out = ""
     for entry in travel:
         out += template.format(*entry)
-    out = out[:-1] # trim trailing newline
+    out = out[:-1]  # trim trailing newline
     return out
 
+
 if __name__ == "__main__":
-    with open(YAML_NAME, "r", encoding='ascii', errors='surrogateescape') as f:
+    with open(YAML_NAME, "r", encoding="ascii", errors="surrogateescape") as f:
         db = yaml.safe_load(f)
 
     locnames = [x[0] for x in db["locations"]]
@@ -537,35 +594,38 @@ if __name__ == "__main__":
     objnames = [el[0] for el in db["objects"]]
     motionnames = [el[0] for el in db["motions"]]
 
-    (travel, tkey) = buildtravel(db["locations"],
-                                 db["objects"])
+    (travel, tkey) = buildtravel(db["locations"], db["objects"])
     ignore = ""
     try:
-        with open(H_TEMPLATE_PATH, "r", encoding='ascii', errors='surrogateescape') as htf:
+        with open(
+            H_TEMPLATE_PATH, "r", encoding="ascii", errors="surrogateescape"
+        ) as htf:
             # read in dungeon.h template
             h_template = DONOTEDIT_COMMENT + htf.read()
-        with open(C_TEMPLATE_PATH, "r", encoding='ascii', errors='surrogateescape') as ctf:
+        with open(
+            C_TEMPLATE_PATH, "r", encoding="ascii", errors="surrogateescape"
+        ) as ctf:
             # read in dungeon.c template
             c_template = DONOTEDIT_COMMENT + ctf.read()
     except IOError as e:
-        print('ERROR: reading template failed ({})'.format(e.strerror))
+        print("ERROR: reading template failed ({})".format(e.strerror))
         sys.exit(-1)
 
     c = c_template.format(
-        h_file             = H_NAME,
-        arbitrary_messages = get_arbitrary_messages(db["arbitrary_messages"]),
-        classes            = get_class_messages(db["classes"]),
-        turn_thresholds    = get_turn_thresholds(db["turn_thresholds"]),
-        locations          = get_locations(db["locations"]),
-        objects            = get_objects(db["objects"]),
-        obituaries         = get_obituaries(db["obituaries"]),
-        hints              = get_hints(db["hints"]),
-        conditions         = get_condbits(db["locations"]),
-        motions            = get_motions(db["motions"]),
-        actions            = get_actions(db["actions"]),
-        tkeys              = bigdump(tkey),
-        travel             = get_travel(travel),
-        ignore             = ignore
+        h_file=H_NAME,
+        arbitrary_messages=get_arbitrary_messages(db["arbitrary_messages"]),
+        classes=get_class_messages(db["classes"]),
+        turn_thresholds=get_turn_thresholds(db["turn_thresholds"]),
+        locations=get_locations(db["locations"]),
+        objects=get_objects(db["objects"]),
+        obituaries=get_obituaries(db["obituaries"]),
+        hints=get_hints(db["hints"]),
+        conditions=get_condbits(db["locations"]),
+        motions=get_motions(db["motions"]),
+        actions=get_actions(db["actions"]),
+        tkeys=bigdump(tkey),
+        travel=get_travel(travel),
+        ignore=ignore,
     )
 
     # 0-origin index of birds's last song.  Bird should
@@ -573,29 +633,29 @@ if __name__ == "__main__":
     deathbird = len(dict(db["objects"])["BIRD"]["sounds"]) - 1
 
     h = h_template.format(
-        num_locations      = len(db["locations"])-1,
-        num_objects        = len(db["objects"])-1,
-        num_hints          = len(db["hints"]),
-        num_classes        = len(db["classes"])-1,
-        num_deaths         = len(db["obituaries"]),
-        num_thresholds     = len(db["turn_thresholds"]),
-        num_motions        = len(db["motions"]),
-        num_actions        = len(db["actions"]),
-        num_travel         = len(travel),
-        num_keys           = len(tkey),
-        bird_endstate      = deathbird,
-        arbitrary_messages = get_refs(db["arbitrary_messages"]),
-        locations          = get_refs(db["locations"]),
-        objects            = get_refs(db["objects"]),
-        motions            = get_refs(db["motions"]),
-        actions            = get_refs(db["actions"]),
-        state_definitions  = statedefines
+        num_locations=len(db["locations"]) - 1,
+        num_objects=len(db["objects"]) - 1,
+        num_hints=len(db["hints"]),
+        num_classes=len(db["classes"]) - 1,
+        num_deaths=len(db["obituaries"]),
+        num_thresholds=len(db["turn_thresholds"]),
+        num_motions=len(db["motions"]),
+        num_actions=len(db["actions"]),
+        num_travel=len(travel),
+        num_keys=len(tkey),
+        bird_endstate=deathbird,
+        arbitrary_messages=get_refs(db["arbitrary_messages"]),
+        locations=get_refs(db["locations"]),
+        objects=get_refs(db["objects"]),
+        motions=get_refs(db["motions"]),
+        actions=get_refs(db["actions"]),
+        state_definitions=statedefines,
     )
 
-    with open(H_NAME, "w", encoding='ascii', errors='surrogateescape') as hf:
+    with open(H_NAME, "w", encoding="ascii", errors="surrogateescape") as hf:
         hf.write(h)
 
-    with open(C_NAME, "w", encoding='ascii', errors='surrogateescape') as cf:
+    with open(C_NAME, "w", encoding="ascii", errors="surrogateescape") as cf:
         cf.write(c)
 
 # end
index f33e2b564ca709b31f8284d4220f1977e3c24a1e..cd786edcd091d6f4dbfaf5116841630026e85f02 100755 (executable)
@@ -18,28 +18,41 @@ Make a DOT graph of Colossal Cave.
 
 import sys, getopt, yaml
 
+
 def allalike(loc):
     "Select out loci related to the Maze All Alike"
     return location_lookup[loc]["conditions"].get("ALLALIKE")
 
+
 def alldifferent(loc):
     "Select out loci related to the Maze All Alike"
     return location_lookup[loc]["conditions"].get("ALLDIFFERENT")
 
+
 def surface(loc):
     "Select out surface locations"
     return location_lookup[loc]["conditions"].get("ABOVE")
 
+
 def forest(loc):
     return location_lookup[loc]["conditions"].get("FOREST")
 
+
 def abbreviate(d):
-    m = {"NORTH":"N", "EAST":"E", "SOUTH":"S", "WEST":"W", "UPWAR":"U", "DOWN":"D"}
+    m = {
+        "NORTH": "N",
+        "EAST": "E",
+        "SOUTH": "S",
+        "WEST": "W",
+        "UPWAR": "U",
+        "DOWN": "D",
+    }
     return m.get(d, d)
 
+
 def roomlabel(loc):
     "Generate a room label from the description, if possible"
-    loc_descriptions = location_lookup[loc]['description']
+    loc_descriptions = location_lookup[loc]["description"]
     description = ""
     if debug:
         description = loc[4:]
@@ -51,8 +64,12 @@ def roomlabel(loc):
         if short.startswith("You're "):
             short = short[7:]
         if short.startswith("You are "):
-            short = short[8 :]
-        if short.startswith("in ") or short.startswith("at ") or short.startswith("on "):
+            short = short[8:]
+        if (
+            short.startswith("in ")
+            or short.startswith("at ")
+            or short.startswith("on ")
+        ):
             short = short[3:]
         if short.startswith("the "):
             short = short[4:]
@@ -69,6 +86,7 @@ def roomlabel(loc):
             description += "\\n(" + ",".join(startlocs[loc]).lower() + ")"
     return description
 
+
 # A forwarder is a location that you can't actually stop in - when you go there
 # it ships some message (which is the point) then shifts you to a next location.
 # A forwarder has a zero-length array of notion verbs in its travel section.
@@ -85,10 +103,12 @@ def roomlabel(loc):
 #      {verbs: [], action: [goto, LOC_NOWHERE]},
 #    ]
 
+
 def is_forwarder(loc):
     "Is a location a forwarder?"
-    travel = location_lookup[loc]['travel']
-    return len(travel) == 1 and len(travel[0]['verbs']) == 0
+    travel = location_lookup[loc]["travel"]
+    return len(travel) == 1 and len(travel[0]["verbs"]) == 0
+
 
 def forward(loc):
     "Chase a location through forwarding links."
@@ -96,6 +116,7 @@ def forward(loc):
         loc = location_lookup[loc]["travel"][0]["action"][1]
     return loc
 
+
 def reveal(objname):
     "Should this object be revealed when mapping?"
     if "OBJ_" in objname:
@@ -105,8 +126,9 @@ def reveal(objname):
     obj = object_lookup[objname]
     return not obj.get("immovable")
 
+
 if __name__ == "__main__":
-    with open("adventure.yaml", "r", encoding='ascii', errors='surrogateescape') as f:
+    with open("adventure.yaml", "r", encoding="ascii", errors="surrogateescape") as f:
         db = yaml.safe_load(f)
 
     location_lookup = dict(db["locations"])
@@ -121,17 +143,17 @@ if __name__ == "__main__":
     subset = allalike
     debug = False
     for (switch, val) in options:
-        if switch == '-a':
+        if switch == "-a":
             subset = lambda loc: True
-        elif switch == '-d':
+        elif switch == "-d":
             subset = alldifferent
-        elif switch == '-f':
+        elif switch == "-f":
             subset = forest
-        elif switch == '-m':
+        elif switch == "-m":
             subset = allalike
-        elif switch == '-s':
+        elif switch == "-s":
             subset = surface
-        elif switch == '-v':
+        elif switch == "-v":
             debug = True
         else:
             sys.stderr.write(__doc__)
@@ -170,7 +192,7 @@ if __name__ == "__main__":
     neighbors = set()
     for loc in nodes:
         for (f, t) in links:
-            if f == 'LOC_NOWHERE' or t == 'LOC_NOWHERE':
+            if f == "LOC_NOWHERE" or t == "LOC_NOWHERE":
                 continue
             if (f == loc and subset(t)) or (t == loc and subset(f)):
                 if loc not in neighbors:
@@ -189,7 +211,7 @@ if __name__ == "__main__":
     # Draw arcs
     for (f, t) in links:
         arc = "%s -> %s" % (f[4:], t[4:])
-        label=",".join(links[(f, t)]).lower()
+        label = ",".join(links[(f, t)]).lower()
         if len(label) > 0:
             arc += ' [label="%s"]' % label
         print("    " + arc)
index 2b4c5157bf67fcd2206d5a83ea5b1f809c33e71d..e4ca88e44386f867b99c4f35310bdc14bd2598b8 100755 (executable)
@@ -29,42 +29,45 @@ DEFAULT_HTML_OUTPUT_PATH = "../coverage/adventure.yaml.html"
 DANGLING_ACTIONS = ["ACT_VERSION"]
 DANGLING_MESSAGES = ["SAVERESUME_DISABLED"]
 
-STDOUT_REPORT_CATEGORY = "  {name:.<19}: {percent:5.1f}% covered ({covered} of {total})\n"
+STDOUT_REPORT_CATEGORY = (
+    "  {name:.<19}: {percent:5.1f}% covered ({covered} of {total})\n"
+)
 
-HTML_SUMMARY_ROW = '''
+HTML_SUMMARY_ROW = """
     <tr>
         <td class="headerItem"><a href="#{name}">{name}:</a></td>
         <td class="headerCovTableEntry">{total}</td>
         <td class="headerCovTableEntry">{covered}</td>
         <td class="headerCovTableEntry">{percent:.1f}%</td>
     </tr>
-'''
+"""
 
-HTML_CATEGORY_SECTION = '''
+HTML_CATEGORY_SECTION = """
     <tr id="{id}"></tr>
     {rows}
     <tr>
         <td>&nbsp;</td>
     </tr>
-'''
+"""
 
-HTML_CATEGORY_HEADER = '''
+HTML_CATEGORY_HEADER = """
     <tr>
         <td class="tableHead" width="60%" colspan="{colspan}">{label}</td>
         {cells}
     </tr>
-'''
+"""
 
 HTML_CATEGORY_HEADER_CELL = '<td class="tableHead" width="15%">{}</td>\n'
 
 HTML_CATEGORY_COVERAGE_CELL = '<td class="{}">&nbsp;</td>\n'
 
-HTML_CATEGORY_ROW = '''
+HTML_CATEGORY_ROW = """
     <tr>
         <td class="coverFile" colspan="{colspan}">{id}</td>
         {cells}
     </tr>
-'''
+"""
+
 
 def search(needle, haystack):
     # Search for needle in haystack, first escaping needle for regex, then
@@ -75,16 +78,19 @@ def search(needle, haystack):
         # if needle is empty, assume we're going to find an empty string
         return True
 
-    needle_san = re.escape(needle) \
-             .replace("\\n", "\n") \
-             .replace("\\t", "\t") \
-             .replace("%S", ".*") \
-             .replace("%s", ".*") \
-             .replace("%d", ".*") \
-             .replace("%V", ".*")
+    needle_san = (
+        re.escape(needle)
+        .replace("\\n", "\n")
+        .replace("\\t", "\t")
+        .replace("%S", ".*")
+        .replace("%s", ".*")
+        .replace("%d", ".*")
+        .replace("%V", ".*")
+    )
 
     return re.search(needle_san, haystack)
 
+
 def obj_coverage(objects, text, report):
     # objects have multiple descriptions based on state
     for _, objouter in enumerate(objects):
@@ -93,19 +99,20 @@ def obj_coverage(objects, text, report):
             for j, desc in enumerate(obj["descriptions"]):
                 name = "{}[{}]".format(obj_name, j)
                 if name not in report["messages"]:
-                    report["messages"][name] = {"covered" : False}
+                    report["messages"][name] = {"covered": False}
                     report["total"] += 1
                 if not report["messages"][name]["covered"] and search(desc, text):
                     report["messages"][name]["covered"] = True
                     report["covered"] += 1
 
+
 def loc_coverage(locations, text, report):
     # locations have a long and a short description, that each have to
     # be checked separately
     for name, loc in locations:
         desc = loc["description"]
         if name not in report["messages"]:
-            report["messages"][name] = {"long" : False, "short": False}
+            report["messages"][name] = {"long": False, "short": False}
             report["total"] += 2
         if not report["messages"][name]["long"] and search(desc["long"], text):
             report["messages"][name]["long"] = True
@@ -114,6 +121,7 @@ def loc_coverage(locations, text, report):
             report["messages"][name]["short"] = True
             report["covered"] += 1
 
+
 def hint_coverage(obituaries, text, report):
     # hints have a "question" where the hint is offered, followed
     # by the actual hint if the player requests it
@@ -121,7 +129,7 @@ def hint_coverage(obituaries, text, report):
         hint = hintouter["hint"]
         name = hint["name"]
         if name not in report["messages"]:
-            report["messages"][name] = {"question" : False, "hint": False}
+            report["messages"][name] = {"question": False, "hint": False}
             report["total"] += 2
         if not report["messages"][name]["question"] and search(hint["question"], text):
             report["messages"][name]["question"] = True
@@ -130,50 +138,61 @@ def hint_coverage(obituaries, text, report):
             report["messages"][name]["hint"] = True
             report["covered"] += 1
 
+
 def obit_coverage(obituaries, text, report):
     # obituaries have a "query" where it asks the player for a resurrection,
     # followed by a snarky comment if the player says yes
     for name, obit in enumerate(obituaries):
         if name not in report["messages"]:
-            report["messages"][name] = {"query" : False, "yes_response": False}
+            report["messages"][name] = {"query": False, "yes_response": False}
             report["total"] += 2
         if not report["messages"][name]["query"] and search(obit["query"], text):
             report["messages"][name]["query"] = True
             report["covered"] += 1
-        if not report["messages"][name]["yes_response"] and search(obit["yes_response"], text):
+        if not report["messages"][name]["yes_response"] and search(
+            obit["yes_response"], text
+        ):
             report["messages"][name]["yes_response"] = True
             report["covered"] += 1
 
+
 def threshold_coverage(classes, text, report):
     # works for class thresholds and turn threshold, which have a "message"
     # property
     for name, item in enumerate(classes):
         if name not in report["messages"]:
-            report["messages"][name] = {"covered" : False}
+            report["messages"][name] = {"covered": False}
             report["total"] += 1
         if not report["messages"][name]["covered"] and search(item["message"], text):
             report["messages"][name]["covered"] = True
             report["covered"] += 1
 
+
 def arb_coverage(arb_msgs, text, report):
     for name, message in arb_msgs:
         if name not in report["messages"]:
-            report["messages"][name] = {"covered" : False}
+            report["messages"][name] = {"covered": False}
             report["total"] += 1
-        if not report["messages"][name]["covered"] and (search(message, text) or name in DANGLING_MESSAGES):
+        if not report["messages"][name]["covered"] and (
+            search(message, text) or name in DANGLING_MESSAGES
+        ):
             report["messages"][name]["covered"] = True
             report["covered"] += 1
 
+
 def actions_coverage(items, text, report):
     # works for actions
     for name, item in items:
         if name not in report["messages"]:
-            report["messages"][name] = {"covered" : False}
+            report["messages"][name] = {"covered": False}
             report["total"] += 1
-        if not report["messages"][name]["covered"] and (search(item["message"], text) or name in DANGLING_ACTIONS):
+        if not report["messages"][name]["covered"] and (
+            search(item["message"], text) or name in DANGLING_ACTIONS
+        ):
             report["messages"][name]["covered"] = True
             report["covered"] += 1
 
+
 def coverage_report(db, check_file_contents):
     # Create report for each category, including total items,  number of items
     # covered, and a list of the covered messages
@@ -181,10 +200,10 @@ def coverage_report(db, check_file_contents):
     for name in db.keys():
         # initialize each catagory
         report[name] = {
-            "name" : name, # convenience for string formatting
-            "total" : 0,
-            "covered" : 0,
-            "messages" : {}
+            "name": name,  # convenience for string formatting
+            "total": 0,
+            "covered": 0,
+            "messages": {},
         }
 
     # search for each message in every test check file
@@ -200,20 +219,21 @@ def coverage_report(db, check_file_contents):
 
     return report
 
+
 if __name__ == "__main__":
     # load DB
     try:
-        with open(YAML_PATH, "r", encoding='ascii', errors='surrogateescape') as f:
+        with open(YAML_PATH, "r", encoding="ascii", errors="surrogateescape") as f:
             db = yaml.safe_load(f)
     except IOError as e:
-        print('ERROR: could not load %s (%s)' % (YAML_PATH, e.strerror))
+        print("ERROR: could not load %s (%s)" % (YAML_PATH, e.strerror))
         sys.exit(-1)
 
     # get contents of all the check files
     check_file_contents = []
     for filename in os.listdir(TEST_DIR):
         if filename.endswith(".chk"):
-            with open(filename, "r", encoding='ascii', errors='surrogateescape') as f:
+            with open(filename, "r", encoding="ascii", errors="surrogateescape") as f:
                 check_file_contents.append(f.read())
 
     # run coverage analysis report on dungeon database
@@ -236,14 +256,20 @@ if __name__ == "__main__":
             colspan = 10 - len(cat_keys)
             for key in cat_keys:
                 headers_html += HTML_CATEGORY_HEADER_CELL.format(key)
-            category_html = HTML_CATEGORY_HEADER.format(colspan=colspan, label=category["name"], cells=headers_html)
+            category_html = HTML_CATEGORY_HEADER.format(
+                colspan=colspan, label=category["name"], cells=headers_html
+            )
 
             # render message coverage row
             for message_id, covered in cat_messages:
                 category_html_row = ""
                 for key, value in covered.items():
-                    category_html_row += HTML_CATEGORY_COVERAGE_CELL.format("uncovered" if not value else "covered")
-                category_html += HTML_CATEGORY_ROW.format(id=message_id,colspan=colspan, cells=category_html_row)
+                    category_html_row += HTML_CATEGORY_COVERAGE_CELL.format(
+                        "uncovered" if not value else "covered"
+                    )
+                category_html += HTML_CATEGORY_ROW.format(
+                    id=message_id, colspan=colspan, cells=category_html_row
+                )
             categories_html += HTML_CATEGORY_SECTION.format(id=name, rows=category_html)
 
             # render category summaries
@@ -260,16 +286,22 @@ if __name__ == "__main__":
 
     # render HTML report
     try:
-        with open(HTML_TEMPLATE_PATH, "r", encoding='ascii', errors='surrogateescape') as f:
+        with open(
+            HTML_TEMPLATE_PATH, "r", encoding="ascii", errors="surrogateescape"
+        ) as f:
             # read in HTML template
             html_template = f.read()
     except IOError as e:
-        print('ERROR: reading HTML report template failed ({})'.format(e.strerror))
+        print("ERROR: reading HTML report template failed ({})".format(e.strerror))
         sys.exit(-1)
 
     # parse template with report and write it out
     try:
-        with open(html_output_path, "w", encoding='ascii', errors='surrogateescape') as f:
-            f.write(html_template.format(categories=categories_html, summary=summary_html))
+        with open(
+            html_output_path, "w", encoding="ascii", errors="surrogateescape"
+        ) as f:
+            f.write(
+                html_template.format(categories=categories_html, summary=summary_html)
+            )
     except IOError as e:
-        print('ERROR: writing HTML report failed ({})'.format(e.strerror))
+        print("ERROR: writing HTML report failed ({})".format(e.strerror))