Improve test coverage.
[open-adventure.git] / newdungeon.py
1 #!/usr/bin/python3
2
3 # This is the new open-adventure dungeon generator. It'll eventually replace the existing dungeon.c It currently outputs a .h and .c pair for C code.
4
5 import yaml
6 import sys
7
8 yaml_name = "adventure.yaml"
9 h_name = "newdb.h"
10 c_name = "newdb.c"
11
12 def c_escape(string):
13     """Add C escape sequences to a string."""
14     string = string.replace("\n", "\\n")
15     string = string.replace("\t", "\\t")
16     string = string.replace('"', '\\"')
17     string = string.replace("'", "\\'")
18     return string
19
20 def quotewrap(string):
21     """Wrap a string in double quotes."""
22     return '"' + string + '"'
23
24 def write_regular_messages(name, h, c):
25
26     h += "enum {}_refs {{\n".format(name)
27     for key, text in dungeon[name]:
28         h += "  {},\n".format(key)
29     h += "};\n\n"
30     
31     c += "const char* {}[] = {{\n".format(name)   
32     index = 0
33     for key, text in dungeon[name]:
34         if text == None:
35             c += "  NULL,\n"
36         else:
37             text = c_escape(text)
38             c += "  \"{}\",\n".format(text)
39         index += 1
40     c += "};\n\n"
41     
42     return (h, c)
43
44 with open(yaml_name, "r") as f:
45     dungeon = yaml.load(f)
46
47 h = """#include <stdio.h>
48
49 typedef struct {
50   const char* inventory;
51   const char** longs;
52 } object_description_t;
53
54 typedef struct {
55   const char* small;
56   const char* big;
57 } descriptions_t;
58
59 typedef struct {
60   descriptions_t description;
61 } location_t;
62
63 typedef struct {
64   const char* query;
65   const char* yes_response;
66 } obituary_t;
67
68 extern location_t locations[];
69 extern object_description_t object_descriptions[];
70 extern const char* arbitrary_messages[];
71 extern const char* class_messages[];
72 extern const char* turn_threshold_messages[];
73 extern obituary_t obituaries[];
74
75 extern size_t CLSSES;
76 extern int maximum_deaths;
77 """
78
79 c = """#include "{}"
80
81 """.format(h_name)
82
83 for name in [
84         "arbitrary_messages",
85         "class_messages",
86         "turn_threshold_messages",
87 ]:
88     h, c = write_regular_messages(name, h, c)
89
90 h += "enum locations_refs {\n"
91 c += "location_t locations[] = {\n"
92 for key, data in dungeon["locations"]:
93     h += "  {},\n".format(key)
94
95     try:
96         short = quotewrap(c_escape(data["description"]["short"]))
97     except AttributeError:
98         short = "NULL"
99     try:
100         long = quotewrap(c_escape(data["description"]["long"]))
101     except AttributeError:
102         long = "NULL"
103
104     c += """  {{
105     .description = {{
106       .small = {},
107       .big = {},
108     }},
109   }},
110 """.format(short, long)
111
112 c += "};\n\n"
113 h += "};\n\n"
114
115 h += "enum object_descriptions_refs {\n"
116 c += "object_description_t object_descriptions[] = {\n"
117 for key, data in dungeon["object_descriptions"]:
118     try:
119         data["inventory"] = "\"{}\"".format(c_escape(data["inventory"]))
120     except AttributeError:
121         data["inventory"] = "NULL"
122     h += "  {},\n".format(key)
123     c += "  {\n"
124     c += "    .inventory = {},\n".format(data["inventory"])
125     try:
126         data["longs"][0]
127         c += "    .longs = (const char* []) {\n"
128         for l in data["longs"]:
129             l = c_escape(l)
130             c += "      \"{}\",\n".format(l)
131         c += "    },\n"
132     except (TypeError, IndexError):
133         c += "    .longs = NULL,\n"
134     c += "  },\n"
135 h += "};\n\n"
136 c += "};\n\n"
137
138 c += "obituary_t obituaries[] = {\n"
139 for obit in dungeon["obituaries"]:
140
141     query = quotewrap(c_escape(obit["query"]))
142     yes_response = quotewrap(c_escape(obit["yes_response"]))
143
144     c += """  {{
145     .query = {},
146     .yes_response = {},
147   }},
148 """.format(query, yes_response)
149
150 c += "};\n"
151
152 c += """
153 size_t CLSSES = {};
154 """.format(len(dungeon["class_messages"]))
155
156 c += """
157 int maximum_deaths = {};
158 """.format(len(dungeon["obituaries"]))
159
160
161 # finally, write out the files
162 d = {
163     h_name: h,
164     c_name: c,
165 }
166 for filename, string in d.items():
167     with open(filename, "w") as f:
168         f.write(string)
169