RANDSUM Dice Notation Specification
Version: 0.9.0 Status: Draft Date: 2026-03-21
Abstract
This document defines the RANDSUM Dice Notation (RDN), a human-readable, machine-parseable format for expressing dice rolls in tabletop role-playing games. RDN specifies a three-stage execution pipeline with deterministic modifier ordering, a faceted classification system for modifiers, and four conformance levels for partial implementation. The notation supports standard dice, custom faces, geometric dice, draw dice, and 26 modifiers across three pipeline stages. This specification is intended to enable interoperable dice notation processing across virtual tabletop platforms, chat bots, and game automation tools.
Document Information:
- Authors: Alex Jarvis (alxjrvs@gmail.com)
- Project: https://github.com/RANDSUM/randsum
- Specification Site: https://notation.randsum.dev
- Documentation: https://randsum.dev
- Playground: https://playground.randsum.dev
Status of This Memo
This document is a community specification published by the RANDSUM project. It is not an Internet Standards Track specification; a submission to the IETF is forthcoming. Distribution of this memo is unlimited.
This is a draft release. The specification is believed to be stable for implementation but may undergo revisions before the 1.0 release. Feedback is welcome via the project repository.
1. Introduction
1.1 Purpose
This document is the formal specification for RANDSUM Dice Notation (RDN), a compact language for describing dice rolls, modifiers, and resolution mechanics used in tabletop role-playing games and related systems.
1.2 Scope
This specification covers:
- Dice types and their generation models
- Modifier taxonomy, classification, and execution semantics
- The three-stage execution pipeline and two-channel architecture
- Condition expression sub-grammar
- Notation syntax overview
- Safety limits and implementation guidance
- Conformance levels for partial implementations
This specification is language-agnostic. It describes the required behavior of any conforming implementation without prescribing implementation language, data structures, or API shape.
1.3 Conventions
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals, as shown here.
1.4 Versioning
This is version 0.9.0 of the RANDSUM Dice Notation Specification. Future versions will follow semantic versioning: breaking changes increment the major version, additive changes increment the minor version.
1.5 Document History
| Version | Date | Changes |
|---|---|---|
| 0.9.0 | 2026-03-21 | Initial public draft. Three-stage pipeline, 26 modifiers, four conformance levels, faceted classification, ABNF grammar, 48 conformance test vectors, security considerations, IANA section. |
2. Glossary
The following terms are used normatively throughout this specification.
| Term | Definition |
|---|---|
| Die Expression | A notation string that describes one or more dice and their modifiers. Example: 4d6L. |
| Roll | A single random outcome produced by a die. A numeric value selected from the die’s face set. |
| Pool | The ordered array of roll values produced by evaluating a die expression. Modified in-place by Stage 1 and Stage 2 modifiers. |
| Total | The scalar numeric result derived from a pool, typically by summation, but optionally by counting (see Reinterpret). |
| Modifier | A named transformation applied to a pool or total during the execution pipeline. Each modifier has a priority, stage, verb, and channel. |
| Primitive | A modifier or die type that provides irreducible behavior not expressible as a composition of other features. |
| Alias | A notation convenience that de-aliases to one or more primitives with identical observable behavior. |
| Macro | A modifier that dispatches to different primitive behaviors based on runtime state. |
| Stage | One of three ordered phases of the execution pipeline. Each stage has a contract governing what operations are permitted. |
| Verb | The operational classification of a modifier describing what it does to the pool or total. One of: Clamp, Map, Filter, Substitute, Generate, Accumulate, Scale, Reinterpret. |
| Channel | The output target of a modifier: Pool (transforms the rolls array) or Total (transforms the scalar total). |
| Priority | A numeric value that determines execution order within the pipeline. Lower numbers execute first. |
| Condition Expression | A sub-grammar for specifying numeric comparisons used by multiple modifiers. See Section 5. |
| Die Type | The generation model and face configuration of a die. Standard, Custom Faces, Geometric, or Draw. |
| Condition | A single comparison operator and operand within a condition expression (e.g., >=7, <3). |
| Presentation Directive | A notation feature that affects display ordering but does not alter the total. Sort is the sole presentation directive. |
| Parser Directive | A notation feature resolved at parse time before the execution pipeline runs. Repeat (xN) is the sole parser directive. |
| Notation Metadata | Information embedded in notation that does not participate in the execution pipeline. Annotations ([text]) are the sole metadata feature. |
3. Classification System
3.1 Faceted Classification Overview
Every modifier in RDN is classified along four independent facets:
- Derivation Status — whether the feature is primitive, alias, or macro
- Pipeline Stage — when the modifier executes (Stage 1, 2, or 3)
- Operational Verb — what the modifier does (Clamp, Map, Filter, etc.)
- Output Channel — where the modifier writes its result (Pool or Total)
These facets are orthogonal but not fully independent: certain combinations are constrained (see Section 3.6). Together they form a complete taxonomy of every modifier in the system.
3.2 Derivation Status
| Status | Definition | Set |
|---|---|---|
| Primitive | Irreducible behavior. Cannot be expressed as a composition of other modifiers. | Closed |
| Alias | De-aliases to one or more primitives. Observable behavior is identical to the de-aliased form. | Closed |
| Macro | Dispatches to different primitives based on runtime state. | Closed |
The set of derivation statuses is closed. No other status exists or may be added without a new specification version.
3.3 Pipeline Stage
| Stage | Name | Priority Range | Contract | Boundary Signal |
|---|---|---|---|---|
| 1 | Deterministic Value Shaping | 10—39 | No RNG. Pure value transformations over the dice array. No pool size changes. requiresRollFn = false. | requiresRollFn = false |
| 2 | Pool Dynamics | 40—69 | Both stochastic and deterministic pool operations. Stochastic modifiers require a roll function. requiresRollFn = true (stochastic). Drop/Keep run after explosions so the filter operates on the expanded pool. | requiresRollFn = true (stochastic) |
| 3 | Total Derivation | 80—100 | Pool is frozen. Operates on the scalar total only. mutatesRolls = false. | mutatesRolls = false |
Stage boundaries are structural: a modifier declaring requiresRollFn = true cannot run in Stage 1. A modifier declaring mutatesRolls = false cannot modify the dice array.
3.4 Operational Verb
| Verb | Stage | Description |
|---|---|---|
| Clamp | 1 | Constrain individual die values to boundaries. Pool size unchanged. |
| Map | 1 | Deterministic value substitution. Pool size unchanged. |
| Filter | 2 | Remove dice from the pool. Pool shrinks. |
| Substitute | 2 | Re-randomize matching dice. Pool size unchanged. |
| Generate | 2 | Append new dice to the pool. Pool grows. |
| Accumulate | 2 | Fold additional rolls into an existing die. Pool size preserved. |
| Scale | 3 | Arithmetic transformation of the total. |
| Reinterpret | 3 | Replace the aggregation model (sum to cardinality). |
There are exactly 8 operational verbs. Wild Die uses Dispatch (see Section 3.2 on Macros), which is not a verb but a macro classification.
3.5 Output Channel
| Channel | Description | Used By |
|---|---|---|
| Pool | Modifier transforms the rolls array. The resulting array is input to the next modifier. | Stage 1, Stage 2 |
| Total | Modifier provides a transformTotal function. The pool passes through unchanged. | Stage 3 |
A modifier MUST use exactly one channel. No modifier uses both channels simultaneously.
3.6 Facet Interaction Constraints
The following constraints govern valid facet combinations:
-
Channel is determined by Stage. Stage 1 and Stage 2 modifiers use the Pool channel. Stage 3 modifiers use the Total channel. An implementation MUST NOT assign a Stage 1 modifier to the Total channel or vice versa.
-
Macros do not have a Verb. A macro dispatches to multiple primitives at runtime; its verb varies by execution path. Wild Die dispatches to Accumulate, Filter, or no-op depending on the rolled value.
-
Aliases inherit facets from their parent primitive. An alias’s Stage, Verb, and Channel are identical to the primitive it de-aliases to. Keep inherits Stage 2 / Filter / Pool from Drop. Minus inherits Stage 3 / Scale / Total from Plus.
3.7 Valid Facet Combinations
| Stage | Verb | Channel | Modifiers |
|---|---|---|---|
| 1 | Clamp | Pool | Cap |
| 1 | Map | Pool | Replace |
| 2 | Substitute | Pool | Reroll, Reroll Once (alias), Unique |
| 2 | Generate | Pool | Explode, Explode Sequence, Inflation (alias), Reduction (alias) |
| 2 | Accumulate | Pool | Compound, Penetrate |
| 2 | Filter | Pool | Drop, Keep (alias), Keep Middle (alias) |
| 2 | Dispatch | Pool | Wild Die (macro) |
| 3 | Reinterpret | Total | Count, Count Successes (alias), Count Failures (alias) |
| 3 | Scale | Total | Multiply, Plus, Minus (alias), Margin of Success (alias), Integer Divide, Modulo, Multiply Total (alias) |
Sort (Stage 3, priority 95) is a Presentation Directive, not an operational modifier. It reorders the pool array for display but does not alter the total. It is classified under the Pool channel as a special case: it mutates the array ordering but not the array contents or the total.
4. Dice Expressions
4.1 Standard Dice (NdS)
Classification: Primitive
| Property | Value |
|---|---|
| Notation | NdS where N is quantity (default 1), S is number of sides |
| Generation Model | Uniform random selection |
| Face Type | Numeric, sequential integers |
| Face Range | [1..S] |
A standard die produces values uniformly distributed over the integers 1 through S inclusive. When N is omitted, it defaults to 1. When S is a positive integer, the die has faces numbered 1 through S.
Implementations MUST support S values of at least 1 through 1000. The quantity N MUST be a positive integer.
4.2 Custom Faces (d{…})
Classification: Primitive
| Property | Value |
|---|---|
| Notation | Nd{f1,f2,...,fN} |
| Generation Model | Uniform random selection |
| Face Type | Numeric or string, arbitrary values |
| Face Range | Exactly the listed face values |
Custom faces dice select uniformly from the explicitly listed face values. Duplicate values are permitted and create weighted distributions: d{1,1,2} has a 2/3 probability of producing 1.
When faces are non-numeric strings, the roll result contains string values. String-faced dice do not produce a numeric total; the total is undefined for string-faced pools.
4.3 Geometric Dice (gN)
Classification: Primitive
| Property | Value |
|---|---|
| Notation | gN |
| Generation Model | Open-ended (repeat until termination) |
| Face Type | Numeric, sequential integers |
| Face Range | [1..N] per roll |
| Termination | Stops when a roll is not equal to N (not max) |
| Safety Cap | 1000 iterations |
A geometric die rolls a standard dN repeatedly as long as each roll equals N (the maximum). The total is the sum of all rolls in the sequence. The die terminates when a roll is less than N, or when the safety cap of 1000 iterations is reached.
4.4 Draw Dice (DDN)
Classification: Primitive
| Property | Value |
|---|---|
| Notation | QddN or QDDN where Q is quantity, N is sides |
| Generation Model | Sampling without replacement |
| Face Type | Numeric, sequential integers |
| Face Range | [1..N] |
Draw dice sample without replacement from the face set [1..N]. Each value appears at most once within each pass through the pool. If the quantity Q exceeds N, the pool is exhausted and then reshuffled, and drawing continues from the fresh pool until all Q values are produced. For example, 8DD6 produces 8 values: the first 6 are a permutation of [1..6], then the pool reshuffles and 2 more values are drawn from it. Implementations SHOULD use Fisher-Yates shuffle or an equivalent unbiased algorithm for each shuffle pass.
4.5 Dice Aliases
4.5.1 Percentile (d%)
Classification: Alias
De-aliases to: Standard die 1d100.
The notation d% is equivalent to 1d100. Implementations MUST treat d% identically to 1d100 in all contexts.
4.5.2 Fate/Fudge (dF, dF.2)
Classification: Alias
Fate Core (dF): De-aliases to d{-1,0,1} (a custom faces die). Produces values in {-1, 0, +1}.
Extended Fudge (dF.2): De-aliases to d{-2,-1,0,1,2} (a custom faces die). Produces values in {-2, -1, 0, +1, +2}.
dF.1 is an alias for dF (standard Fate die with faces [-1, 0, 1]). Implementations that accept dF.1 MUST treat it identically to dF.
The standard Fate roll is 4dF, producing a total in the range [-4, +4].
4.5.3 Zero-Bias (zN)
Classification: Alias
De-aliases to: Custom Faces die d{0,1,2,...,N-1}. Produces values in [0..N-1] instead of [1..N].
4.6 Dice Type Classification Table
| Die Type | Generation Model | Face Type | Primitive/Alias | Notation |
|---|---|---|---|---|
| Standard | Uniform selection | Numeric sequential | Primitive | NdS |
| Custom Faces | Uniform selection | Numeric or string, arbitrary | Primitive | Nd{...} |
| Geometric | Open-ended repeat | Numeric sequential | Primitive | gN |
| Draw | Without replacement | Numeric sequential | Primitive | DDN |
| Percentile | Uniform selection | Numeric sequential | Alias (Standard) | d% |
| Fate/Fudge | Uniform selection | Numeric mapped | Alias (Custom) | dF, dF.2 |
| Zero-Bias | Uniform selection | Numeric shifted | Alias (Custom Faces) | zN |
5. Condition Expressions
Condition expressions are a shared sub-grammar used by Cap, Drop, Reroll, and Count modifiers.
5.1 Operator Syntax
A condition consists of an optional comparison operator followed by an integer operand.
| Operator | Meaning | Example |
|---|---|---|
>N | Greater than N | >5 |
<N | Less than N | <3 |
>=N | Greater than or equal to N | >=7 |
<=N | Less than or equal to N | <=2 |
=N | Exactly equal to N | =4 |
N (bare integer) | Modifier-specific (see Section 5.3) | 5 |
5.2 Condition Lists
Multiple conditions MAY be specified by separating them with commas within braces:
{>=7,<3} -- matches values >= 7 OR values < 3
{1,6} -- bare integers, interpretation depends on modifier
{>=5,<=2} -- matches values >= 5 OR values <= 2
Conditions within a list are combined with logical OR unless the modifier specifies otherwise (see Count deduct mode in Section 6.6.2).
5.3 Modifier-Specific Semantics
The bare integer form (no operator prefix) has different semantics depending on the modifier:
| Modifier | Bare Integer Meaning |
|---|---|
| Cap | Maximum cap: no result exceeds N. Equivalent to >N (clamp down to N). |
| Drop | Exact match: drop dice showing exactly N. Equivalent to =N. |
| Reroll | Exact match: reroll dice showing exactly N. Equivalent to =N. |
| Count | Exact match: count dice showing exactly N. Equivalent to =N. |
Implementations MUST document this divergence. The Cap modifier’s bare integer interpretation is the only case where a bare integer does not mean exact match.
6. Execution Pipeline
This section is normative. Conforming implementations MUST execute modifiers according to the rules described here.
6.1 Three-Stage Model
The execution pipeline processes modifiers in three sequential stages:
- Stage 1 — Deterministic Value Shaping (priority 10—39): Pure value transformations of the pool array. No random number generation. No pool size changes.
- Stage 2 — Pool Dynamics (priority 40—69): Both stochastic and deterministic pool operations. Requires a roll function for stochastic modifiers. Drop and Keep run after explosions so the filter operates on the expanded pool.
- Stage 3 — Total Derivation (priority 80—100): Pool is frozen. Operates on the scalar total.
Modifiers execute in ascending priority order. Within a stage, priority ordering is strict: a modifier with priority 20 MUST execute before a modifier with priority 30.
6.2 Two-Channel Architecture
The pipeline maintains two output channels:
- Pool channel (
rolls): An ordered array of numeric values representing individual die results. Stage 1 and Stage 2 modifiers write to this channel. - Total channel (
transformTotal): A function that transforms the computed scalar total. Stage 3 modifiers write to this channel.
Mutual exclusion invariant: A single modifier invocation MUST write to exactly one channel. No modifier writes to both channels simultaneously.
The total is computed by summing the final pool array, then applying all transformTotal functions in priority order.
6.3 Priority Ordering
The following priority assignments are normative. Implementations MUST respect these exact values:
| Priority | Modifier | Stage |
|---|---|---|
| 10 | Cap | 1 |
| 30 | Replace | 1 |
| 40 | Reroll | 2 |
| 50 | Explode | 2 |
| 51 | Compound | 2 |
| 52 | Penetrate | 2 |
| 53 | Explode Sequence | 2 |
| 55 | Wild Die | 2 |
| 60 | Unique | 2 |
| 65 | Drop | 2 |
| 66 | Keep (alias for Drop) | 2 |
| 80 | Count | 3 |
| 85 | Multiply | 3 |
| 90 | Plus | 3 |
| 91 | Minus (alias for Plus) | 3 |
| 93 | Integer Divide | 3 |
| 94 | Modulo | 3 |
| 95 | Sort | 3 |
| 100 | Multiply Total (alias for Multiply) | 3 |
6.4 Stage 1 — Deterministic Value Shaping
6.4.1 Contract
Stage 1 modifiers:
- MUST NOT invoke a random number generator.
- MUST be pure functions: given the same pool and options, they produce the same output.
- MUST NOT require a roll function (
requiresRollFn = false). - MUST NOT change pool size (no dice are added or removed).
6.4.2 Cap
Primitive | Stage 1 | Clamp | Pool channel | Priority 10
Notation: C{condition-list}
Options: ComparisonOptions
Effect: Constrains individual die values to specified boundaries. Values exceeding an upper bound are clamped down to the bound. Values below a lower bound are clamped up to the bound. Pool size is unchanged.
Evaluation order: Upper bounds are applied first (greaterThan, greaterThanOrEqual, exact), then lower bounds (lessThan, lessThanOrEqual).
Bare integer semantics: A bare integer N in the condition expression means “maximum cap” — no result exceeds N. This differs from other modifiers where bare integers mean exact match (see Section 5.3).
Pool size: Unchanged.
Aliases: None.
6.4.3 Replace
Primitive | Stage 1 | Map | Pool channel | Priority 30
Notation: V{from=to,...}
Options: ReplaceOptions (from/to value pairs)
Effect: Deterministic substitution of die values. For each die in the pool, if its value matches a from value, it is replaced with the corresponding to value. Pool size is unchanged.
Multiple replacement rules are applied in the order specified. A die may be replaced multiple times if subsequent rules match the replaced value.
Pool size: Unchanged.
Aliases: None.
6.5 Stage 2 — Pool Dynamics
6.5.1 Contract
Stage 2 modifiers:
- MAY have access to a roll function (
requiresRollFn = truefor stochastic modifiers). - MAY introduce new random values into the pool.
- MAY grow, shrink, or preserve the pool size depending on the modifier.
6.5.2 Reroll
Primitive | Stage 2 | Substitute | Pool channel | Priority 40
Notation: R{condition-list}, R{condition-list}N
Options: RerollOptions (comparison conditions, optional max count)
Effect: Re-randomizes dice matching the condition expression. Each matching die is re-rolled. If the new value still matches, the die is re-rolled again, up to a safety maximum of 99 attempts per die. The optional trailing integer N limits the total number of dice that will be rerolled across the pool.
Pool size: Unchanged.
Aliases: Reroll Once (Section 6.7.3).
6.5.3 Unique
Primitive | Stage 2 | Substitute | Pool channel | Priority 60
Notation: U
Options: boolean or UniqueOptions
Effect: Enforces that no two dice in the pool share the same value. This is a pool-level constraint: any duplicate values are re-rolled until unique or until the safety limit is reached.
When specified as a boolean true, applies to all dice. UniqueOptions may specify a notUnique list of values exempt from the uniqueness constraint.
Pool size: Unchanged.
Aliases: None.
6.5.4 Drop
Primitive | Stage 2 | Filter | Pool channel | Priority 65
Notation: L, Ln, H, Hn, D{condition-list}
Options: DropOptions (highest, lowest, and/or comparison conditions)
Effect: Removes dice from the pool. L drops the lowest-valued die (or Ln drops the N lowest). H drops the highest-valued die (or Hn drops the N highest). D{...} drops dice matching comparison conditions.
When both positional (highest/lowest) and conditional drops are specified, conditional filtering is applied first, then positional drops operate on the remaining pool.
Drop runs after all explosion-family modifiers (priority 50—55), so the filter operates on the fully expanded pool.
Pool size: Shrinks.
Validation: The total number of dice dropped MUST be less than the pool size. Dropping all dice is an error.
Aliases: Keep (Section 6.7.1), Keep Middle (Section 6.7.2).
6.5.5 Keep
Alias | Stage 2 | Filter | Pool channel | Priority 66
Notation: K, Kn, kl, klN
De-aliases to: Drop (inverse). Keeping the highest N dice from a pool of Q dice is equivalent to dropping the lowest (Q - N) dice.
Pool size: Shrinks.
Aliases: None (is itself an alias).
6.5.6 Explode
Primitive | Stage 2 | Generate | Pool channel | Priority 50
Notation: !
Options: boolean | ComparisonOptions
Effect: Each die showing its maximum value triggers one additional die roll appended to the pool. This is a single-pass operation: newly added dice are not evaluated for further explosions. This is a known limitation of the current implementation.
Implementation note: The RANDSUM roller applies explode in a single pass. Newly added dice that also show the maximum value do not trigger further explosions in the current implementation.
Conditional Explode (!{condition}): When a condition expression is provided, the explosion triggers on values matching the condition instead of only on the maximum value. Bare ! with no condition defaults to triggering on the die’s maximum face value.
Pool size: Grows (one new die per triggered explosion per pass).
Aliases: None.
6.5.7 Explode Sequence
Primitive | Stage 2 | Generate | Pool channel | Priority 53
Notation: !s{N1,N2,...}
Options: number[] (sequence of die sizes)
Effect: On a maximum roll, instead of re-rolling the same die, the next die in the provided size sequence is rolled. If that die also shows its maximum, the sequence advances again. When the sequence is exhausted, the last die size repeats (capped by the explosion depth limit).
Pool size: Grows.
Aliases: Inflation (Section 6.7.9), Reduction (Section 6.7.10).
6.5.8 Compound
Primitive | Stage 2 | Accumulate | Pool channel | Priority 51
Notation: !!, !!N
Options: boolean | number | ComparisonOptions (explosion depth; true = default depth, 0 = unlimited up to safety cap, integer N = max N additional rolls)
Effect: Like Explode, but instead of adding new dice to the pool, each additional roll is added to the triggering die’s value. Pool size is preserved. The resulting die value may exceed the die’s maximum face value.
Conditional Compound (!!{condition}): When a condition expression is provided, the compound triggers on values matching the condition instead of only on the maximum value.
Pool size: Preserved.
Aliases: None.
6.5.9 Penetrate
Primitive | Stage 2 | Accumulate | Pool channel | Priority 52
Notation: !p, !pN
Options: boolean | number | ComparisonOptions (explosion depth)
Effect: Like Compound, but each subsequent roll has 1 subtracted from its value before accumulation. On a maximum roll, the die is re-rolled, 1 is subtracted from the new roll, and the result is added to the triggering die’s accumulated total. This continues while subsequent rolls also hit the maximum, up to the depth limit.
Conditional Penetrate (!p{condition}): When a condition expression is provided, the penetrate triggers on values matching the condition instead of only on the maximum value.
Pool size: Preserved.
Aliases: None.
6.5.10 Wild Die
Macro | Stage 2 | Dispatch | Pool channel | Priority 55
Notation: W
Options: boolean
Effect: The Wild Die macro implements the D6 System wild die mechanic. It examines the last die in the pool and dispatches:
- Maximum value: Compound-explode the wild die (Accumulate behavior). The wild die’s value is replaced with the accumulated total.
- Value of 1: Drop the wild die AND the highest remaining non-wild die (Filter behavior). Pool shrinks by 2.
- Any other value: No operation. Pool unchanged.
Wild Die requires rollOne because the compound-explosion path introduces randomness.
Pool size: Varies (preserved on max, shrinks by 2 on 1, unchanged otherwise).
Aliases: None.
6.6 Stage 3 — Total Derivation
6.6.1 Contract
Stage 3 modifiers:
- MUST NOT modify the pool array (
mutatesRolls = false). - Operate exclusively on the scalar total via
transformTotal. - The pool is frozen: its contents are final after Stage 2 completes.
Presentation directive carve-out: Presentation directives (Sort) MAY reorder the pool array but MUST NOT alter its values or the total. Sort does not violate the Stage 3 immutability contract because it changes only presentation order, not values.
6.6.2 Count
Primitive | Stage 3 | Reinterpret | Total channel | Priority 80
Notation: #{condition-list}
Options: CountOptions (comparison conditions, optional deduct flag)
Effect: Replaces the default sum aggregation with a cardinality count. Instead of summing all die values, the total becomes the number of dice matching the specified conditions.
Count is the only modifier that changes what the total means rather than what the total is. All subsequent Scale modifiers operate on the count result, not the original sum.
Dual-condition deduct mode: When both an upper condition (greaterThan/greaterThanOrEqual) and a lower condition (lessThan/lessThanOrEqual) are specified, the modifier automatically activates deduct mode. Dice matching the upper condition are counted as successes; dice matching the lower condition are counted as failures and subtracted from the success count.
Pool size: N/A (Total channel).
Aliases: Count Successes (Section 6.7.7), Count Failures (Section 6.7.8).
6.6.3 Multiply
Primitive | Stage 3 | Scale | Total channel | Priority 85
Notation: *N
Options: number
Effect: Multiplies the current total by N. This is a pre-arithmetic multiplication: it runs before Plus/Minus (priority 90/91) but after Count (priority 80).
Pool size: N/A (Total channel).
Aliases: Multiply Total (Section 6.7.6).
6.6.4 Plus
Primitive | Stage 3 | Scale | Total channel | Priority 90
Notation: +N
Options: number
Effect: Adds N to the current total.
Pool size: N/A (Total channel).
Aliases: Minus (Section 6.7.4), Margin of Success (Section 6.7.5).
6.6.5 Integer Divide
Primitive | Stage 3 | Scale | Total channel | Priority 93
Notation: //N
Options: number
Effect: Divides the current total by N using truncating integer division (round toward zero).
Pool size: N/A (Total channel).
Aliases: None.
6.6.6 Modulo
Primitive | Stage 3 | Scale | Total channel | Priority 94
Notation: %N
Options: number
Effect: Replaces the current total with the remainder of dividing by N (total mod N).
Pool size: N/A (Total channel).
Aliases: None.
6.7 Modifier Aliases
Each alias de-aliases to a normative primitive form. An alias MUST produce identical observable behavior to its de-aliased form.
6.7.1 Keep
Notation: K, Kn, kl, klN
De-aliases to: Drop (inverse).
Keeping the highest N dice from a pool of Q dice is equivalent to dropping the lowest (Q - N) dice. KN on a pool of Q dice de-aliases to L(Q-N). klN (keep lowest) de-aliases to H(Q-N).
When N is omitted, it defaults to 1.
Inherits: Stage 2 | Filter | Pool channel | Priority 66.
6.7.2 Keep Middle
Notation: KM
De-aliases to: Drop lowest 1 + Drop highest 1.
Removes both the lowest and highest die from the pool, keeping only the middle values.
Inherits: Stage 2 | Filter | Pool channel | Priority 66.
6.7.3 Reroll Once
Notation: ro{condition-list}
De-aliases to: Reroll with max: 1.
Rerolls matching dice at most once. The new result stands regardless of whether it still matches the condition.
Inherits: Stage 2 | Substitute | Pool channel | Priority 40.
6.7.4 Minus
Notation: -N
De-aliases to: Plus with negated operand. -N is equivalent to +(-N).
Inherits: Stage 3 | Scale | Total channel | Priority 91.
6.7.5 Margin of Success
Notation: ms{N}
De-aliases to: Minus N, which itself de-aliases to Plus (-N). This is a transitive de-aliasing chain: ms{N} -> -N -> +(-N).
Subtracts a target number from the total to compute how far above or below the target the roll landed.
Inherits: Stage 3 | Scale | Total channel | Priority 91.
6.7.6 Multiply Total
Notation: **N
De-aliases to: Multiply at priority 100 (post-arithmetic phase).
Unlike Multiply (*N, priority 85) which runs before Plus/Minus, Multiply Total runs after all arithmetic modifiers have been applied.
Inherits: Stage 3 | Scale | Total channel | Priority 100.
6.7.7 Count Successes
Notation: S{N}
De-aliases to: Count with greaterThanOrEqual: N. Optionally S{N,B} de-aliases to Count with greaterThanOrEqual: N, lessThanOrEqual: B, deduct: true.
Counts dice meeting or exceeding threshold N. Used in dice pool systems (e.g., World of Darkness).
Inherits: Stage 3 | Reinterpret | Total channel | Priority 80.
6.7.8 Count Failures
Notation: F{N}
De-aliases to: Count with lessThanOrEqual: N.
Counts dice at or below threshold N.
Inherits: Stage 3 | Reinterpret | Total channel | Priority 80.
6.7.9 Inflation
Notation: !i
De-aliases to: Explode Sequence with the ascending TTRPG standard die set: !s{4,6,8,10,12,20,100}.
On a maximum roll, the next die in the standard ascending sequence is rolled.
Inherits: Stage 2 | Generate | Pool channel | Priority 53.
6.7.10 Reduction
Notation: !r
De-aliases to: Explode Sequence with the descending TTRPG standard die set: !s{100,20,12,10,8,6,4}.
On a maximum roll, the next die in the standard descending sequence is rolled.
Inherits: Stage 2 | Generate | Pool channel | Priority 53.
6.8 Non-Modifier Notation Features
The following features appear in notation but are not modifiers in the pipeline sense.
6.8.1 Sort
Classification: Presentation Directive
Notation: sa (ascending), sd (descending)
Priority: 95
Effect: Reorders the pool array for display purposes. Sort does not alter the total. It runs in Stage 3 by execution ordering (the pool is frozen before sort runs), but produces no arithmetic change.
Sort is the only feature that modifies the pool array in Stage 3. This is a presentation-only reordering: the set of values is unchanged, only their order differs.
6.8.2 Annotations
Classification: Notation Metadata
Notation: [text]
Priority: None (no pipeline participation)
Effect: Attaches a label or descriptor to a die expression. Annotations have no effect on rolls, pools, or totals. They are preserved for display and logging purposes.
Examples: 2d6+3[fire], 1d8[healing].
6.8.3 Repeat
Classification: Parser Directive
Notation: xN
Priority: None (pre-pipeline expansion)
Effect: Expands a single die expression into N independent roll evaluations. 4d6Lx6 is equivalent to rolling 4d6L six separate times, producing six independent results.
Repeat is resolved at parse time before the execution pipeline runs. Each repeated expression is evaluated independently with its own pool, modifiers, and total.
6.9 Modifier Composition Rules
When multiple modifiers are applied to the same die expression, the following composition rules govern their interaction:
Count + Scale: Count (priority 80) replaces the sum with a cardinality count before any Scale modifiers run. Plus (+N, priority 90), Minus (-N, priority 91), Multiply (*N, priority 85), and other Scale modifiers operate on the count result, not the original sum. Example: 5d10S{7}+2 counts successes >= 7, then adds 2 to that count.
Explosion + Filter: Explosions (Stage 2, priority 50—55) run before drop/keep (priority 65—66), so the pool is fully expanded before filtering. 4d6L! explodes first, then drops the lowest from the expanded pool — matching Roll20 and Foundry VTT conventions.
Cap + Drop: Cap (priority 10) runs before Drop (priority 65). A value that would have been dropped may first be capped, potentially changing which dice are “lowest” or “highest”. Example: 4d6C{>5}L caps all values at 5, then drops the lowest of the capped results.
Multiply + Plus: Multiply (priority 85) runs before Plus (priority 90). 2d6*2+3 doubles the sum first, then adds 3. To multiply after arithmetic, use Multiply Total (**N, priority 100): 2d6+3**2 adds 3 first, then doubles the result.
Count + Count: Multiple Count modifiers on the same expression are not supported. The behavior is undefined. Implementations SHOULD reject notation containing more than one Count-family modifier.
7. Operational Groups (Informative)
See Appendix E for the operational group reference.
8. Notation Syntax
This section provides a brief overview of notation syntax. The full syntax is defined in this specification. For a practical guide with interactive examples, see https://randsum.dev/notation/randsum-dice-notation/.
8.0 Character Encoding
RDN notation strings are sequences of Unicode scalar values. Implementations SHOULD accept UTF-8 encoded input. All syntactic tokens (d, L, H, K, C, R, V, etc.) are drawn from the ASCII subset. Annotation text ([text]) and custom face labels (d{face1,face2}) MAY contain arbitrary Unicode text excluding their respective closing delimiters (] and }). Implementations that render annotation text in HTML MUST sanitize against injection attacks (see Section 9.6).
8.1 Case Insensitivity
All RDN notation is case-insensitive. 2d8, 2D8, 2d8l, and 2D8L are equivalent. Implementations MUST treat uppercase and lowercase notation identically.
8.2 Whitespace
RDN notation strings MUST NOT contain whitespace between tokens. 4d6L is valid; 4 d6 L is not. Implementations MAY strip whitespace from input as a preprocessing convenience, but MUST NOT rely on whitespace for semantic meaning. Leading and trailing whitespace MUST be ignored. Whitespace within annotation text ([fire damage]) and custom face values (d{fire, ice}) is preserved as part of the text content.
8.3 Lexical Segmentation
RDN notation is parsed left-to-right. After the core die expression (NdS or special die type) is consumed, the remaining suffix is matched against modifier patterns in a longest-match, priority-ordered fashion. When two modifier patterns could match at the same position, the more specific pattern takes precedence (e.g., !! is matched as Compound before ! is matched as Explode; ** is matched as Multiply Total before * is matched as Multiply).
Any characters in the notation string that do not match a recognized die expression, modifier, annotation, or repeat operator MUST cause the implementation to reject the notation as invalid. Implementations MUST NOT silently discard unrecognized characters.
Disambiguation rules:
Sfollowed by{is Count Successes, not SortSfollowed byaord(not followed by{or a digit) is Sortd{...}orD{...}at the die-core position (start of expression or after+/-pool separator) is Custom Faces.D{...}at a modifier position (after a die-core has been consumed) is conditional Drop. Case-insensitive matching applies to token identity; positional context resolves the ambiguity.!!is Compound;!alone is Explode;!pis Penetrate;!s{...}is Explode Sequence;!iis Inflation;!ris Reduction**is Multiply Total;*is Multiply//is Integer Divide+NdSor-NdSafter a core die is an additional pool, not arithmetic Plus/Minus
8.4 Base Syntax
The base die expression follows the form NdS where:
Nis the quantity of dice (positive integer, default 1 if omitted)dis the die separator (literal character)Sis the number of sides (positive integer)
Special die types replace the dS portion: d%, dF, dF.2, d{...}, zN, gN, DDN.
8.5 Modifier Suffix Chaining
Modifiers are appended as suffixes to the base die expression. Multiple modifiers chain left to right in notation, but execute in priority order regardless of notation position.
4d6L!+2 -- Drop lowest, explode, add 2
-- Execution: Explode(50) -> Drop(65) -> Plus(90)
The notation order does not determine execution order. Priority ordering (Section 6.3) is the sole determinant.
8.6 Annotations
Annotations are enclosed in square brackets and may appear at the end of a die expression:
2d6+3[fire damage]
1d8[healing]
Annotations do not affect the roll result. They are metadata only.
8.7 Multi-Roll Expressions
Multiple die expressions within a single notation string are combined via arithmetic operators. + introduces an additive pool and - introduces a subtractive pool: 1d20+5+2d6 rolls 1d20, adds 5, and adds the result of 2d6.
Comma-separated notation (e.g., 1d20+5, 2d6) is NOT part of the RDN grammar. Implementations that accept multiple independent roll expressions SHOULD provide a separate API mechanism (e.g., variadic arguments, array input) rather than overloading the notation string.
8.8 Repeat Operator
The repeat operator xN expands a single expression into N independent evaluations:
4d6Lx6 -- Six independent rolls of 4d6-drop-lowest
Repeat is a parser directive resolved before the execution pipeline.
9. Safety and Limits
Implementations MUST enforce safety limits to prevent unbounded computation.
9.1 Explosion Depth
Explode, Compound, and Penetrate modifiers have a default explosion depth cap of 1000 iterations. When a depth of 0 is specified (unlimited), the implementation MUST still cap at 1000.
Implementations MAY allow configuration of this limit but MUST NOT allow it to be disabled entirely.
9.2 Geometric Die Cap
Geometric dice (gN) MUST terminate after at most 1000 iterations, even if every roll equals the maximum.
9.3 Reroll Attempts
The Reroll modifier MUST terminate after at most 99 re-roll attempts per die. If the condition is still matched after 99 attempts, the current value stands.
9.4 Pool Size Considerations
Implementations SHOULD impose a reasonable upper bound on pool size to prevent memory exhaustion. Explosion-family modifiers (Explode, Explode Sequence) can grow the pool unboundedly in theory. The explosion depth cap (Section 9.1) provides practical limits.
Implementations MAY reject die expressions where the initial pool size exceeds an implementation-defined maximum. Such limits SHOULD be documented and SHOULD be at least 1000 dice.
9.5 Error Semantics
Conforming implementations MUST reject invalid notation before execution and MUST signal an error. The error signaling mechanism is implementation-defined (exceptions, error objects, result types). Implementations MUST NOT silently ignore invalid notation or produce a result from partially-parsed input.
Examples of invalid notation that MUST be rejected:
- Malformed expressions (e.g.,
4d,d,2d6LL) - Out-of-range values (e.g.,
d0, negative pool sizes) - Notation that violates explicit constraints defined in this specification
9.6 Security Considerations
RDN notation strings MAY originate from untrusted sources (user input, network messages, database records). Implementations MUST consider the following security implications:
Denial of Service: Crafted notation can cause excessive computation. The explosion depth cap (Section 9.1), geometric die iteration cap (Section 9.2), and reroll attempt cap (Section 9.3) provide baseline protection, but implementations SHOULD impose additional limits on:
- Total pool size (number of dice across all pools)
- Repeat operator count (
xN— large N values generate many independent evaluations) - Input string length
Input Validation: Implementations MUST validate notation strings before execution. Notation strings MUST NOT be interpolated into shell commands, SQL queries, or other injection-susceptible contexts without proper escaping. The annotation feature ([text]) permits arbitrary text content — implementations that display annotations MUST sanitize them against cross-site scripting (XSS) if rendered in HTML contexts.
Resource Exhaustion: Implementations SHOULD set timeouts on notation evaluation to prevent unbounded computation from blocking other operations.
Randomness: RDN does not require cryptographically secure random number generation. Pseudorandom number generators (PRNGs) are acceptable for tabletop gaming use cases. Implementations used in contexts where roll integrity matters (e.g., gambling, competitive play) SHOULD use a CSPRNG and SHOULD document their randomness source.
Annotation Privacy: Annotation text ([text]) may contain personally identifiable information (PII) or sensitive context (e.g., character names, campaign details). Implementations that log, persist, or transmit roll results SHOULD treat annotation content as potentially sensitive and apply appropriate data handling policies.
10. Conformance Levels
Implementations MAY claim partial conformance to this specification at one of four levels. Each level is a strict superset of the previous level.
10.1 Level 1 — Core
Universal features supported by all major dice platforms (Roll20, Foundry VTT, Avrae, rpg-dice-roller, Sophie’s Dice, GNOLL, Dice Maiden).
| Feature | Notation | Level |
|---|---|---|
| Standard dice | NdS | 1 |
| Plus | +N | 1 |
| Minus | -N | 1 |
| Multiply | *N | 1 |
| Drop | L, H, D{...} | 1 |
| Keep | K, kl | 1 |
| Reroll | R{...} | 2 |
| Reroll Once | ro{...} | 2 |
| Explode | ! | 2 |
| Conditional Explode | !{condition} | 2 |
| Count | #{...} | 2 |
| Count Successes | S{N} | 2 |
| Count Failures | F{N} | 2 |
| Condition Expressions | Section 5 | 2 |
| Cap | C{...} | 3 |
| Compound | !! | 3 |
| Penetrate | !p | 3 |
| Sort | sa, sd | 3 |
| Unique | U | 3 |
| Custom Faces | d{...} | 3 |
| Keep Middle | KM | 3 |
| Replace | V{...} | 3 |
| Multiply Total | **N | 3 |
| Integer Divide | //N | 3 |
| Modulo | %N | 3 |
| Percentile dice | d% | 3 |
| Fate/Fudge dice | dF, dF.2 | 3 |
| Annotations | [text] | 3 |
| Zero-Bias dice | zN | 4 |
| Geometric dice | gN | 4 |
| Draw dice | DDN | 4 |
| Repeat | xN | 4 |
| Explode Sequence | !s{...} | 4 |
| Inflation | !i | 4 |
| Reduction | !r | 4 |
| Wild Die | W | 4 |
| Conditional Compound | !!{condition} | 4 |
| Conditional Penetrate | !p{condition} | 4 |
| Margin of Success | ms{N} | 4 |
10.2 Level 2 — VTT Baseline
Features supported by 5+ of 7 major platforms. This level covers running games on any mainstream VTT — D&D 5e, World of Darkness, Fate Core, PbtA, and most published systems. Includes all Level 1 features plus the features marked Level 2 in the table above.
10.3 Level 3 — Extended
Features supported by 2—4 major platforms. Covers Savage Worlds (Compound), Hackmaster (Penetrate), Fate Core (dF), custom dice systems, and advanced pool mechanics. Includes all Level 2 features plus the features marked Level 3 in the table above.
10.4 Level 4 — Full
RANDSUM extensions and features with 0—1 platform precedent. Includes all Level 3 features plus the features marked Level 4 in the table above.
10.5 Partial Conformance Claims
An implementation claiming conformance MUST state the level and version:
"RDN v1.0 Level 2 Conformant"
An implementation MAY support individual features from a higher level without claiming that level, provided all features of the claimed level are fully supported.
11. IANA Considerations
This document has no IANA actions at this time. A future version of this specification MAY register a media type for RDN notation strings (e.g., text/x-dice-notation) if protocol-level identification becomes necessary.
12. References
12.1 Normative References
- [BCP14] Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, March 1997; Leiba, B., “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”, BCP 14, RFC 8174, May 2017.
- [RFC5234] Crocker, D., Ed. and P. Overell, “Augmented BNF for Syntax Specifications: ABNF”, STD 68, RFC 5234, January 2008.
- [RFC7405] Kyzivat, P., “Case-Sensitive String Support in ABNF”, RFC 7405, December 2014.
12.2 Informative References
- [RFC3629] Yergeau, F., “UTF-8, a transformation format of ISO 10646”, STD 63, RFC 3629, November 2003.
- [ROLL20] Roll20 Virtual Tabletop, “Dice Reference”, https://wiki.roll20.net/Dice_Reference
- [FOUNDRYVTT] Foundry Virtual Tabletop, “Dice”, https://foundryvtt.com/article/dice/
- [AVRAE] Avrae Discord Bot, “Dice Syntax”, https://avrae.readthedocs.io/en/latest/cheatsheets/dice_reference.html
- [RPGDICEROLLER] rpg-dice-roller, https://dice-roller.github.io/documentation/
- [SOPHIESDICE] Sophie Houlden, “Dice Notation”, https://sophiehoulden.com/dice/documentation/notation.html
Appendix A: Priority Table
Complete modifier priority ordering. Modifiers execute in ascending priority order.
| Priority | Name | Stage | Verb | Channel | Status |
|---|---|---|---|---|---|
| 10 | Cap | 1 | Clamp | Pool | Primitive |
| 30 | Replace | 1 | Map | Pool | Primitive |
| 40 | Reroll | 2 | Substitute | Pool | Primitive |
| 50 | Explode | 2 | Generate | Pool | Primitive |
| 51 | Compound | 2 | Accumulate | Pool | Primitive |
| 52 | Penetrate | 2 | Accumulate | Pool | Primitive |
| 53 | Explode Sequence | 2 | Generate | Pool | Primitive |
| 55 | Wild Die | 2 | Dispatch | Pool | Macro |
| 60 | Unique | 2 | Substitute | Pool | Primitive |
| 65 | Drop | 2 | Filter | Pool | Primitive |
| 66 | Keep | 2 | Filter | Pool | Alias |
| 80 | Count | 3 | Reinterpret | Total | Primitive |
| 85 | Multiply | 3 | Scale | Total | Primitive |
| 90 | Plus | 3 | Scale | Total | Primitive |
| 91 | Minus | 3 | Scale | Total | Alias |
| 93 | Integer Divide | 3 | Scale | Total | Primitive |
| 94 | Modulo | 3 | Scale | Total | Primitive |
| 95 | Sort | 3 | — | Pool | Directive |
| 100 | Multiply Total | 3 | Scale | Total | Alias |
Appendix B: Alias De-Aliasing Table
| Alias | Notation | Primitive | De-Aliasing Rule |
|---|---|---|---|
| Keep | KN | Drop | KN on Q dice = Drop lowest (Q - N) |
| Keep (lowest) | klN | Drop | klN on Q dice = Drop highest (Q - N) |
| Keep Middle | KM | Drop | Drop lowest 1 + Drop highest 1 |
| Reroll Once | ro{...} | Reroll | Reroll with max: 1 |
| Minus | -N | Plus | Plus with negated operand: +(-N) |
| Margin of Success | ms{N} | Plus | Minus N -> Plus (-N). Transitive chain. |
| Multiply Total | **N | Multiply | Multiply at priority 100 (post-arithmetic) |
| Count Successes | S{N} | Count | Count with greaterThanOrEqual: N |
| Count Failures | F{N} | Count | Count with lessThanOrEqual: N |
| Inflation | !i | Explode Sequence | !s{4,6,8,10,12,20,100} (ascending) |
| Reduction | !r | Explode Sequence | !s{100,20,12,10,8,6,4} (descending) |
Appendix C: Full Faceted Records
Complete faceted classification for every modifier, alias, macro, and directive in RDN.
| Modifier | Notation | Status | Stage | Verb | Channel | Priority | Pool Size | Options | Group |
|---|---|---|---|---|---|---|---|---|---|
| Cap | C{...} | Primitive | 1 | Clamp | Pool | 10 | Unchanged | ComparisonOptions | Value Transformers (E.1) |
| Replace | V{...} | Primitive | 1 | Map | Pool | 30 | Unchanged | ReplaceOptions | Value Transformers (E.1) |
| Reroll | R{...} | Primitive | 2 | Substitute | Pool | 40 | Unchanged | RerollOptions | Pool Shapers (E.2) |
| Reroll Once | ro{...} | Alias -> Reroll | 2 | Substitute | Pool | 40 | Unchanged | RerollOptions (max: 1) | Pool Shapers (E.2) |
| Explode | ! | Primitive | 2 | Generate | Pool | 50 | Grows | boolean | ComparisonOptions | Explosion Variants (E.3) |
| Compound | !! | Primitive | 2 | Accumulate | Pool | 51 | Preserved | boolean | number | ComparisonOptions | Explosion Variants (E.3) |
| Penetrate | !p | Primitive | 2 | Accumulate | Pool | 52 | Preserved | boolean | number | ComparisonOptions | Explosion Variants (E.3) |
| Explode Sequence | !s{...} | Primitive | 2 | Generate | Pool | 53 | Grows | number[] | Explosion Variants (E.3) |
| Inflation | !i | Alias -> Explode Sequence | 2 | Generate | Pool | 53 | Grows | number[] (fixed: [4,6,8,10,12,20,100]) | Explosion Variants (E.3) |
| Reduction | !r | Alias -> Explode Sequence | 2 | Generate | Pool | 53 | Grows | number[] (fixed: [100,20,12,10,8,6,4]) | Explosion Variants (E.3) |
| Wild Die | W | Macro | 2 | Dispatch | Pool | 55 | Varies | boolean | Explosion Variants (E.3) |
| Unique | U | Primitive | 2 | Substitute | Pool | 60 | Unchanged | boolean | UniqueOptions | Pool Shapers (E.2) |
| Drop | L, H, D{...} | Primitive | 2 | Filter | Pool | 65 | Shrinks | DropOptions | Pool Shapers (E.2) |
| Keep | K, kl | Alias -> Drop | 2 | Filter | Pool | 66 | Shrinks | KeepOptions | Pool Shapers (E.2) |
| Keep Middle | KM | Alias -> Drop | 2 | Filter | Pool | 66 | Shrinks | (none) | Pool Shapers (E.2) |
| Count | #{...} | Primitive | 3 | Reinterpret | Total | 80 | N/A | CountOptions | Counting (E.5) |
| Count Successes | S{N} | Alias -> Count | 3 | Reinterpret | Total | 80 | N/A | CountOptions (greaterThanOrEqual: N) | Counting (E.5) |
| Count Failures | F{N} | Alias -> Count | 3 | Reinterpret | Total | 80 | N/A | CountOptions (lessThanOrEqual: N) | Counting (E.5) |
| Multiply | *N | Primitive | 3 | Scale | Total | 85 | N/A | number | Arithmetic (E.4) |
| Plus | +N | Primitive | 3 | Scale | Total | 90 | N/A | number | Arithmetic (E.4) |
| Minus | -N | Alias -> Plus | 3 | Scale | Total | 91 | N/A | number | Arithmetic (E.4) |
| Margin of Success | ms{N} | Alias -> Plus (via Minus) | 3 | Scale | Total | 91 | N/A | number | Arithmetic (E.4) |
| Integer Divide | //N | Primitive | 3 | Scale | Total | 93 | N/A | number | Arithmetic (E.4) |
| Modulo | %N | Primitive | 3 | Scale | Total | 94 | N/A | number | Arithmetic (E.4) |
| Sort | sa/sd | Presentation Directive | 3 | — | Pool (display only) | 95 | Unchanged | ’asc’ | ‘desc’ | Display (E.6) |
| Multiply Total | **N | Alias -> Multiply | 3 | Scale | Total | 100 | N/A | number | Arithmetic (E.4) |
Appendix D: Four-Gate Test for Extensions
Any proposed addition to the RANDSUM Dice Notation MUST pass all four gates:
Gate 1: Mechanical
The feature MUST operate on dice values, the dice pool, or the total. Features that do not interact with the dice pipeline are out of scope for RDN. (They may belong in game-specific specifications.)
Gate 2: Game-Agnostic Describable
The feature MUST be explainable without naming a specific game system. “Reroll dice below 3” passes. “Roll Blades in the Dark resistance” fails. Game-specific mechanics belong in game packages, not in the notation.
Gate 3: Precedented
The feature MUST be supported by at least 2 other dice platforms (Roll20, FoundryVTT, Avrae, rpg-dice-roller, etc.) OR be used by at least 3 published tabletop game systems.
Gate 4: Composable
The feature MUST work with existing modifiers without introducing a new return type, channel, or pipeline stage. It MUST be assignable to an existing verb and stage, or the proposal MUST include a justification for extending the taxonomy.
All four gates MUST pass. A feature that fails any single gate is rejected. The proposer MAY revise the feature to address the failing gate and resubmit.
Appendix E: Operational Groups (Informative)
This appendix provides a player-facing view of modifiers organized by what they do, rather than by pipeline stage. This grouping is informative and does not override the normative definitions in Section 6.
E.1 Value Transformers
Modifiers that change individual die values without adding or removing dice.
| Modifier | Notation | Effect |
|---|---|---|
| Cap | C{...} | Clamp values to boundaries |
| Replace | V{...} | Substitute specific values |
E.2 Pool Shapers
Modifiers that change which dice remain in the pool or re-randomize their values.
| Modifier | Notation | Type | Effect |
|---|---|---|---|
| Drop | L, H, D{...} | Primitive | Remove dice from pool |
| Keep | K, kl | Alias (Drop) | Keep N dice, drop the rest |
| Keep Middle | KM | Alias (Drop) | Drop lowest and highest |
| Reroll | R{...} | Primitive | Re-roll matching dice |
| Reroll Once | ro{...} | Alias (Reroll) | Re-roll matching dice at most once |
| Unique | U | Primitive | Enforce no duplicate values |
E.3 Explosion Variants
Modifiers that generate additional dice or accumulate values on trigger conditions.
| Modifier | Notation | Type | Effect |
|---|---|---|---|
| Explode | ! | Primitive | Add new die on max |
| Compound | !! | Primitive | Fold extra rolls into existing die |
| Penetrate | !p | Primitive | Fold extra rolls minus 1 |
| Explode Sequence | !s{...} | Primitive | Step through die sizes |
| Inflation | !i | Alias (Explode Sequence) | Ascending standard set |
| Reduction | !r | Alias (Explode Sequence) | Descending standard set |
| Wild Die | W | Macro | D6 System wild die dispatch |
E.4 Arithmetic
Modifiers that perform arithmetic on the total.
| Modifier | Notation | Type | Effect |
|---|---|---|---|
| Plus | +N | Primitive | Add N to total |
| Minus | -N | Alias (Plus) | Subtract N from total |
| Margin of Success | ms{N} | Alias (Plus) | Subtract target N from total |
| Multiply | *N | Primitive | Multiply total (pre-arithmetic) |
| Multiply Total | **N | Alias (Multiply) | Multiply total (post-arithmetic) |
| Integer Divide | //N | Primitive | Truncating division |
| Modulo | %N | Primitive | Remainder |
E.5 Counting
Modifiers that replace the summation model with a count.
| Modifier | Notation | Type | Effect |
|---|---|---|---|
| Count | #{...} | Primitive | Count dice matching conditions |
| Count Successes | S{N} | Alias (Count) | Count dice >= N |
| Count Failures | F{N} | Alias (Count) | Count dice <= N |
E.6 Display
Notation features that affect presentation without altering the total.
| Feature | Notation | Effect |
|---|---|---|
| Sort | sa, sd | Reorder pool ascending/descending |
Appendix F: Formal Grammar (ABNF)
This appendix provides a formal grammar for RDN notation per RFC 5234 (ABNF) with the case-sensitive string extension from RFC 7405. All alphabetic literals use %i (case-insensitive). This grammar assumes whitespace-stripped input (Section 8.2).
Six prose constraints supplement the grammar where ABNF cannot express negative lookahead or ordered alternation:
- P1:
mod-sortMUST NOT match whenS/sis immediately followed by{or a digit. Implementations MUST attemptmod-count-successesbeforemod-sort. - P2: When input contains
**followed by digits, implementations MUST matchmod-multiply-total, notmod-multiplyfollowed by stray*. - P3: When input begins with
!, implementations MUST attempt matches in order:!!(compound),!p(penetrate),!s{(explode sequence),!i(inflation),!r(reduction), then bare!(explode). - P4: When
+/-is followed by digits thendthen digits, implementations MUST parse as an additional dice pool, not arithmetic. - P5: A conforming parser MUST reject any die-expression containing more than one Count-family modifier (
#{},S{},F{}). - P6: Implementations MUST strip all whitespace from the notation string before grammatical parsing, except within annotation delimiters (
[...]) and custom face delimiters (d{...}), where whitespace is preserved as content.
;; ===================================================================
;; RANDSUM Dice Notation (RDN) v0.9.0
;; Augmented Backus-Naur Form (ABNF) per RFC 5234 / RFC 7405
;; ===================================================================
;; Top-Level
rdn-input = die-expression
die-expression = die-core *modifier [repeat] [annotation]
;; Core Die Types
die-core = draw-die / geometric-die / fate-die / zero-bias-die
/ custom-faces-die / percentile-die / standard-die
standard-die = [quantity] %i"d" positive-integer
custom-faces-die = [quantity] %i"d" "{" face-list "}"
face-list = face-value *( "," face-value )
face-value = ["-"] 1*DIGIT / 1*( ALPHA / DIGIT / "-" / "_" / SP )
geometric-die = [quantity] %i"g" positive-integer
draw-die = [quantity] %i"dd" positive-integer
percentile-die = [quantity] %i"d" "%"
fate-die = [quantity] %i"dF" ["." ("1" / "2")]
zero-bias-die = [quantity] %i"z" positive-integer
quantity = positive-integer
;; Condition Expressions
condition-block = "{" condition-list "}"
condition-list = condition *( "," condition )
condition = comparison-op integer / integer
comparison-op = ">=" / "<=" / ">" / "<" / "="
;; Modifiers (ordered by specificity for disambiguation)
modifier = mod-cap / mod-replace / mod-reroll-once / mod-reroll
/ mod-explode-seq / mod-inflation / mod-reduction
/ mod-compound / mod-penetrate / mod-explode
/ mod-wild-die / mod-unique
/ mod-drop-conditional / mod-drop-highest / mod-drop-lowest
/ mod-keep-middle / mod-keep-lowest / mod-keep
/ mod-count-successes / mod-count-failures / mod-count
/ mod-multiply-total / mod-multiply
/ mod-integer-divide / mod-modulo
/ mod-margin-of-success / mod-minus / mod-plus
/ mod-sort / mod-add-pool
;; Stage 1: Deterministic Value Shaping
mod-cap = %i"C" condition-block
mod-replace = %i"V" "{" replace-list "}"
replace-list = replace-pair *( "," replace-pair )
replace-pair = [comparison-op] non-neg-integer "=" non-neg-integer
;; Stage 2: Pool Dynamics
mod-reroll = %i"R" condition-block [non-neg-integer]
mod-reroll-once = %i"ro" condition-block
mod-explode = "!" [condition-block]
mod-compound = "!!" [non-neg-integer] [condition-block]
mod-penetrate = "!" %i"p" [non-neg-integer] [condition-block]
mod-explode-seq = "!" %i"s" "{" integer-list "}"
mod-inflation = "!" %i"i"
mod-reduction = "!" %i"r"
mod-wild-die = %i"W"
mod-unique = %i"U" ["{" non-neg-integer *("," non-neg-integer) "}"]
mod-drop-lowest = %i"L" [positive-integer]
mod-drop-highest = %i"H" [positive-integer]
mod-drop-conditional = %i"D" condition-block
mod-keep = %i"K" [positive-integer]
mod-keep-lowest = %i"kl" [positive-integer]
mod-keep-middle = %i"km"
;; Stage 3: Total Derivation
mod-count = "#" condition-block
mod-count-successes = %i"S" "{" positive-integer ["," positive-integer] "}"
mod-count-failures = %i"F" "{" positive-integer "}"
mod-multiply = "*" positive-integer
mod-plus = "+" non-neg-integer
mod-minus = "-" non-neg-integer
mod-integer-divide = "//" positive-integer
mod-modulo = "%" positive-integer
mod-sort = %i"sa" / %i"sd"
mod-multiply-total = "**" positive-integer
mod-margin-of-success = %i"ms" "{" positive-integer "}"
;; Additional Pool
mod-add-pool = ("+" / "-") positive-integer %i"d" positive-integer
;; Non-Modifier Features
repeat = %i"x" positive-integer
annotation = "[" 1*( %x20-5C / %x5E-7E / UTF8-non-ascii ) "]"
;; Unicode scalar values above ASCII, excluding surrogates
UTF8-non-ascii = %x80-D7FF / %xE000-10FFFF
;; Numeric Primitives
positive-integer = %x31-39 *DIGIT
non-neg-integer = "0" / positive-integer
integer = ["-"] non-neg-integer
integer-list = positive-integer *( "," positive-integer )
DIGIT = %x30-39
ALPHA = %x41-5A / %x61-7A
SP = %x20
WSP = SP / %x09
Appendix G: Conformance Test Vectors
The following test vectors enable independent implementors to verify conformance with RDN v0.9.0. Each vector specifies a notation input, fixed dice values (the raw values the RNG would produce, in order), the expected pool after all modifiers, and the expected total.
Conventions:
- “Seed/Rolls” lists the raw die values produced by the RNG, in order consumed
- “Expected Pool” is the pool array after all modifiers have executed
- “Expected Total” is the scalar result after total derivation
- “ERROR” means the implementation MUST reject the notation
G.1 Dice Expressions
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 1 | 1d20 | [14] | [14] | 14 | 4.1 |
| 2 | d20 | [14] | [14] | 14 | 4.1 |
| 3 | 3d6 | [3, 5, 2] | [3, 5, 2] | 10 | 4.1 |
| 4 | d% | [73] | [73] | 73 | 4.5.1 |
| 5 | 4dF | [-1, 0, 1, 1] | [-1, 0, 1, 1] | 1 | 4.5.2 |
| 6 | 1dF.2 | [-2] | [-2] | -2 | 4.5.2 |
G.2 Stage 1 Modifiers
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 7 | 4d6C{5} | [6, 3, 5, 6] | [5, 3, 5, 5] | 18 | 6.4.2 |
| 8 | 4d6V{1=6,3=5} | [1, 3, 4, 1] | [6, 5, 4, 6] | 21 | 6.4.3 |
G.3 Stage 2 Modifiers
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 9 | 4d6R{1} | [1, 3, 5, 2] reroll: [4] | [4, 3, 5, 2] | 14 | 6.5.2 |
| 10 | 4d6L | [3, 5, 2, 6] | [3, 5, 6] | 14 | 6.5.4 |
| 11 | 4d6H | [3, 5, 2, 6] | [3, 5, 2] | 10 | 6.5.4 |
| 12 | 4d6! | [6, 3, 5, 2] explode: [4] | [6, 3, 5, 2, 4] | 20 | 6.5.6 |
| 13 | 4d6! | [3, 4, 2, 5] | [3, 4, 2, 5] | 14 | 6.5.6 |
G.4 Stage 3 Modifiers
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 14 | 2d6+5 | [3, 4] | [3, 4] | 12 | 6.6.4 |
| 15 | 2d6-3 | [4, 5] | [4, 5] | 6 | 6.7.4 |
| 16 | 2d6*3 | [2, 4] | [2, 4] | 18 | 6.6.3 |
| 17 | 2d6//3 | [4, 5] | [4, 5] | 3 | 6.6.5 |
| 18 | 5d10#{>=7} | [8, 3, 10, 7, 2] | [8, 3, 10, 7, 2] | 3 | 6.6.2 |
G.5 Aliases
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 19 | 4d6K3 | [2, 5, 3, 6] | [5, 3, 6] | 14 | 6.7.1 |
| 20 | 4d6kl | [2, 5, 3, 6] | [2] | 2 | 6.7.1 |
| 21 | 5d10S{7} | [8, 3, 10, 7, 2] | [8, 3, 10, 7, 2] | 3 | 6.7.7 |
| 22 | 5d10F{3} | [8, 3, 10, 1, 2] | [8, 3, 10, 1, 2] | 3 | 6.7.8 |
| 23 | 1d20ms{15} | [18] | [18] | 3 | 6.7.5 |
G.6 Non-Modifier Features
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 24 | 4d6sa | [3, 1, 5, 2] | [1, 2, 3, 5] | 11 | 6.8.1 |
| 25 | 2d6+3[fire] | [4, 5] | [4, 5] | 12 | 6.8.2 |
G.7 Case Insensitivity
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 26 | 2D6 | [3, 4] | [3, 4] | 7 | 8.1 |
| 27 | 4D6l | [3, 5, 2, 6] | [3, 5, 6] | 14 | 8.1 |
G.8 Error Cases
| # | Notation | Expected | Section |
|---|---|---|---|
| 28 | 4d | ERROR | 9.5 |
| 29 | d | ERROR | 9.5 |
| 30 | d0 | ERROR | 9.5 |
G.9 Extended Coverage
| # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section |
|---|---|---|---|---|---|
| 31 | 2d{fire,ice,lightning} | [face2=ice, face1=fire] | [ice, fire] | N/A (string faces) | 4.2 |
| 32 | g6 | [6, 6, 3] | [6, 6, 3] | 15 | 4.3 |
| 33 | 3DD6 | [4, 1, 6] | [4, 1, 6] | 11 | 4.4 |
| 34 | z6 | [0] | [0] | 0 | 4.5.3 |
| 35 | 3d6U | [3, 3, 5] reroll: [4] | [3, 4, 5] | 12 | 6.5.3 |
| 36 | 2d6!! | [6, 3] compound: [4] | [10, 3] | 13 | 6.5.8 |
| 37 | 2d6!p | [6, 3] penetrate: [3] | [8, 3] | 11 | 6.5.9 |
| 38 | 5d6W | [6, 3, 4, 2, 5] | [6, 3, 4, 2, 5] | 20 | 6.5.10 |
| 39 | 3d6!s{4,6,8} | [4, 2, 5] sequence: [6] | [4, 2, 5, 6] | 17 | 6.5.7 |
| 40 | 2d6%4 | [3, 5] | [3, 5] | 0 | 6.6.6 |
| 41 | 2d6**2 | [3, 4] | [3, 4] | 14 | 6.7.6 |
| 42 | 4d6C{5}L!+2 | [6, 1, 5, 6] cap: [5, 1, 5, 5] explode: [3] drop: [1, 5, 5, 5, 3] -> drop lowest | depends on priority | 6.9 | |
| 43 | 4d6ro{1} | [1, 3, 5, 4] reroll: [2] | [2, 3, 5, 4] | 14 | 6.7.3 |
| 44 | 6d6KM | [1, 4, 3, 5, 2, 6] | [4, 3, 5, 2] | 14 | 6.7.2 |
| 45 | 5d10#{>=7,<=2} | [8, 1, 10, 7, 2] | [8, 1, 10, 7, 2] | 1 | 6.6.2 |
G.10 Additional Error Cases
| # | Notation | Expected | Section |
|---|---|---|---|
| 46 | 0d6 | ERROR | 9.5 |
| 47 | 5d10S{7}F{3} | ERROR (multiple Count modifiers) | 6.9 |
| 48 | 4d6 L | ERROR (whitespace between tokens) | 8.2 |
G.11 Conformance Level Cross-Index
Implementors can verify conformance at each level using the following vector subsets:
| Level | Required Vectors | Features Covered |
|---|---|---|
| Level 1 (Core) | 1-3, 10-11, 14-16, 19-20, 26-27 | NdS, +, -, *, L, H, K, kl |
| Level 2 (VTT Baseline) | All Level 1 + 7, 9, 12-13, 18, 21-22, 25, 35, 40, 43, 45 | R, ro, !, !{}, #, S{}, F{}, conditions |
| Level 3 (Extended) | All Level 2 + 4-6, 8, 23, 31, 34, 36-37, 41, 44 | C, !!, !p, U, d{}, dF, KM, V{}, **, //, %, d% |
| Level 4 (Full) | All Level 3 + 32-33, 38-39 | gN, DDN, W, !s{}, zN |
| Error Cases | 28-30, 46-48 | Invalid notation rejection |
Authors’ Addresses
Alex Jarvis RANDSUM Email: alxjrvs@gmail.com URI: https://github.com/alxjrvs
End of RANDSUM Dice Notation Specification v0.9.0 rev.1