Skip to content

A library for parsing complex dice expression and rolling it

License

Notifications You must be signed in to change notification settings

twonirwana/DiceEvaluator

Repository files navigation

License: AGPL v3 codecov Maven Central

The Dice Evaluator

Dice infix notation (aka calculator notation) expression evaluator, using the shunting yard algorithm. The dice can have numbers or text/symbols on them. Some operators work only on numbers. It is also possible to tag elements and provide a color. This implementation was inspired by Javaluator. The dice evaluator works on lists, summing the rolls together is optional.

Goals

  • Always get the dice rolls in the answer, even for complex expressions

  • Clear defined operator precedence and correct bracket handling

  • Support for custom dice sides with text or symbols instant of numbers

  • Don’t always give the sum of the dice as answer but working with lists

  • Operators can handle, when possible and intuitive, lists of elements/numbers

  • Custom exception for errors in the expression evaluation

  • Usage of as few symbols as possible

  • No infinity loops possible and configurable limits for expensive operations

  • Make testing easy be providing the options to provide a custom, non-random, number generator

Development Decisions

  • It should be possible to get the resul of each die (for e.g. pool systems) and to add the dice results together. Therefor the default must be only append elements together and a special operator = must be used to sum the elements.

  • Exception over unintuitive behavior → It is better that the user knows what is not working then that something is working, but they don’t know what and why

  • not every operator can handel lists → to many operators are not intuitive:

    • is max on list the count or the sum of the elements

    • how to multiply and divide lists

Usage

The evaluator processes dice expression and returns a list of rolls, each containing a list of elements. Elements have a value (a number or a text) and can have a color. For example 2d6 rolls two six-sided dice and returns a list with two elements, each with a value between 1 and 6. To get the sum of the roll, simple add a = at the end, for example in this case 2d6=. The same applies to numbers 3 + 5 has as result a list with the elements 3 and 5, only if written as 3 + 5= the result is 8. All non-functional text must be surrounded (escaped) by '. For example 1d('head' + 'tail') will flip a coin. List can be included into the expression by using square brackets. An empty list [] or empty literal '' are non value. For example 1d[2,2,4,4,6,6] will a die which has two sides with 2, two sides with 4 and two sides with 6. The roll will be a list with one element, which has a value of 2, 4 or 6. Lists also escape characters, so 1d[head,tail] will also flip a coin.

Multiple expression can be separated by ,. For example 3d6, 4d8 will roll two six-sided dice and return a list with two rolls, the first one containing the roll elements of the 3d6 and the second one the roll of the 4d8.

Operators have a precedent, which is defined by the order of the operators in the table below. Operators with a higher precedence are evaluated first. Brackets can be used to change the order of evaluation. For example 1d4+3d6 is the appending of roll of 1d4 and 3d6, but (1d4+3=)d6) gets first the sum of the roll of 1d4 and 3 and then rolls this number of d6.

The number of dice is limited to 1000 and every number approve 9 digits or with more than 9 digit after the decimal dot result in an error.

Boolean values will be represented by 'true' (or 1) and 'false' (or 0) .

It is possible to set tags and color. Colors have no direct effect and will be also set to all random elements of the expression. Tags on the other hand wil change the interaction of with other operators, in most cases operators will work only on elements with the same tag.

Operators

All operators are case insensitiv.

Name Notation Example Description Precedent Associativity Left parameter Right parameter

Repeat

<number>x<expression>

3x2d6

Repeats the expression separately a number of times given in <number>. This should be used outside other expressions and will not work inside most expressions

0

left

a single integer number between 1-10

a expression

List Repeat

<number>r<expression>

3r(2d6=)

Repeats the expression a number of times given in <number> and combines the results in one list.

1

left

a single integer number between 0-20

a expression

Concat

<expression>_<expression>

d20_'dmg''

Combines the result of both expression into on single element

2

left

one or more elements

one or more elements

Or

<boolean>||<boolean>

d6=?5 || d6=?6

Boolean or operation of the two boolean values

3

left

boolean value

boolean value

And

<boolean>&&<boolean>

d6=?5 && d6=?6

Boolean and operation of the two boolean values

4

left

boolean value

boolean value

Not

!<boolean>

!d6=?5

Negates the boolean value right from it

5

right

boolean value

Equal

<left> =? <right>

d6=?5

Compare the left and the right and returns true if equal and false otherwise

6

left

one or more elements

one or more elements

Lesser

<left> <? <right>

d6<?5

Compare the left and the right and returns true if <left> is lesser than <right> otherwise false

7

left

a single number

a single number

Lesser Equal

<left> <=? <right>

d6<=?5

Compare the left and the right and returns true if <left> is lesser or equal then <right> otherwise false

8

left

a single number

a single number

Greater

<left> >? <right>

d6>?5

Compare the left and the right and returns true if <left> is greater than <right> otherwise false

9

left

a single number

a single number

Greater Equal

<left> >=? <right>

d6>=?5

Compare the left and the right and returns true if <left> is greater or equal than <right> otherwise false

10

left

a single number

a single number

In

<left> in <right>

d6 in [1/3/5]

Returns true if every element in left is contained in right otherwise false

11

left

a one or more elements

one or more elements

Sum

<left> =

2d6=

Sums the list of on the left side of the symbol. An empty list has the sum of 0

12

left

a list of numbers

-

Modulo

<left> mod <right>

d6 mod 2

returns the remainder of the division

13

left

a single integer number

a single non zero integer number

Multiply

<left> * <right>

2 * 6

Multiplies the right number with the left number

14

left

a single number

a single number

Divide

<left> / <right>

4 / 2

Divides the right number with the left number and rounds down to the next full number

15

left

a single integer number

a single integer number

Decimal Divide

<left> // <right>

4 // 3

Divides the right number with the left number and provides a decimal number with up to 5 decimal digital

16

left

a single number

a single number

Count

<list> c

3d6>3c

Counts the number of elements in a list

17

left

a list

-

Greater Then Filter

<list> > <number>

3d6>3

Keeps only the elements of the left list that are bigger as the right number. Applies only to elements with the same tag.

18

left

one or more numbers

a single number

Lesser Then Filter

<list> < <number>

3d6<3

Keeps only the elements of the left list that are lesser as the right number. Applies only to elements with the same tag.

19

left

one or more numbers

a single number

Greater Equal Then Filter

<list> >= <number>

3d6>=3

Keeps only the elements of the left list that are bigger or equal as the right number. Applies only to elements with the same tag.

20

left

one or more numbers

a single number

Lesser Equal Then Filter

<list> <= <number>

3d6<=3

Keeps only the elements of the left list that are lesser or equal as the right number. Applies only to elements with the same tag.

21

left

one or more numbers

a single number

Equal Filter

<list> == <element>

3d6==3

Keeps only the elements of the left list that are equal to the element. Applies only to elements with the same tag.

22

left

one or more elements

a single elements

Keep Highest

<list> k <numberToKept>

3d6k2

keeps the highest values out a list, like the roll of multiple dice. Applies only to elements with the same tag.

23

left

one or more elements

a single number

Keep Lowest

<list> l <numberToKept>

3d6l2

keeps the lowest values out a list, like the roll of multiple dice. Applies only to elements with the same tag.

24

left

one or more elements

a single number

Add to List

<left> + <right>

2d6 + 2 or +3

Combines the rolls of both sides to a single list. If used as unary operator, it will be ignored e.g. +5 will process to 5

25

left for binary and right for unary

none or more elements

one or more elements

Remove or Negative add to List

<left> - <right>

2 - 1 or -d6

Combines the rolls of both sides to a single list. If the element exists on both sides, it will be removed. If the element only exists on the right side and is a number then it will be multiplied with -1 and added

26

left for binary and right for unary

none or more elements

numbers or elements that are also elements of the left side

Reroll

<expression>rr<rerollIfIn>

10d6rr1

Reroll the whole <expression> once if any of the elements of <expression> are in the elements of <rerollIfIn>

27

left

one or more elements

one or more elements

Tag

<expression>tag<text>

d6 tag 'special'

Set a tag to all elements of an expression, most operator work on elements with the same tag. The tag will be appended to the name but a number remains a number, even with a text tag.

28

left

one or more elements

a single text

Color

<expression>col<text>

d6 col 'red'

Set a color to all elements, and all in it involved random elements, of an expression. The color will not directly given in the result and has no effect on other operations

29

left

one or more elements

a single text

Exploding Add Dice

<numberOfDice>d!!<numberOfFaces>

3d!!6

Throws dice and any time the max value of a die is rolled, that die is re-rolled and added to the die previous resul total. A roll of the reroll the sum of the value.

30

left for binary and right for unary

none or a single positiv integer number (max 1000)

a single integer number

Exploding Dice

<numberOfDice>d!<numberOfFaces>

4d!6 or d!6

Throws dice and any time the max value of a die is rolled, that die is re-rolled and added to the dice set total. A reroll will be represented as two dice roll elements

31

left for binary and right for unary

none or a single integer number (max 1000)

a single positiv integer number

Regular Dice

<numberOfDice>d<numberOfFaces>

3d20, d20 or 3d[2/4/8]

Throws a number of dice given by the left number. The number sides are given by the right number. If the right side a list, an element of the list is randomly picked. The roll is a list with the dice throw

32

left for binary and right for unary

none or a single integer number (max 1000)

a single positiv number or multiple elements

Functions

All functions are case insensitiv.

Name Notation Example Description

min

min(<expression1>, <expression2> …​)

min(4d6)

returns the smallest elements (multiple if the smallest is not unique) of one or more inner expressions. Text is compared alphabetically

max

max(<expression1>, <expression2> …​)

max(4d6)

returns the largest elements (multiple if the largest is not unique) of one or more inner expressions. Text is compared alphabetically

sort asc

asc(<expression1>, <expression2> …​)

asc(4d6)

sorts all elements ascending of one or more inner expressions. Text is compared alphabetically

sort desc

desc(<expression1>, <expression2> …​)

desc(4d6)

sorts all elements descending of one or more inner expressions. Text is compared alphabetically

cancel

cancel(<expression>, <listA>, <listB>)

cancel(8d10, 10, 1)

the elements of listA and listB (can also be single elements) cancel each other and remove each other from the result.

replace

replace(<expression>, <find>, <replace> …​)

replace(8d10, [9/10], 'bonus')

each element in <expression> that matches on of the elements in <find> will be replaced with the elements in <replace>. <replace> can be an empty list [] or literal '' and thereby removing the found elements. It is possible to add multiple <find>/<replace> pairs to replace different elements in one replace. If the <replace> expression contains dice then they will only be rolled on a matching find element and for each matching element again.

color on

colorOn(<expression>, <in>, <color> …​)

replace(8d10, [9/10], 'red')

each element in <expression> that is in the elements in in will be get the color <color>. <color> can be an empty list [] or literal '' and thereby removing the current color. It is possible to add multiple <in>/<color> pairs to set different color for different values. If the <color> expression contains dice then they will be rolled once and used for all elements.

explode

exp(<roll>,<rerollOnList>,<numberOfRerolls>

exp(d[1/2/3],[2/3]) or exp(d[1/2/3],[2/3], 2)

Rerolls the <roll> expression if any of its result elements are in the elements of <rerollOnList> and returns the original result and all reroll results. If <roll> contain more then one die then all are rerolled, even if only on result of one die matches the reroll list. <numberOfRerolls> define the maximum number of rerolls bevor the function stops with rerolls and returns the current result. <numberOfRerolls> must be a number between 0 and 100. If <numberOfRerolls> is not provided as argument then 100 will be used.

if

if(<boolean>,<true>,<false>)

if(1d6=?6,'six','not six') or if(1d6=?6,'six') or val('$r',1d6), if('$r'=?1,'one','$r'=?2,'two','else')

if <boolean> equal true then return the <true> expression or else the <false> expression. The <false> expression is optional, if it is missing and <boolean> is false then the result empty. It is possible to add more than <boolean>,<true> pair in the function, the result will be the <true> of the first true <boolean>, coming from left. All <boolean> must be non-empty and contain only on element (therefor can’t contain only val). val are will only set in the first <true>. Use the following structure to use if to set different value in a val: if(1d6>?4, val('$a',10), val('$a',-10)), this will set '$a' to 10 if the 1d6 roll is bigger than 4 and to -10 otherwise.

group count

groupC(<expression1>, <expression2> …​)

groupC(20d6)

counts all elements of with the same value and provides the results as list in the format of <count>x<value>

concatenate

concat(<expression1>, <expression2> …​)

concat('Attack: ', 2d20, ' Damage:', 3d6+5=)

Joining all expressions together to a single result.

value

val(<valueName>, <value>)

val('$1',6d6), '$1'=, ('$1'>4)c

Defining a value (that get evaluated once) that can be used in multiple times in the same expression. The value name must be surrounded by two '. Every occurrence of the valueName will be replaced by the value, with the expeption of <valueName> and it is possible to overwrite a value name. For example val('$1',6d6), '$1'=, ('$1'>4)c would define the result of 6d6 as '$1'. Therefore, '$1'= would provide the sum and ('$1'>4)c the count of dice with a value greater than 4 of the same dice throw.