comparison orpg/dieroller/dieroller.txt @ 0:4385a7d0efd1 grumpy-goblin

Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author sirebral
date Tue, 14 Jul 2009 16:41:58 -0500
parents
children bf799efe7a8a
comparison
equal deleted inserted replaced
-1:000000000000 0:4385a7d0efd1
1 The New Dicing System: A Proposal for OpenRPG
2 ----------------------------------------------
3
4 The current dice system for OpenRPG has several limitations. Foremost
5 among these are the fact that adding a new, non-standard dicing mechanism
6 requires editing of the basic dice code. There are several secondary
7 limitations, such as the fact that while the dice system can handle math,
8 it cannot be used as a calculator -- it will not allow expressions that do
9 not involve dice.
10
11 This proposal is for a new dicing system to replace the current one in
12 OpenRPG. Since the dicing system is something that users will interact
13 with frequently, a new system needs to be considered carefully. This
14 document attempts to describe the new dicing system so that such
15 consideration can be given to it. It is expected that this document will
16 grow and change as it is scrutinized.
17
18
19 Design goals for this dicing system:
20
21 1. Should be easy for new users to get started with, based on knowing
22 standard RPG dice notation (NdX) and basic math.
23
24 2. Should, as far as practical, maintain compatibility with existing
25 character sheets, etc., that use the current dice system.
26
27 3. Should allow users to create new dice types and new ways of
28 counting dice. Ideally, this should not require programming, except
29 in exceptional cases.
30
31 4. The dice system should be usable for doing basic math that does
32 not involve dice.
33
34 5. The dice system should be able to handle most current RPG dicing
35 systems.
36
37 Things this dicing system is designed to NOT do:
38
39 1. Be a programming language. There are no facilities in it for
40 user input, output formatting, loops, if-then-else, or similar
41 things. If these are desired for something involving dice, an
42 appropriate node and nodehandler can be created.
43
44 2. Handle all theoretically possible dicing systems without the
45 need for programming plugins. First off, this is impossible.
46 Second, even making an attempt to would require supporting
47 dicing methods that don't actually turn up in any real game.
48
49 3. Handle floating-point math. I don't know of any systems that
50 use it in their dice schemes right now. If there are some,
51 we might have to consider adding it.
52
53
54 Syntax Specification
55
56 What follows is a BNF specification for the proposed dicing system, with
57 explanatory text interleaved. At the end of this document is a copy of
58 the BNF with no explanations, for those who would like to look at it "all
59 together". Note that BNF describes only syntax, and not semantics; thus,
60 while anything generated with this grammar should be syntactically correct,
61 that doesn't mean it will make sense or be allowed.
62
63
64 dice string ::= <expression>
65 <expression> of <comparison> | <comparison>
66
67 This is the top level. The major thing of note here is that comparisons
68 only occur at this level. This is intentional; the result of a comparison
69 is a boolean true/false flag rather than a number. Thus, it makes no
70 sense to allow people to perform further numerical operations on the
71 result of a comparison. Systems where dice are triggered by the results
72 of other dice are left for the realm of plugins.
73
74
75 comparison ::= <expression> <relation> <expression>
76
77 expression ::= <factor> | <factor> <low-op> <factor>
78
79 The separation into "low-op" and "high-op" of the operators is to allow
80 order of operations to be handled more easily. Syntactically, it's not
81 really necessary, but it should be helpful in implementation.
82
83
84 factor :: = <term> | <term> <high-op> <term> |
85 <multi-dice> | <multi-dice> <high-op> <term> |
86 <term> <high-op> <multi-dice>
87
88 Here we start to hit some complication. The intent of the different
89 entries for multi-dice is that we don't want to allow things like
90 [3d6 each * 2d6 each]. We are *not* doing vector multiplication!
91 The "expression" level doesn't have any such limitation on syntax;
92 things like [3d6 each + 2d6 each] we'll have to either think of a
93 logical way to handle, or disallow on a semantic level. (Well... I
94 suppose it could be handled in the BNF, but I think it would get
95 kind of messy.)
96
97
98 term ::= <dice> | <unit>
99
100 unit ::= <number> | ( <expression> )
101
102 Dice are not considered a unit. This means that things like [3d6d10] can't
103 be done without using parentheses. I consider that to be a win for
104 clarity.
105
106
107 dice ::= <unit>d<unit> | <unit>d<name> | <dice> <flag> | lastroll
108
109 The <name> entry here allows for user-created dice (in the syntax, at
110 least...).
111
112
113 multi-dice ::= ( <dice>, <dice>+ ) | # (1d6,1d8)
114 <dice> each | # 3d6 each
115 ( <expression> of <expression> ) | # (3 of 2d6)
116 lastroll | # lastroll
117 <multi-dice> <flag> # (3 of 2d6) best 2
118
119 "lastroll" by itself can be either dice or multi-dice. I'm thinking that it
120
121 should be whatever type the last roll was.
122
123
124 flag ::= reroll <condition> | # repeats
125 reroll <slice> | # once only
126 grow <condition> | # reroll and add
127 shrink <condition> | # reroll and subtract
128 drop <condition> |
129 drop <slice> |
130 take <condition> |
131 take <slice> |
132 <slice> | # implied "take"
133 <name> <condition> | # user-created
134 <name> # user-created
135
136 Technically, we don't need both "drop" and "take" -- one implies the
137 other. However, having both should make the language easier to use.
138
139 "reroll" will work differently depending on whether a condition or a
140 slice is given. If a condition is given, it will reroll until none of
141 the dice in the set meet the reroll condition (or until it hits a maximum
142 allowed number of rerolls). If a slice is given, it will reroll those
143 dice once. IMHO, this behavior makes the most sense.
144
145 The <name> entries here are to allow for user-created flags. Note that
146 as I've specified things right now, a user-created flag can have a
147 condition,
148 but not a slice. That's mostly because I couldn't think of a case where
149 a slice would be useful... should we add it anyways?
150
151
152 slice ::= highest | lowest | highest <number> | lowest <number>
153
154 "highest" and "lowest" without a number are equivalent to doing them with
155 1 as the number. This is to simplify things like [4d6 drop lowest].
156
157
158 condition ::= <relation> <unit>
159
160 This is for conditions on flags. Note that it can take a unit, so you could
161
162 use dice in a condition; however, I think the unit should only be evaluated
163
164 once, to make things faster. Anyone for repetitive evaluation?
165
166
167 low-op ::= + | - | min | max
168
169 "min" takes two values and returns the highest of them, and "max"
170 returns the lowest of them. This might seem counterintuitive, but
171 it's meant to be used with dice, like so:
172
173 3d6 min 8 - always returns 8 or higher
174
175 3d6 max 15 - always returns 15 or lower
176
177 I decided to put min and max as having the same precedence as + and -,
178 because if they had higher precedence, then:
179
180 3d6+2 min 10
181
182 would be equivalent to 3d6+10. (It would take the max of 2 and 10, then
183 add that to 3d6). One problem that does arise here is with multiplication
184 and division: [1d6 min 5 * 2] will be equivalent to [1d6 min 10], since
185 multiplication has higher precedence. We may just want to warn people
186 that min and max can be screwy unless you parenthesize, unless someone can
187 think of a better way to handle them.
188
189
190 high-op ::= * | / | mod
191
192 The / is integer division, of course, since we're doing integer math.
193
194
195 number ::= <digit>+ | -<digit>+
196
197 Positive and negative numbers are allowed. This means that, syntactically,
198 [-2d-4] is legal. Do we want to modify the BNF to disallow this, or handle
199 it on a semantic level?
200
201
202 name ::= <letter>[<letter>|<digit>]*
203
204 We may want to expand to allow underscores and dashes in user-created names.
205
206
207
208 letter ::= A-Z | a-z
209
210 digit ::= 0-9
211
212 relation ::= < | > | <= | >= | => | =< | = | ==
213
214
215
216 Well, that's the BNF. Again, at the end is a copy without all the running
217 commentary.
218
219
220 Thoughts on Implementation:
221
222 First, I think a sort of "dice library" of common functions needs to be
223 created. This would include rolling a set of dice, getting the highest
224 of a group of dice, growing and shrinking dice from a set based on
225 conditions, and so on. These functions should be available for use by
226 custom-written dice types.
227
228 Next, that library should be used as a tool in implementing a dice-string
229 interpreter. That will require creating a parser for the dice-string
230 'language'. This could be either a custom-written parser, or possibly
231 one created with some of the Python parser generators. A custom-written
232 parser may take longer to do and be a bit more finicky to maintain, but
233 it would remove a dependency from the code.
234
235 User-created flags and dice types could be supported in two ways:
236
237 - First, by allowing users to specify strings in the dice language
238 that the flags/expressions would expand to -- basically, allowing
239 dice macros.
240
241 - Second, by adding hooks for python modules to be associated with
242 user-created dice or flag types. This is likely to be the more
243 complicated of the two solutions, but it would also be more
244 flexible.
245
246 Personally, I think both are desirable -- the first, so that
247 non-programming users can create simple die and flag types. The
248 second, because by design, there are some things this dice system
249 just won't do.
250
251
252 Further work needed:
253
254 - specs for the "dice library"
255
256 - specs on an interface for python modules meant to be dice and
257 flag types.
258
259 ----------------------------------------------------------------
260
261 start ::= <expression> |
262 <comparison>
263
264 comparison ::= <expression> <condition>
265
266 expression ::= <term> | <term> <low-op> <factor>
267
268 term ::= <factor> | <factor> <high-op> <unit>
269
270 factor ::= <atom> | <dice_set>
271
272 atom ::= <number> | ( <expression> )
273
274 dice_set ::= <dice> <dice_set>, <dice> | <expr> of <dice> | <dice> each |
275 lastroll
276
277 dice ::= <atom>d<atom> | <atom>d<name> | <dice> <flag>
278
279 flag ::= reroll <condition> |
280 reroll <slice> |
281 grow <condition> |
282 shrink <condition> |
283 drop <condition> |
284 drop <slice> |
285 take <condition> |
286 take <slice> |
287 <slice> |
288 <name> <condition> |
289 <name> <slice> |
290 <name>
291
292 slice ::= highest | lowest | highest <number> | lowest <number>
293
294 condition ::= <relation> <unit>
295
296 low-op ::= + | -
297
298 high-op ::= * | / | mod| max | min
299
300 number ::= <digit>+ | -<digit>+
301
302 name ::= <letter>[<letter>|<digit>]*
303
304 letter ::= A-Z | a-z
305
306 digit ::= 0-9
307
308 relation ::= < | > | <= | >= | => | =< | = | ==
309