# RANDSUM Dice Notation Specification **Version:** 0.9.0 (revision 5) **Status:** Draft Community Specification **Date:** 2026-10-31 **Editor:** Alex Jarvis ## 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. ## Status of This Document 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 document 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 [specification repository](https://github.com/RANDSUM/notation-spec/issues). Sections and appendices marked **(Informative)** are non-normative. Examples, notes and tables of contents are informative. Everything else is normative. ## 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, and the badges that advertise them 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](https://www.rfc-editor.org/info/bcp14) ([RFC 2119](https://www.rfc-editor.org/rfc/rfc2119), [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174)) 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. While the specification is a draft, a revision corrects a version in place and is listed in Section 1.5; a conformance claim names the version, not the revision. ### 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. | | 0.9.0 rev. 2 | 2026-10-31 | Audit against the reference implementation. Whitespace (8.2), multi-pool expressions (8.7), annotations (8.6) and repeat scope (6.8.3) now match the parser; Inflation and Reduction start past the die's own size (6.7.9, 6.7.10); Keep Middle takes a count and runs as Drop (6.7.2); conditional Drop may empty the pool (6.5.4); Unique's exemption list and its error (6.5.3); a modifier that sets a value appears once per expression (8.3); the ABNF top level, conditions and faces corrected (Appendix F); feature levels made consistent with the vectors, Conditional Drop moved to Level 2 (Section 10); conformance badges (10.7); vector 39 corrected and vector 55 listed (Appendix G). Explode, Compound and Penetrate chain by default, and Penetrate subtracts 1 with no minimum (6.5.6, 6.5.8, 6.5.9, 9.1); Reroll Once rerolls each matching die once (6.7.3); vectors 56 to 59 cover them. | | 0.9.0 rev. 3 | 2026-10-31 | The specification, its vectors and a conformance runner move to their own repository, github.com/RANDSUM/notation-spec, under CC BY 4.0 (text) and MIT (vectors and runner). Vectors 31, 33 and 51 gain an `unseeded` field that states what a result must satisfy when the seed cannot be replayed, replacing the multiset comparison; the vectors file gains a JSON Schema; the conformance runner and its adapter protocol (11.3). No vector changes its expected result. | | 0.9.0 rev. 4 | 2026-10-31 | The specification is published at its own address, https://notation.randsum.dev/. The badge URLs (10.7), the conformance vectors' location and `$id` (11.3) and the JSON Schema move with it; the old randsum.dev addresses redirect. No normative change. | | 0.9.0 rev. 5 | 2026-11-01 | A geometric die takes only Scale modifiers (4.3): a modifier that reads or changes individual dice, such as `g6L` or `3g6#{>6}`, MUST be rejected; error vectors 60 and 61 cover it. Custom Faces dice MAY take a quantity, as Section 4.2 and vector 31 already show (4.6 said otherwise). | --- ## 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: 1. **Derivation Status** -- whether the feature is primitive, alias, or macro 2. **Pipeline Stage** -- when the modifier executes (Stage 1, 2, or 3) 3. **Operational Verb** -- what the modifier does (Clamp, Map, Filter, etc.) 4. **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: 1. **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. 2. **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. 3. **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. An alias MAY run at its own priority (Keep at 66, Minus at 91, Multiply Total at 100); Keep Middle has none of its own and runs as Drop at 65. ### 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. A face is any text other than `,` and `}`; surrounding whitespace is trimmed. Every face MUST be non-empty: `d{a,}` MUST be rejected. When every face is an integer (optionally negative), the faces are numeric and the die sums like any other. Otherwise every face is a string, the roll result contains the string values, and the pool has no meaningful numeric total: a conforming implementation MUST NOT present a sum of string faces as the roll's result. (The reference implementation reports the faces alongside a total of their 1-based positions.) ### 4.3 Geometric Dice (gN) **Classification:** Primitive | Property | Value | | ---------------- | --------------------------------------------- | | Notation | `gN`, `QgN` where Q is quantity (default 1) | | 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. `g1` therefore always reaches the cap. A geometric pool holds every roll of every sequence, not one value per die, so a geometric die takes only the Scale modifiers (Section 3.7: Multiply, Plus, Minus, Integer Divide, Modulo, Multiply Total), which act on the total. Any other modifier, which reads or changes individual dice (Cap, Replace, Reroll, Unique, Explode and its relatives, Drop, Keep, Wild Die, Count, Sort), MUST be rejected: `g6L` and `3g6#{>6}` are errors, while `g6+3` and `2g6*2` are valid. ### 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` | Modifiers apply to every die type (`die-core *modifier`): `4dF+1`, `d%+5` and `3DD6L` are valid. A geometric die takes only Scale modifiers (Section 4.3). A modifier that triggers on the maximum face without a condition (Explode `!`, Compound `!!`, Penetrate `!p`, Explode Sequence `!s{}`/`!i`/`!r`, Wild Die `W`) MUST be rejected on Fate, Zero-Bias and Custom Faces dice, whose faces are not [1..N]. A conditional form such as `4dF!{>0}` is valid. Every die type MAY take a quantity: `2d%`, `3dF`, `3z6`, `3g6`, `3DD6` and `2d{a,b,c}` are valid. --- ## 5. Condition Expressions Condition expressions are a shared sub-grammar used by Cap, Drop, Reroll, Count, and the conditional forms of Explode, Compound and Penetrate. Replace uses a single condition as the left side of each rule (Section 6.4.3). ### 5.1 Operator Syntax A condition consists of an optional comparison operator followed by a non-negative integer operand. Negative operands are not part of this version: on a Fate die, match −1 with `<0` rather than `=-1`. | Operator | Meaning | Example | | ------------------ | ----------------------------------- | ------- | | `>N` | Greater than N | `>5` | | `=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`. | | **Explode**, **Compound**, **Penetrate** (conditional forms) | Exact match: trigger on 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: 1. **Stage 1 -- Deterministic Value Shaping** (priority 10--39): Pure value transformations of the pool array. No random number generation. No pool size changes. 2. **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. 3. **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. **Bounds:** Each condition sets one bound, and a die beyond it is set to the operand: | Condition | Effect on each die | | --------- | ------------------ | | `>N`, `>=N`, `=N`, bare `N` | Upper bound: a die above N becomes N | | `5,<2}` on [6, 1, 3, 2] gives [5, 2, 3, 2]. **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,...}`, where `from` is a non-negative integer or a single condition (`V{>4=6}`) and `to` is a non-negative integer **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 or condition, 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 = true` for 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, optional `once` flag) **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: dice are taken in pool order, and once N dice have changed value the rest are left as rolled. With `once` (Reroll Once, Section 6.7.3), each matching die is re-rolled a single time and the new value stands. An exact (bare or `=N`) value that the die cannot show MUST be rejected: `1d6R{7}` is an error. **Pool size:** Unchanged. **Aliases:** Reroll Once (Section 6.7.3). #### 6.5.3 Unique `Primitive | Stage 2 | Substitute | Pool channel | Priority 60` **Notation:** `U`, `U{N1,N2,...}` **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 value is re-rolled until it is unique. If no unique value is found within the reroll safety limit (Section 9.3), the implementation MUST signal an error rather than return a pool with duplicates. `U` applies to all dice. `U{N1,N2,...}` exempts the listed values, which MAY repeat: `4d6U{1}` allows several 1s. A pool with more dice than the die has faces cannot be unique and MUST be rejected: `7d6U` is an error. **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. Repeated positional drops add up: `4d6LL` drops the two lowest, as does `4d6L2`. Drop runs after all explosion-family modifiers (priority 50--55), so the filter operates on the fully expanded pool. **Pool size:** Shrinks. **Validation:** The number of dice dropped by position (`L`, `H`, and Keep Middle) MUST be less than the pool size; `2d6L2` is an error. A conditional drop depends on the roll, so it MAY empty the pool: `2d6D{>=1}` yields an empty pool with a total of 0. **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. **Validation:** N MUST be at least 1 and at most the pool size: `4d6K0` and `2d6K3` are errors. **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. Explosions chain: a new die that also triggers adds another, until a new die does not trigger or the depth limit is reached (Section 9.1). `4d6!` on [6, 6, 1, 1] with explosion rolls [6, 2, 3] gives [6, 6, 1, 1, 6, 2, 3]: the first 6 explodes into a 6, which explodes into a 2, and the second 6 explodes into a 3. **Conditional Explode** (`!{condition}`): When a condition expression is provided, the explosion triggers on values matching the condition instead of only on the maximum value, and new dice chain on the same condition. Bare `!` with no condition defaults to triggering on the die's maximum face value. **Pool size:** Grows (one new die per triggered explosion). **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:** Each die showing its maximum value starts the sequence: the first listed size is rolled and appended to the pool. If that die also shows its maximum, the next size is rolled, and so on. When the sequence is exhausted, the last die size repeats (capped by the explosion depth limit). Every size MUST be a positive integer: `!s{0}` is an error. **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` and `0` = unlimited up to the safety cap, integer N = at most 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. Rolls keep being added while the added roll is also a maximum: bare `!!` and `!!0` up to the safety cap (Section 9.1), and `!!N` up to N rolls. `2d6!!` on [6, 1] with compound rolls [6, 6, 2] gives [20, 1]. **Conditional Compound** (`!!{condition}`): When a condition expression is provided, the compound triggers on values matching the condition instead of only on the maximum value, and keeps adding while the added roll matches. A depth and a condition MUST NOT be combined: `!!2{>4}` is an error. **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 added roll has 1 subtracted from it before accumulation, so an added 1 contributes nothing. Whether the chain continues is decided by the added roll before the subtraction. Depth follows Compound: bare `!p` and `!p0` continue while the added roll is also a maximum, up to the safety cap, and `!pN` up to N rolls. `2d6!p` on [6, 1] with penetrate rolls [6, 6, 1] gives [16, 1]. **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, and continues while the added roll matches. A depth and a condition MUST NOT be combined: `!p2{>4}` is an error. **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, or to empty when the wild die is the only die. - **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): `1d6-7//2` on [1] is −3, not −4. N MUST be a positive integer; `//0` is an error. **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 truncating division by N, which takes the sign of the total: `1d6-8%3` on [1] is −1. N MUST be a positive integer; `%0` is an error. **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`, `KMn` **De-aliases to:** Drop lowest n + Drop highest n (n defaults to 1). Removes the n lowest and the n highest dice from the pool, keeping only the middle values. It adds to any other positional drop: `5d6KML` drops the lowest two and the highest one. **Inherits:** Stage 2 | Filter | Pool channel | Priority 65 (it is a Drop). #### 6.7.3 Reroll Once **Notation:** `ro{condition-list}` **De-aliases to:** Reroll with `once: true` (Section 6.5.2): each matching die is rerolled at most once. Every die that matches is rerolled one time, and the new result stands even if it matches again: `4d6ro{1}` on [1, 1, 3, 4] with rerolls [1, 2] gives [1, 2, 3, 4]. **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 over the standard die set {4, 6, 8, 10, 12, 20, 100}, keeping only the sizes larger than the die's own, in ascending order. On a d6, `!i` is `!s{8,10,12,20,100}`; on a d20 it is `!s{100}`; on a d100 there is no larger size and `!i` does nothing. On a maximum roll, the next larger standard die is rolled; past d100, d100 repeats. **Inherits:** Stage 2 | Generate | Pool channel | Priority 53. #### 6.7.10 Reduction **Notation:** `!r` **De-aliases to:** Explode Sequence over the standard die set, keeping only the sizes smaller than the die's own, in descending order. On a d20, `!r` is `!s{12,10,8,6,4}`; on a d6 it is `!s{4}`; on a d4 there is no smaller size and `!r` does nothing. Reduction takes no condition block. `!r` followed by `{` or `o{` is Explode then Reroll (Section 8.3). On a maximum roll, the next smaller standard die is rolled; below d4, d4 repeats. **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. A die expression takes at most one annotation, after its die core or any of its modifiers: `2d6+3[fire]` and `2d6[fire]+3` are equivalent, and `2d6[a][b]` MUST be rejected. Examples: `2d6+3[fire]`, `1d8[healing]`, `1d20+5+2d6[fire]`. #### 6.8.3 Repeat **Classification:** Parser Directive **Notation:** `xN` **Priority:** None (pre-pipeline expansion) **Effect:** Expands the whole notation string into N independent roll evaluations. `4d6Lx6` is equivalent to rolling `4d6L` six separate times, producing six independent results; `1d20+2d6x3` repeats both pools three times. Repeat is resolved at parse time before the execution pipeline runs. Each repeated expression is evaluated independently with its own pool, modifiers, and total. Repeat MUST come last, after every pool, modifier and annotation; `2d6x2+3` MUST be rejected. Consecutive repeats multiply: `4d6x2x3` is six evaluations. N MUST be a positive integer. ### 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:** A die expression MUST NOT contain more than one Count-family modifier (`#{}`, `S{}`, `F{}`); implementations MUST reject it (Appendix F, P5). **Repeated modifiers:** Within one die expression, a modifier that sets a single value MUST appear at most once. Writing it twice (`2d6*2*3`, `4d6V{1=6}V{2=6}`, `4d6sasd`, `4d6!i!r`) MUST be rejected rather than resolved by keeping one occurrence. The forms whose repeats add up MAY repeat: Plus, Minus, Margin of Success, and positional Drop (`L`, `H`). A flag with no operand (`!`, `!!`, `!p`, `!i`, `!r`, `W`, `U`, `sa`, `sd`) MAY be written twice to the same effect: `4d6!L!` explodes once. --- ## 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, and `4 d6 L` and `4d6 L` MUST be rejected. Leading and trailing whitespace MUST be ignored. Whitespace within annotation text (`[fire damage]`) is preserved as part of the text content; whitespace around a custom face (`d{fire, ice}`) is trimmed, and whitespace inside one is kept. ### 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, nor a recognized token whose meaning another token overrides (Section 6.9, Repeated modifiers). **Disambiguation rules:** - `S` followed by `{` is Count Successes, not Sort - `S` followed by `a` or `d` (not followed by `{` or a digit) is Sort - `d{...}` or `D{...}` 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; `!p` is Penetrate; `!s{...}` is Explode Sequence; `!i` is Inflation; `!r` is Reduction - `!R{...}` or `!ro{...}` is Explode followed by Reroll (or Reroll Once), not Reduction: Reduction takes no condition block - `!RR{...}` is Reduction (`!R`) followed by Reroll (`R{...}`) - `**` is Multiply Total; `*` is Multiply - `//` is Integer Divide - `+` or `-` followed by a die core (`+2d6`, `-d4`, `+4dF`, `+d%`) starts an additional pool (Section 8.7); followed by digits alone it is arithmetic Plus/Minus ### 8.4 Base Syntax The base die expression follows the form `NdS` where: - `N` is the quantity of dice (positive integer, default 1 if omitted) - `d` is the die separator (literal character) - `S` is 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. A die expression takes at most one, after its die core or any of its modifiers, and before a repeat: ``` 2d6+3[fire damage] 2d6[fire damage]+3 1d8[healing] 4d6L[strength]x6 ``` 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. The first pool MAY carry a sign (`-1d6`). Each pool is evaluated with its own modifiers, which bind to the pool before them: in `1d20+5+2d6L` the `+5` belongs to the d20 and the `L` to the 2d6. Any die type may start a pool: `1d20+4dF`, `2d6-g6`. The notation's total is the signed sum of the pool totals. 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 the whole notation string into N independent evaluations: ``` 4d6Lx6 -- Six independent rolls of 4d6-drop-lowest ``` Repeat is a parser directive resolved before the execution pipeline. It MUST come last (Section 6.8.3). --- ## 9. Safety and Limits Implementations MUST enforce safety limits to prevent unbounded computation. ### 9.1 Explosion Depth Explode MUST add at most **1000** new dice per original die. Compound and Penetrate MUST stop after at most 1000 additional rolls per die, including when no depth or a depth of 0 (unlimited) is specified, and Explode Sequence after at most the sequence's length plus 1000 rolls per triggering die. 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. > **Note:** The reference implementation (Section 11.5) rejects input longer than 1000 characters, a quantity above 10,000, sides above 1,000,000, and a repeat count above 1000. ### 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`, `2d6+`, `4d6R`) - 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 conformance to this specification at one of four levels. Each level is a strict superset of the previous level. The levels follow how widely a feature is supported by the dice platforms named in Section 13.2, so that Level 2 is enough to run most published games. ### 10.1 Feature Levels Table 10.1 is normative: it assigns every feature of this specification to exactly one level. | Feature | Notation | Section | Level | | -------------------------------- | ---------------------------- | ------------ | ----- | | Standard dice | `NdS` | 4.1 | 1 | | Additional pools | `+NdS`, `-NdS`, leading `-` | 8.7 | 1 | | Plus | `+N` | 6.6.4 | 1 | | Minus | `-N` | 6.7.4 | 1 | | Multiply | `*N` | 6.6.3 | 1 | | Drop lowest / highest | `L`, `Ln`, `H`, `Hn` | 6.5.4 | 1 | | Keep highest / lowest | `K`, `Kn`, `kl`, `klN` | 6.7.1 | 1 | | Case insensitivity, whitespace | (lexical) | 8.1, 8.2 | 1 | | Rejecting invalid notation | (errors) | 9.5 | 1 | | Condition expressions | `{>=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 | Standard sizes above S, ascending | | Reduction | `!r` | Explode Sequence | Standard sizes below S, 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 (once: true) | 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[] (standard sizes above S) | Explosion Variants (E.3) | | Reduction | `!r` | Alias -> Explode Sequence | 2 | Generate | Pool | 53 | Grows | number[] (standard sizes below S) | Explosion Variants (E.3) | | Wild Die | `W` | Macro | 2 | Dispatch | Pool | 55 | Varies | boolean | Explosion Variants (E.3) | | Unique | `U`, `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`, `KMn` | Alias -> Drop | 2 | Filter | Pool | 65 | Shrinks | DropOptions (lowest: n, highest: n) | 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](https://www.rfc-editor.org/rfc/rfc5234) (ABNF) with the case-sensitive string extension from [RFC 7405](https://www.rfc-editor.org/rfc/rfc7405). All alphabetic literals use `%i` (case-insensitive). This grammar describes input with leading and trailing whitespace removed (Section 8.2). Seven prose constraints supplement the grammar where ABNF cannot express negative lookahead, ordered alternation, or counting: - **P1:** `mod-sort` MUST NOT match when `S`/`s` is immediately followed by `{` or a digit. Implementations MUST attempt `mod-count-successes` before `mod-sort`. - **P2:** When input contains `**` followed by digits, implementations MUST match `mod-multiply-total`, not `mod-multiply` followed 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). `mod-reduction` MUST NOT match when `r`/`R` is immediately followed by `{` or `o{`: there the `!` is a bare Explode and the rest is Reroll or Reroll Once, so `4d6!R{<3}` is Explode then Reroll, while `1d20!RR{<3}` is Reduction then Reroll. - **P4:** A sign followed by a `die-core` starts a new `pool`; a sign followed by digits that do not begin a `die-core` is `mod-plus` or `mod-minus`. `+3d6` is a pool and `+3` is Plus. - **P5:** A conforming parser MUST reject any `pool` containing more than one Count-family modifier (`#{}`, `S{}`, `F{}`). - **P6:** Within one `pool`, a modifier MUST NOT appear twice unless it is `mod-plus`, `mod-minus`, `mod-margin-of-success`, `mod-drop-lowest` or `mod-drop-highest`, or both occurrences are the same operand-free flag (Section 6.9). `mod-explode-seq`, `mod-inflation` and `mod-reduction` count as one modifier, as do `mod-explode` with and without a condition block. A `pool` takes at most one `annotation`. - **P7:** Whitespace MUST NOT appear between tokens. It is part of the token only inside an `annotation` and inside a `face-value`, where whitespace around the face is trimmed. ```abnf ;; =================================================================== ;; RANDSUM Dice Notation (RDN) v0.9.0, revision 4 ;; Augmented Backus-Naur Form (ABNF) per RFC 5234 / RFC 7405 ;; =================================================================== ;; Top-Level rdn-input = expression *repeat expression = [sign] pool *( sign pool ) pool = die-core *( modifier / annotation ) sign = "+" / "-" ;; 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*face-char ; not whitespace only (P7) face-char = %x20-2B / %x2D-7C / %x7E / UTF8-non-ascii ; any text but "," and "}" 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] non-neg-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 ;; 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 = condition "=" 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" [non-neg-integer] mod-drop-highest = %i"H" [non-neg-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" [positive-integer] ;; Stage 3: Total Derivation mod-count = "#" condition-block mod-count-successes = %i"S" "{" non-neg-integer ["," non-neg-integer] "}" mod-count-failures = %i"F" "{" non-neg-integer "}" mod-multiply = "*" non-neg-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 = "**" non-neg-integer mod-margin-of-success = %i"ms" "{" non-neg-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 = 1*DIGIT integer-list = positive-integer *( "," positive-integer ) DIGIT = %x30-39 ``` --- ## 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}` | [6, 2, 5] sequence: [3] (on the d4) | [6, 2, 5, 3] | 16 | 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], no 6 left to explode, drop lowest | [5, 5, 5] | 17 | 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 Modifiers on Special Dice | # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section | | --- | -------- | ------------- | ------------- | -------------- | ------- | | 49 | `4dF+1` | [-1, 0, 1, 1] | [-1, 0, 1, 1] | 2 | 4.5.2 | | 50 | `d%+5` | [73] | [73] | 78 | 4.5.1 | | 51 | `3DD6L` | [4, 1, 6] | [4, 6] | 10 | 4.4 | ### G.12 Divisor and Face Error Cases | # | Notation | Expected | Section | | --- | -------- | ------------------------------------- | ------- | | 52 | `2d6//0` | ERROR (zero divisor) | 6.6.5 | | 53 | `2d6%0` | ERROR (zero divisor) | 6.6.6 | | 54 | `4dF!` | ERROR (max-face explosion on dF) | 4.6 | | 60 | `g6L` | ERROR (pool modifier on a geometric die) | 4.3 | | 61 | `3g6#{>6}` | ERROR (pool modifier on a geometric die) | 4.3 | ### G.13 Lexical Disambiguation | # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section | | --- | ---------- | ------------------------------------ | --------------- | -------------- | ------- | | 55 | `4d6!R{1}` | [1, 3, 6, 2] reroll: [4] explode: [5] | [4, 3, 6, 2, 5] | 20 | 8.3 | ### G.14 Chaining and Reroll Once | # | Notation | Seed/Rolls | Expected Pool | Expected Total | Section | | --- | ---------- | ----------------------------- | ------------------ | -------------- | ------- | | 56 | `3d6!` | [6, 4, 6] explode: [6, 2, 3] | [6, 4, 6, 6, 2, 3] | 27 | 6.5.6 | | 57 | `2d6!!` | [6, 1] compound: [6, 6, 2] | [20, 1] | 21 | 6.5.8 | | 58 | `2d6!p` | [6, 1] penetrate: [6, 6, 1] | [16, 1] | 17 | 6.5.9 | | 59 | `4d6ro{1}` | [1, 1, 3, 4] reroll: [1, 2] | [1, 2, 3, 4] | 10 | 6.7.3 | ### G.15 Conformance Level Cross-Index Each vector's level is the highest level among the features it uses (Table 10.1). A level requires its own vectors and those of every lower level. | Level | Required Vectors | Features Covered | | ---------------------- | -------------------------------------------------------------------- | -------------------------------------------------------- | | Level 1 (Core) | 1-3, 10-11, 14-16, 19-20, 26-27 | NdS, +, -, \*, L, H, K, kl, case | | Level 2 (VTT Baseline) | All Level 1 + 9, 12-13, 18, 21-22, 43, 45, 55-56, 59 | R, ro, !, #, S{}, F{}, conditions | | Level 3 (Extended) | All Level 2 + 4-8, 17, 24-25, 31, 35-37, 40-42, 44, 49-50, 57-58 | d%, dF, C, V, //, sa, [text], d{}, U, !!, !p, %, \*\*, KM | | Level 4 (Full) | All Level 3 + 23, 32-34, 38-39, 51 | ms{}, gN, DDN, zN, W, !s{} | | Error Cases | 28-30, 46-48, 52-54, 60-61 (required at every level) | 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, revision 4_