Skip to content

Repository files navigation

OneRoll

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.

Install

pip install oneroll

Build an editable package from source:

uv sync --frozen
uv run maturin develop

Python

Use 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())

CLI

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"

Current syntax

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.

Roadmap and specification

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.

Verify

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/html

License

AGPL-3.0

About

OneRoll: An efficient dice expression parsing tool, based on Rust and PEG grammar | 高效的骰子表达式解析工具, 基于 Rust 与 PEG 文法

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages