ExDiceRoller
Provides functionality around calculating both simple and complex dice rolling equations.
Features
-
Supports common math operators:
+,-,*,/ -
Supports
+and-unary operators. - Supports creating common expressions and parenthetically grouped expressions into complex dice-roll equations.
-
Supports creating uncommon dice rolls, such as
(1d4)d(3d6)-(1d4+7). -
Supports using variables in expressions that can be given values upon use, such as
1dx+y. - Allows developers to tokenize, parse, and then compile dice roll strings into reusable anonymous functions.
Installation
Add :ex_dice_roller to your list of dependencies in mix.exs:
def deps do
[
{:ex_dice_roller, "~> 0.3.0-alpha"}
]
endNext, run:
$ mix deps.getUsage
ExDiceRoller supports a variety of possible dice roll permutations that can be used in your application.
iex> ExDiceRoller.roll("1")
1
iex> ExDiceRoller.roll("1+2")
3
iex> ExDiceRoller.roll("1d6")
1
iex> ExDiceRoller.roll("1d20-5")
12
iex> ExDiceRoller.roll("1d20-(5*6)")
-28
iex> ExDiceRoller.roll("1d4d6")
10
iex> ExDiceRoller.roll("(1d4+2)d8")
28
iex> ExDiceRoller.roll("(1d4+2)d(1d20)")
16
iex> ExDiceRoller.roll("(1d4+2)d((5*6)d20-5)")
677
iex> ExDiceRoller.roll("1dx+y", x: 20, y: 13)
16Compiled Expressions
Parsed expressions can be compiled into a single, executable anonymous function. This function can be reused again and again, with any dice rolls being randomized and calculated for each call.
Note that while ExDiceRoller.roll/1 always returns integers, ExDiceRoller.execute/1 will
return either floats or integers.
iex> {:ok, roll_fun} = ExDiceRoller.compile("1d6 - (3d6)d5 + (1d4)/5")
{:ok, #Function<6.11371143/0 in ExDiceRoller.Compiler.compile_op/5>}
iex> ExDiceRoller.execute(roll_fun)
21.6
iex> ExDiceRoller.execute(roll_fun)
34.4
iex> {:ok, roll_fun} = ExDiceRoller.compile("1dx+10")
{:ok, #Function<8.36233920/1 in ExDiceRoller.Compiler.compile_op/5>}
iex> ExDiceRoller.execute(roll_fun, x: 5)
11
iex> ExDiceRoller.execute(roll_fun, x: "10d100")
523How It Works
- ExDiceRoller utilizes Erlang's leex library to tokenize a given dice roll string.
- The tokens are then passed to yecc which parses the tokens into an abstract syntax tree.
- The syntax tree is then interpreted through recursive navigation.
- During interpretation:
- Any basic numerical values are calculated.
- Any dice rolls are converted into anonymous functions.
- Any portion of the expression or equation that use both base values and dice rolls are converted into anonymous functions.
- The results of interpretation are then wrapped by a final anonymous function.
- This final anonymous function is then executed and the value returned.
iex(3)> expr = "(1d4+2)d((5*6)d20-5)"
"(1d4+2)d((5*6)d20-5)"
iex(4)> {:ok, tokens} = ExDiceRoller.tokenize(expr)
{:ok,
[
{:"(", 1, '('},
{:digit, 1, '1'},
{:roll, 1, 'd'},
{:digit, 1, '4'},
{:basic_operator, 1, '+'},
{:digit, 1, '2'},
{:")", 1, ')'},
{:roll, 1, 'd'},
{:"(", 1, '('},
{:"(", 1, '('},
{:digit, 1, '5'},
{:complex_operator, 1, '*'},
{:digit, 1, '6'},
{:")", 1, ')'},
{:roll, 1, 'd'},
{:digit, 1, '20'},
{:basic_operator, 1, '-'},
{:digit, 1, '5'},
{:")", 1, ')'}
]}
iex(5)> {:ok, ast} = ExDiceRoller.parse(tokens)
{:ok,
{:roll,
{{:operator, '+'},
{:roll, {:digit, '1'}, {:digit, '4'}},
{:digit, '2'}},
{{:operator, '-'},
{:roll,
{{:operator, '*'}, {:digit, '5'}, {:digit, '6'}},
{:digit, '20'}},
{:digit, '5'}}}}
iex(6)> {:ok, roll_fun} = ExDiceRoller.compile(ast)
{:ok, #Function<12.11371143/0 in ExDiceRoller.Compiler.compile_roll/4>}
iex(7)> roll_fun.()
739
iex(8)> roll_fun.()
905
iex(9)> ExDiceRoller.Compiler.fun_info(roll_fun)
{#Function<9.16543174/1 in ExDiceRoller.Compiler.compile_roll/4>,
:"-compile_roll/4-fun-0-",
[
{#Function<1.16543174/1 in ExDiceRoller.Compiler.compile_add/4>,
:"-compile_add/4-fun-1-",
[
{#Function<12.16543174/1 in ExDiceRoller.Compiler.compile_roll/4>,
:"-compile_roll/4-fun-3-", [1, 4]},
2
]},
{#Function<14.16543174/1 in ExDiceRoller.Compiler.compile_sub/4>,
:"-compile_sub/4-fun-1-",
[
{#Function<12.16543174/1 in ExDiceRoller.Compiler.compile_roll/4>,
:"-compile_roll/4-fun-3-", [30, 20]},
5
]}
]}Test Coverage and More
- ex_coveralls provides test coverage metrics.
- credo is used for static code analysis.