Packages
Add a dice-rolling DSL with sigil support and reusable compiled functions to your application.
Current section
Files
Jump to
Current section
Files
lib/compilers/separator.ex
defmodule ExDiceRoller.Compilers.Separator do
@moduledoc """
Handles the `,` separator for rolls.
The separator allows for multiple, separate dice expressions to be evaluated
and only one returned based on provided options:
* `:highest`: returns the highest calculated value and is the default option
* `:lowest`: returns the lowest calculated value
Examples:
iex> ExDiceRoller.roll("1,2")
2
iex> ExDiceRoller.roll("1,1")
1
iex> ExDiceRoller.roll("1,2", opts: [:highest])
2
iex> ExDiceRoller.roll("1,2", opts: [:lowest])
1
iex> ExDiceRoller.roll("1d6+2,10d8+3", opts: [:highest])
49
iex> ExDiceRoller.roll("1d6+8,10d8+5", opts: [:lowest])
14
Seperator expressions can be wrapped in parentheses to be utilized it as a
subexpression in a larger expression.
Examples:
iex> ExDiceRoller.roll("(5d1,2d1)+5", opts: [:highest])
10
iex> ExDiceRoller.roll("(5d1,2d1)+5", opts: [:lowest])
7
## Separator Use And Keeping Dice
The separator can be used alongside kept dice rolls, provided:
* one side is a list and the other a number
* both sides are lists of equal length
When both sides are lists of equal length, separator will begin comparing the
values from both lists by index location.
iex> ExDiceRoller.roll("5d6,5d100", opts: [:keep, :lowest])
[2, 2, 6, 4, 5]
iex> ExDiceRoller.roll("5d6,5d100", opts: [:keep, :highest])
[47, 6, 49, 91, 54]
iex> ExDiceRoller.roll("(5d2,5d6)+5", opts: [:highest, :keep])
[7, 9, 9, 11, 6]
iex> ExDiceRoller.roll("(5d1,5d100)+5", opts: [:lowest, :keep])
[6, 6, 6, 6, 6]
iex> ExDiceRoller.roll("5d6, 3", opts: [:keep])
[3, 3, 6, 4, 5]
iex> ExDiceRoller.roll("3, 5d6", opts: [:keep])
[3, 4, 4, 6, 3]
iex> ExDiceRoller.roll("4, xd5", x: ["1d4", 2.5], opts: [:keep])
[5, 4, 4, 4]
iex> ExDiceRoller.roll("2d4, 1d8", opts: [:keep])
** (ArgumentError) cannot use separator on lists of differing lengths
"""
@behaviour ExDiceRoller.Compiler
alias ExDiceRoller.{Args, Compiler, ListComprehension}
@impl true
def compile({:sep, left_expr, right_expr}) do
compile_sep(Compiler.delegate(left_expr), Compiler.delegate(right_expr))
end
@spec compile_sep(Compiler.compiled_val(), Compiler.compiled_val()) :: Compiler.compiled_fun()
defp compile_sep(l, r) when is_function(l) and is_function(r),
do: fn args -> high_low(l.(args), r.(args), args) end
defp compile_sep(l, r) when is_function(l),
do: fn args -> high_low(l.(args), r, args) end
defp compile_sep(l, r) when is_function(r),
do: fn args -> high_low(l, r.(args), args) end
defp compile_sep(l, r), do: fn args -> high_low(l, r, args) end
@spec high_low(Compiler.calculated_val(), Compiler.calculated_val(), Compiler.args()) ::
Compiler.calculated_val()
defp high_low(l, l, _), do: l
defp high_low(l, r, args) when is_list(args) do
case Args.find_first(args, [:highest, :lowest]) do
:highest -> ListComprehension.apply(l, r, :highest, "separator", &do_high_low/3)
:lowest -> ListComprehension.apply(l, r, :lowest, "separator", &do_high_low/3)
_ -> ListComprehension.apply(l, r, :highest, "separator", &do_high_low/3)
end
end
@spec do_high_low(Compiler.calculated_val(), Compiler.calculated_val(), :highest | :lowest) ::
Compiler.calculated_val()
defp do_high_low(l, r, :highest) when l > r, do: l
defp do_high_low(_, r, :highest), do: r
defp do_high_low(l, r, :lowest) when l < r, do: l
defp do_high_low(_, r, :lowest), do: r
end