# Evaluating RPG-formatted dice rolls with repeats in Clojure

My target was to write a function that takes a String as input, interprets it as a series of dice rolls, evaluates it and returns a series of integers.

The input is in a form that is typical in table top RPGs, slightly extended:

• 1d20; d20; 20 - Roll a single twenty sided die, result would be e.g. 12
• 2d6 + 4 - Roll two six sided dice and add 4 to the sum, result would be e.g. 12 (5 [d6] + 3 [d6] + 4)
• d20, d20; 20, (note the comma) - Roll two twenty sided dice and display the results separately, result would be e.g. 6 // 14

I handled the parsing with instaparse. The function I am interested in feedback for, which you'll find below, is the one that then transforms a single "roll".

According to my specifications, I can add any number of commas (,) to a roll (a series of dice and constant modifiers). I opted to have those additional trailing commas parsed as being part of the preceding roll, syntactically, being added as :repeat flags to the end of the syntax tree. Because these rolls can be repeated, I had to pass functions instead of values, so the repeated rolls would actually yield different results. That means example arguments to this function can look like this:

[#(d 4 12) #(+ 5) :repeat :repeat] - which would correspond to "4d12 + 5,,", where d is defined as follows:

(defn d
([count faces]
(reduce + (map d (repeat count faces))))
([faces] (inc (rand-int faces))))


(defn roll [& dice-info]
(let [until-repeat (complement {:repeat true})
[dice repeat] (split-with until-repeat dice-info)
eval #(%)
eval-dice #(map eval dice)
eval-roll (fn [_] (apply + (eval-dice)))]
(map eval-roll (cons :repeat repeat))))


I am particularly interested in how idiomatic this is, and whether the approach I've chosen is reasonable, but of course any input is welcome.

Here is the grammar text:

Series = Roll ( <','> Roll )*

Roll = <S?> ( QuickDie | ( Die ( <S?> Modifier)* ) ) <S?> Repeat*
Repeat = <',' S?>

Die = Integer? <'d'> Integer
QuickDie = Integer
Integer = #"([1-9][0-9]*|0)"

<Modifier> = ( Plus | Minus )
Plus = <'+'> <S?> Addend
Minus = <'-'> <S?> Addend
<Addend> = ( Integer | Die )

S = #"\s+"


And for completeness' sake, the whole code: DiceRoll.clj and DiceRoll.eval.clj.

• Can we see your Instaparse grammar text, please? – Thumbnail Apr 28 '17 at 21:50
• @Thumbnail sure, added it. – ammut Apr 29 '17 at 21:06