OneRoll is a Rust dice-language engine exposed to Python through PyO3. It ships with a Python SDK, CLI, and Textual TUI. The v2 package boundary will make UI dependencies optional for SDK-only installations.
The current 1.x engine supports numeric dice expressions, arithmetic,
parentheses, comments, and a compatibility set of modifiers. The 2.0 roadmap
evolves that expression roller into a bounded, typed program language with
ranges, weighted-list dice, validators, variables, option pipelines, and
structured results.
Current scalar literals and totals use checked signed 64-bit arithmetic.
Overflow, division by zero, and invalid exponents raise ValueError with a
stable arithmetic.* code instead of panicking or wrapping.
Numeric dice use the request-scoped oneroll-chacha12-v1 protocol and unbiased
rejection sampling. Rust embedders can supply a canonical RandomSeed through
DiceCalculator::with_seed and inspect the replay descriptor. The frozen
Python Engine(..., seed=...) and CLI replay surface remain tracked v2 work;
the compatibility helpers do not expose a temporary seed signature.
OneRoll is being hardened for production. Features listed in the v2 RFC are targets unless the implementation status explicitly says otherwise.
pip install onerollBuild an editable package from source:
uv sync --frozen
uv run maturin developUse roll() for one compatibility expression:
import oneroll
result = oneroll.roll("4d6kh3 # attribute")
print(result["total"])
print(result["comment"])Use run() for a non-empty semicolon-separated program:
program = oneroll.run("1d20 + 5; 2d6 # encounter")
print([result["total"] for result in program["results"]])
print(program["comment"])Every instruction in one program shares the same request budget. Invalid or
exhausted evaluations raise ValueError instead of hanging or panicking.
Parsing, recursion, generated values, collections, output, work, and batches
all have safe defaults and non-disableable hard ceilings.
Create an immutable policy when an embedding needs lower limits:
policy = oneroll.ResourcePolicy().with_limit("generated_values", 2_000)
roller = oneroll.OneRoll(policy)
print(policy.limits())python -m oneroll "3d6 + 2"
python -m oneroll "1d20 + 5; 2d6 # encounter"
python -m oneroll --stats "3d6" --times 100
python -m oneroll --show-limits
python -m oneroll --limit generated_values=2000 "20d6"The parser's authoritative accepted syntax lives in
src/oneroll/grammar.pest and is embedded directly
in the language guide. Observable 1.x semantics
are recorded in the conformance corpus, including
compatibility-sensitive behavior and known defects.
- Language guide
- Resource limits
- Deterministic randomness
- Fuzzing and property gates
- Production roadmap
- RFC-0001: OneRoll Program Language v2
- RFC-0002: Execution Safety, Budgets, and Randomness
- RFC-0003: Typed Program Results, Roll Traces, and Errors
- RFC-0004: Python Engine API and Package Boundary
- GitHub milestones
- GitHub issues
The implementation order is Program → typed values → dice sources → validators → option pipelines → functions and conditionals. CLI commands such
as help and la stay outside the pure core language, and @ remains reserved
until bounded jump semantics are accepted.
cargo test
cargo test --release property_
uv run --frozen maturin develop
uv run --frozen python -m unittest discover -s tests -v
uv run --frozen sphinx-build -W --keep-going -b html docs/source docs/_build/htmlAGPL-3.0