# Dice parser

I got the urge to write a parser for table top gaming. For example `4d6+1` meaning roll 4 6 sided dices, sum it all up and add 1 to the result. A pretty simple task, that could probably be done by just looking at the string and doing some splits or substrings. But I want to do it with nearley, because that sound like fun to me.

# Basics

Lets begin with the most basic example of `4d6`. It has 3 components, a number followed by a letter `d` followed by another number. So lets create a parser for it with nearley

``# this is our starting ruleMain -> int "d" int# We are only interested in integers. floats does not make sense hereint -> [0-9]:+        {% (d) => parseInt(d.flat().join("")) %}``

Note that the `int` rule produces an array of strings, so with the postprocessing function we can make sure that it outputs an integer.

So, parsing `4d6` will output `[4, 'd', 6]`.

Next up, lets add a parser for the optional `+1` at the end

``Main -> int "d" intMain -> int "d" int "+" int``

And we are pretty much done. But I think we can do better, by adding a few features. To begin with it would be good if we could use more than one type of dice. For instance 2 6 sided dices and 1 10 sided dice `2d6 1d10`.

# Multiple dice and recursion

To do this we can begin by refactoring the main rule into a dice rule

``Main -> DiceDice -> int "d" int         {% (d) => ({count: d, sides: d, bonus: 0}) %}      | int "d" int "+" int {% (d) => ({count: d, sides: d, bonus: d}) %}``

Note that we are also formatting the output into an object to make it less error prone. Now to add multiple dices it is as easy as adding right hand recursion on the `Main` rule.

``Main -> Dice      | Dice " " Main {% (d) => [d, d].flat()%}``

The post processing step on the second rule makes it so that we get an easy to parse output with an array of dice objects, instead of a deeply nested array.

Last thing I want to add to the language is something specific to dungeons and dragons. In that game it is common to throw twenty sided dices with either advantage or disadvantage. This means you throw the dice twice and keep the largest or smallest result respectively.

I think I want to represent this as `1d20a` or `1d20d`. This got a bit messy, but here are the rules I made for it

``Main -> BonusDice      | BonusDice " " Main {% (d) => [d, d].flat()%}BonusDice -> EnhancedDice　        {% (d) => ({...d, bonus: 0}) %}      	 | EnhancedDice "+" int  {% (d) => ({...d, bonus: d}) %}EnhancedDice -> Dice      {% (d) => ({...d, advantage: false, disadvantage: false}) %}              | Dice "a"  {% (d) => ({...d, advantage: true, disadvantage: false}) %}              | Dice "b"  {% (d) => ({...d, advantage: false, disadvantage: true}) %}Dice -> int "d" int         {% (d) => ({count: d, sides: d}) %}``

# Polish

The last convenience I want in the language is the option to not specify how many dices to throw, and default to 1. So `1d20` should be the same as `d20`. Pretty simple to just add to `Dice`

``Dice -> int "d" int         {% (d) => ({count: d, sides: d}) %}      | "d" int             {% (d) => ({count: 1, sides: d}) %}``

That is basically it! The output can easily be parsed and you can create a dice roller. Or, you can do like I did and add the dice rolling straight into the parser.

``# this is our starting ruleMain -> MultiDice {% (d) => d.map(v => ({...v, sum: v.rolls.reduce((acc, val) => acc + val) + v.bonus})) %}MultiDice -> BonusDice           | BonusDice " " MultiDice {% (d) => [d, d].flat()%}BonusDice -> RolledDice　        {% (d) => ({...d, bonus: 0}) %}      	   | RolledDice "+" int  {% (d) => ({...d, bonus: d}) %}#Do the roll directly in the parsingRolledDice -> EnhancedDice {% (d) => ({...d, rolls:									   new Array(d.count)									   .fill(undefined)									   .map(() => {										   //Create two rolls for each dice										   const rolls = new Array(2).fill(undefined).map(() => Math.floor(Math.random() * d.sides + 1))										   if(d.advantage)											   return Math.max(rolls, rolls)										   if(d.disadvantage)											   return Math.min(rolls, rolls)										   else return rolls									   })}) %}EnhancedDice -> Dice      {% (d) => ({...d, advantage: false, disadvantage: false}) %}              | Dice "a"  {% (d) => ({...d, advantage: true, disadvantage: false}) %}			  | Dice "b"  {% (d) => ({...d, advantage: false, disadvantage: true}) %}Dice -> int "d" int         {% (d) => ({count: d, sides: d}) %}      | "d" int             {% (d) => ({count: 1, sides: d}) %}int -> [0-9]:+        {% (d) => parseInt(d.flat().join("")) %}``

It got a bit messy, but pretty neat to get an output with a list of all the dice values and the result of adding them all together

Here is an example of the output if run with the input `1d20a+2 d4+1`

`json`[  {    count: 1,    sides: 20,    advantage: true,    disadvantage: false,    rolls: ,    bonus: 2,    sum: 16  },  {    count: 1,    sides: 4,    advantage: false,    disadvantage: false,    rolls: ,    bonus: 1,    sum: 5  }]``

# Final words

Nearley might not have been the best fit for this, but it was in all honesty really fun to use. And the result speaks for itself, it works and is pretty neat. The best part is that it is quite easy to build upon.

Now all that is missing is to starting using this system, and while implementing this I was thinking of creating a system like the one in DnDBeyond where you have 3D dice rolling on the screen. Would be fun to experiment with, and should be doable using three.js without too much effort