Programs that handle role playing game dice rolls are often unsophisticated. They usually only handle the form XdY+Z where X, Y, and Z are integers. In this article I will lead up to a calculator that rolls dice inside simple mathematic expressions such as 2d(1d6*2)/3+1. Being able to do this is often useful for things like the weakened status in D&D 4e, where all attacks do half damage.

It’s a bit difficult to jump straight to a grammar with operators like + or * that have the arguments to the operation on the left and right, so I’ll start with an s-expression version of the calculator. An s-expressions version of the above dice expression is

(+ (/ (d 2 (* (d 1 6) 2)) 3) 1) .

Here are a few more examples: (+ 1 1) , (d 1 20) , (+ 10 (* 10 0)) , 6 .

As you can see, the expression is a set of composed functions of the form (operator value value) where value is either a number or another function. So here’s what we can say about our language:

  1. An expression is either a number or of the form (operator expression expression ... expression)
  2. An operator is +, -, *, /, or d
  3. A number is one or more consecutive digits.

This defines the language we’ve created. (+ 19 (* 2 4)) clearly fits the rules. Starting with the rule for expression, we can keep applying the rules until we’ve transformed it until our mathematical expression emerges:

  • (operator expression expression)
  • (+ number (operator expression expression))
  • (+ 19 (* number number))
  • (+ 19 (* 2 4))

As a counter-example, (+ (1+2) 20) doesn’t fit:

  • (operator expression expression)
  • (+ expression number)
  • (+ expression 20)
  • (+ (operator expression expression) 20)
  • (+ (operator number number) 20)
  • (+ (operator number 2) 20)

The expansion can’t go further because operator can’t turn into 2 due to our second rule, and number cannot turn into + because of our third rule.

To evaluate expressions in this form, just apply the operator to the two argument numbers. If one of the arguments is a function instead of a number, evaluate that function and use the number result. Here’s a step by step example of an expression being evaluated:

  • (+ (/ (d 2 (* (d 1 6) 2)) 3) 1)
  • Here I rolled a six-sided die and got 5
  • (+ (/ (d 2 (* 5 2)) 3) 1)
  • (+ (/ (d 2 10) 3) 1)
  • Here I rolled two 10-sided dice and got a total of 12
  • (+ (/ 12 3) 1)
  • (+ 4 1)
  • 5

In my next blog post, we’ll build the parser in python.