Generate initializer from YAML for Section 9 bits.
[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
7 yaml_name = "adventure.yaml"
8 h_name = "newdb.h"
9 c_name = "newdb.c"
10
11 h_template = """/* Generated from adventure.yaml - do not hand-hack! */
12 #ifndef NEWDB_H
13 #define NEWDB_H
14
15 #include <stdio.h>
16
17 typedef struct {{
18   const char* inventory;
19   const char** longs;
20 }} object_description_t;
21
22 typedef struct {{
23   const char* small;
24   const char* big;
25 }} descriptions_t;
26
27 typedef struct {{
28   descriptions_t description;
29 }} location_t;
30
31 typedef struct {{
32   const char* query;
33   const char* yes_response;
34 }} obituary_t;
35
36 typedef struct {{
37   const int threshold;
38   const int point_loss;
39   const char* message;
40 }} turn_threshold_t;
41
42 typedef struct {{
43   const int threshold;
44   const char* message;
45 }} class_t;
46
47 typedef struct {{
48   const int number;
49   const int turns;
50   const int penalty;
51   const char* question;
52   const char* hint;
53 }} hint_t;
54
55 extern location_t locations[];
56 extern object_description_t object_descriptions[];
57 extern const char* arbitrary_messages[];
58 extern const class_t classes[];
59 extern turn_threshold_t turn_thresholds[];
60 extern obituary_t obituaries[];
61 extern hint_t hints[];
62 extern const long conditions[];
63
64 extern const size_t CLSSES;
65 extern const int maximum_deaths;
66 extern const int turn_threshold_count;
67 #define HINT_COUNT {}
68
69 enum arbitrary_messages_refs {{
70 {}
71 }};
72
73 enum locations_refs {{
74 {}
75 }};
76
77 enum object_descriptions_refs {{
78 {}
79 }};
80
81 #endif /* end NEWDB_H */
82 """
83
84 c_template = """/* Generated from adventure.yaml - do not hand-hack! */
85
86 #include "common.h"
87 #include "{}"
88
89 const char* arbitrary_messages[] = {{
90 {}
91 }};
92
93 const class_t classes[] = {{
94 {}
95 }};
96
97 turn_threshold_t turn_thresholds[] = {{
98 {}
99 }};
100
101 location_t locations[] = {{
102 {}
103 }};
104
105 object_description_t object_descriptions[] = {{
106 {}
107 }};
108
109 obituary_t obituaries[] = {{
110 {}
111 }};
112
113 hint_t hints[] = {{
114 {}
115 }};
116
117 const long conditions[] = {{
118 {}
119 }};
120
121 const size_t CLSSES = {};
122 const int maximum_deaths = {};
123 const int turn_threshold_count = {};
124
125 /* end */
126 """
127
128 def make_c_string(string):
129     """Render a Python string into C string literal format."""
130     if string == None:
131         return "NULL"
132     string = string.replace("\n", "\\n")
133     string = string.replace("\t", "\\t")
134     string = string.replace('"', '\\"')
135     string = string.replace("'", "\\'")
136     string = '"' + string + '"'
137     return string
138
139 def get_refs(l):
140     reflist = [x[0] for x in l]
141     ref_str = ""
142     for ref in reflist:
143         ref_str += "    {},\n".format(ref)
144     ref_str = ref_str[:-1] # trim trailing newline
145     return ref_str
146
147 def get_arbitrary_messages(arb):
148     template = """    {},
149 """
150     arb_str = ""
151     for item in arb:
152         arb_str += template.format(make_c_string(item[1]))
153     arb_str = arb_str[:-1] # trim trailing newline
154     return arb_str
155
156 def get_class_messages(cls):
157     template = """    {{
158         .threshold = {},
159         .message = {},
160     }},
161 """
162     cls_str = ""
163     for item in cls:
164         threshold = item["threshold"]
165         message = make_c_string(item["message"])
166         cls_str += template.format(threshold, message)
167     cls_str = cls_str[:-1] # trim trailing newline
168     return cls_str
169
170 def get_turn_thresholds(trn):
171     template = """    {{
172         .threshold = {},
173         .point_loss = {},
174         .message = {},
175     }},
176 """
177     trn_str = ""
178     for item in trn:
179         threshold = item["threshold"]
180         point_loss = item["point_loss"]
181         message = make_c_string(item["message"])
182         trn_str += template.format(threshold, point_loss, message)
183     trn_str = trn_str[:-1] # trim trailing newline
184     return trn_str
185
186 def get_locations(loc):
187     template = """    {{
188         .description = {{
189             .small = {},
190             .big = {},
191         }},
192     }},
193 """
194     loc_str = ""
195     for item in loc:
196         short_d = make_c_string(item[1]["description"]["short"])
197         long_d = make_c_string(item[1]["description"]["long"])
198         loc_str += template.format(short_d, long_d)
199     loc_str = loc_str[:-1] # trim trailing newline
200     return loc_str
201
202 def get_object_descriptions(obj):
203     template = """    {{
204         .inventory = {},
205         .longs = (const char* []) {{
206 {}
207         }},
208     }},
209 """
210     obj_str = ""
211     for item in obj:
212         i_msg = make_c_string(item[1]["inventory"])
213         longs_str = ""
214         if item[1]["longs"] == None:
215             longs_str = " " * 12 + "NULL,"
216         else:
217             for l_msg in item[1]["longs"]:
218                 longs_str += " " * 12 + make_c_string(l_msg) + ",\n"
219             longs_str = longs_str[:-1] # trim trailing newline
220         obj_str += template.format(i_msg, longs_str)
221     obj_str = obj_str[:-1] # trim trailing newline
222     return obj_str
223
224 def get_obituaries(obit):
225     template = """    {{
226         .query = {},
227         .yes_response = {},
228     }},
229 """
230     obit_str = ""
231     for o in obit:
232         query = make_c_string(o["query"])
233         yes = make_c_string(o["yes_response"])
234         obit_str += template.format(query, yes)
235     obit_str = obit_str[:-1] # trim trailing newline
236     return obit_str
237
238 def get_hints(hnt, arb):
239     template = """    {{
240         .number = {},
241         .penalty = {},
242         .turns = {},
243         .question = {},
244         .hint = {},
245     }},
246 """
247     hnt_str = ""
248     md = dict(arb)
249     for member in hnt:
250         item = member["hint"]
251         number = item["number"]
252         penalty = item["penalty"]
253         turns = item["turns"]
254         question = make_c_string(md[item["question"]])
255         hint = make_c_string(md[item["hint"]])
256         hnt_str += template.format(number, penalty, turns, question, hint)
257     hnt_str = hnt_str[:-1] # trim trailing newline
258     return hnt_str
259
260 def get_condbits(locations):
261     cnd_str = ""
262     for (name, loc) in locations:
263         conditions = loc["conditions"]
264         hints = loc.get("hints") or []
265         flaglist = []
266         for flag in conditions:
267             if conditions[flag]:
268                 flaglist.append(flag)
269         line = "|".join([("(1<<COND_%s)" % f) for f in flaglist])
270         trail = "|".join([("(1<<COND_H%s)" % f['name']) for f in hints])
271         if trail:
272             line += "|" + trail
273         if line.startswith("|"):
274             line = line[1:]
275         if not line:
276             line = "0"
277         cnd_str += "    " + line + ",\t// " + name + "\n"
278     return cnd_str
279
280 if __name__ == "__main__":
281     with open(yaml_name, "r") as f:
282         db = yaml.load(f)
283
284     h = h_template.format(
285         len(db["hints"]),
286         get_refs(db["arbitrary_messages"]),
287         get_refs(db["locations"]),
288         get_refs(db["object_descriptions"]),
289     )
290
291     c = c_template.format(
292         h_name,
293         get_arbitrary_messages(db["arbitrary_messages"]),
294         get_class_messages(db["classes"]),
295         get_turn_thresholds(db["turn_thresholds"]),
296         get_locations(db["locations"]),
297         get_object_descriptions(db["object_descriptions"]),
298         get_obituaries(db["obituaries"]),
299         get_hints(db["hints"], db["arbitrary_messages"]),
300         get_condbits(db["locations"]),
301         len(db["classes"]),
302         len(db["obituaries"]),
303         len(db["turn_thresholds"]),
304     )
305
306     with open(h_name, "w") as hf:
307         hf.write(h)
308
309     with open(c_name, "w") as cf:
310         cf.write(c)
311
312 # end