DicEx (dicEx v0.2.0)

Copy Markdown View Source

Quick start

DicEx.roll("1d20")           # => %DicEx.Result{total: 14, ...}
DicEx.roll("3d6 + 2")        # => %DicEx.Result{total: 13, ...}
DicEx.roll("2d20kh1")        # advantage — keep highest
DicEx.roll("4d6dl1")         # 4d6, drop lowest
DicEx.roll("8d6!")           # explode (fireball)
DicEx.roll("1d20r1")         # reroll natural 1s

# safe variant for untrusted/LLM-generated expressions
{:ok, result} = DicEx.roll_e(prompted_by_the_llm)

# programmatic API matching a UI's "count + die + modifier"
DicEx.roll_dice(2, 20, mod: 5, advantage: true)

# reproducible
DicEx.roll("4d6", seed: 42)

Notation reference

TokenMeaning
NdSRoll N dice of S sides (d4..d100)
kh[n]Keep highest n (advantage)
kl[n]Keep lowest n (disadvantage)
dh[n]Drop highest n
dl[n]Drop lowest n
! / !pExplode / explode & penetrate
r<op>nReroll (< <= = >= >); ro rerolls once
+ / -Add / subtract pools or modifiers

Only +/- compose — there's no *, /, or parentheses.

Reproducible rolls

Seed the default RNG for a reproducible sequence — useful for tests, replays, and anti-cheat:

DicEx.roll("2d20kh1", seed: 42)

A note on :seed

:seed reseeds the calling process's :rand state to produce a reproducible sequence. The prior state is not restored, so in a long-lived process (e.g. a LiveView) a later unseeded roll/2 continues the seeded sequence rather than drawing fresh entropy. Thread :rng explicitly when you need isolation, or re-seed per request.

Summary

Functions

Pretty-prints a result as a human readable string, e.g. "2d20kh1+5 = 23".

Rolls the given expression and returns a DicEx.Result.

Convenience for a single typed roll — the shape the dragonEx UI emits.

Like roll/2 but returns {:ok, result} or {:error, reason} without raising. Handy when the expression comes from untrusted input (e.g. a chat command parsed by an LLM in dragonEx).

Types

roll_opt()

@type roll_opt() :: {:rng, module() | {module(), term()}} | {:seed, integer()}

Functions

format(result)

@spec format(DicEx.Result.t()) :: String.t()

Pretty-prints a result as a human readable string, e.g. "2d20kh1+5 = 23".

roll(expression, opts \\ [])

@spec roll(binary(), [roll_opt()]) :: DicEx.Result.t()

Rolls the given expression and returns a DicEx.Result.

Options

  • :seed — integer seed for the default RNG (reproducible sequence).
  • :rng — an explicit RNG module (stateless) or {module, state} tuple.

roll_dice(count, sides, opts \\ [])

@spec roll_dice(pos_integer(), pos_integer(), keyword()) :: DicEx.Result.t()

Convenience for a single typed roll — the shape the dragonEx UI emits.

DicEx.roll_dice(2, 20)        # 2d20
DicEx.roll_dice(1, 20, mod: 5) # 1d20 + 5

Options

  • :mod — integer modifier added to the total (+/-).
  • :advantage — boolean, keeps highest of 2 (implies 2 dice if count==1).
  • :disadvantage — boolean, keeps lowest of 2.
  • Plus any option accepted by roll/2 (:seed, :rng).

roll_e(expression, opts \\ [])

@spec roll_e(binary(), [roll_opt()]) :: {:ok, DicEx.Result.t()} | {:error, String.t()}

Like roll/2 but returns {:ok, result} or {:error, reason} without raising. Handy when the expression comes from untrusted input (e.g. a chat command parsed by an LLM in dragonEx).