DEL Specification for AI Systems
Machine-readable specification for the Dice Expression Language. Optimized for language models and AI assistants.
# DICE EXPRESSION LANGUAGE (DEL) SPECIFICATION
# Version: 1.0
# Purpose: AI-readable specification for parsing and understanding DEL expressions
# Target: Language models, AI assistants, code generators
================================================================================
OVERVIEW
================================================================================
DEL (Dice Expression Language) is a domain-specific language for expressing
tabletop RPG dice mechanics. It supports dice notation, arithmetic, comparisons,
conditionals, and mathematical functions.
All arithmetic uses INTEGER MATH. Division floors the result.
================================================================================
GRAMMAR (EBNF-style)
================================================================================
expression → conditional
conditional → comparison ( "?" expression ":" expression )?
comparison → sum ( ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) sum )?
sum → product ( ( "+" | "-" ) product )*
product → power ( ( "*" | "/" | "%" ) power )*
power → unary ( "^" unary )*
unary → ( "-" )? call
call → IDENTIFIER "(" arguments? ")" | primary
arguments → expression ( "," expression )*
primary → NUMBER | dice | "(" expression ")"
dice → NUMBER? "d" NUMBER modifiers*
================================================================================
TOKEN TYPES
================================================================================
NUMBER → [0-9]+ (integers only, no decimals)
IDENTIFIER → [a-z]+ (function names: min, max, abs, floor, ceil, median)
OPERATOR → + | - | * | / | % | ^ | == | != | < | <= | > | >= | ? | :
PAREN → ( | )
COMMA → ,
DICE → [0-9]*d[0-9]+ (e.g., d20, 3d6, 10d10)
================================================================================
DICE NOTATION
================================================================================
Format: NdX
- N = number of dice (optional, defaults to 1)
- d = literal character 'd'
- X = number of sides on each die
Examples:
d20 → roll 1 twenty-sided die (implicit N=1)
1d20 → roll 1 twenty-sided die (explicit)
3d6 → roll 3 six-sided dice and sum
4d4 → roll 4 four-sided dice and sum
10d10 → roll 10 ten-sided dice and sum
Each die produces a uniformly random integer in range [1, X].
The base result is the SUM of all dice rolled.
================================================================================
DICE MODIFIERS
================================================================================
Modifiers are appended directly after dice notation (no spaces).
They are processed in this FIXED ORDER regardless of how they're written:
1. Reroll
2. Explode
3. Keep/Drop
4. Sort
5. Success Count
--------------------------------------------------------------------------------
KEEP MODIFIERS
--------------------------------------------------------------------------------
kh<N> → Keep Highest N dice, sum only those
kl<N> → Keep Lowest N dice, sum only those
dh<N> → Drop Highest N dice, sum the rest
dl<N> → Drop Lowest N dice, sum the rest
Examples:
4d6kh3 → Roll 4d6, keep the 3 highest, sum them (D&D ability scores)
2d20kh1 → Roll 2d20, keep highest 1 (D&D advantage)
2d20kl1 → Roll 2d20, keep lowest 1 (D&D disadvantage)
5d10dh1 → Roll 5d10, drop the highest, sum remaining 4
3d8dl1 → Roll 3d8, drop the lowest, sum remaining 2
--------------------------------------------------------------------------------
EXPLODE MODIFIER
--------------------------------------------------------------------------------
! → Explode on maximum value (roll again if max, can chain)
!<N> → Explode on exactly N
!<OP><N> → Explode when comparison is true
Comparison operators: = | == | != | < | <= | > | >=
Each exploded die can trigger another explosion (recursive).
Implementation should cap recursion to prevent infinite loops (typically 100).
Examples:
2d6! → Roll 2d6, each 6 adds another d6 (can chain)
d20! → Roll d20, 20s add another d20
4d10!10 → Roll 4d10, explode on exactly 10
5d8!>=7 → Roll 5d8, explode on 7 or 8
3d6!<=2 → Roll 3d6, explode on 1 or 2
--------------------------------------------------------------------------------
REROLL MODIFIER
--------------------------------------------------------------------------------
r<OP><N> → Reroll WHILE condition is true (can reroll same die multiple times)
ro<OP><N> → Reroll ONCE if condition is true (maximum one reroll per die)
Comparison operators: = | == | != | < | <= | > | >=
Examples:
4d6r=1 → Roll 4d6, keep rerolling any 1s until no 1s remain
4d6ro=1 → Roll 4d6, reroll 1s at most once per die
6d10r<=2 → Roll 6d10, keep rerolling while result <= 2
3d8ro<3 → Roll 3d8, reroll once any die showing < 3
--------------------------------------------------------------------------------
SORT MODIFIER
--------------------------------------------------------------------------------
sa → Sort Ascending (display order only, does not affect sum)
sd → Sort Descending (display order only, does not affect sum)
Examples:
5d6sa → Roll 5d6, display sorted low to high
4d8sd → Roll 4d8, display sorted high to low
--------------------------------------------------------------------------------
SUCCESS COUNT MODIFIER
--------------------------------------------------------------------------------
cs<OP><N> → Count Successes: return COUNT of dice meeting condition (not sum)
cf<OP><N> → Count Failures: return COUNT of dice meeting condition (not sum)
When cs or cf is used, the dice expression returns a COUNT instead of a SUM.
Comparison operators: = | == | != | < | <= | > | >=
Examples:
10d10cs>=8 → Roll 10d10, count how many are >= 8 (World of Darkness)
8d6cs>=5 → Roll 8d6, count how many are 5 or 6
5d10cf<=3 → Roll 5d10, count how many are <= 3
12d6cs=6 → Roll 12d6, count only the 6s
--------------------------------------------------------------------------------
COMBINING MODIFIERS
--------------------------------------------------------------------------------
Multiple modifiers can be chained. They execute in fixed order:
reroll → explode → keep/drop → sort → success count
Examples:
4d6ro=1kh3 → Reroll 1s once, then keep highest 3
6d10!ro<=2kh4 → Explode 10s, reroll <=2 once, keep top 4
10d6!>=6cs>=4 → Explode 6s, then count 4+ as successes
5d8ro=1sacs>=5 → Reroll 1s once, sort ascending, count 5+ successes
================================================================================
ARITHMETIC OPERATORS
================================================================================
Operator Precedence Associativity Description
-------- ---------- ------------- -----------
+ 1 Left Addition
- 1 Left Subtraction
* 2 Left Multiplication
/ 2 Left Integer division (floor)
% 2 Left Modulo (remainder)
^ 3 Right Exponentiation (power)
All operations produce integers. Division truncates toward negative infinity.
Examples:
5 + 3 → 8
10 - 4 → 6
3 * 7 → 21
17 / 5 → 3 (integer division)
17 % 5 → 2 (remainder)
2 ^ 10 → 1024
================================================================================
COMPARISON OPERATORS
================================================================================
Return 1 for true, 0 for false.
Operator Description
-------- -----------
== Equal to
!= Not equal to
< Less than
<= Less than or equal
> Greater than
>= Greater than or equal
Examples:
5 > 3 → 1 (true)
3 == 3 → 1 (true)
5 != 5 → 0 (false)
10 < 5 → 0 (false)
================================================================================
TERNARY CONDITIONAL
================================================================================
Format: condition ? value_if_true : value_if_false
The condition is evaluated. If non-zero (truthy), returns the first value.
If zero (falsy), returns the second value.
Examples:
10 > 5 ? 100 : 0 → 100 (condition is true)
3 > 5 ? 100 : 0 → 0 (condition is false)
1d20 >= 15 ? 1d8+3 : 0 → Roll d20, if >=15 then roll 1d8+3, else 0
1d20 == 20 ? 2d8 : 1d8 → Critical hit logic: nat 20 = 2d8, else 1d8
Nested conditionals:
1d20 == 20 ? 2d8+5 : 1d20 >= 15 ? 1d8+5 : 0
→ Crit on 20 (2d8+5), hit on 15+ (1d8+5), miss otherwise (0)
================================================================================
BUILT-IN FUNCTIONS
================================================================================
All functions use parentheses and comma-separated arguments.
--------------------------------------------------------------------------------
min(a, b, ...)
--------------------------------------------------------------------------------
Returns the minimum value among all arguments.
Accepts 2 or more arguments.
Examples:
min(5, 3) → 3
min(4d6, 20) → Roll 4d6 or 20, whichever is lower (damage cap)
min(10, 20, 5, 15) → 5
--------------------------------------------------------------------------------
max(a, b, ...)
--------------------------------------------------------------------------------
Returns the maximum value among all arguments.
Accepts 2 or more arguments.
Examples:
max(5, 3) → 5
max(2d6, 8) → Roll 2d6 or 8, whichever is higher (minimum damage)
max(1, 2, 3, 4) → 4
--------------------------------------------------------------------------------
abs(x)
--------------------------------------------------------------------------------
Returns the absolute value of x.
Accepts exactly 1 argument.
Examples:
abs(-5) → 5
abs(5) → 5
abs(3 - 10) → 7
--------------------------------------------------------------------------------
floor(x)
--------------------------------------------------------------------------------
Returns the floor of x (round down toward negative infinity).
Note: Division already floors in DEL, so this is rarely needed.
Accepts exactly 1 argument.
Examples:
floor(7) → 7
floor(-3) → -3
--------------------------------------------------------------------------------
ceil(x)
--------------------------------------------------------------------------------
Returns the ceiling of x (round up toward positive infinity).
Since DEL uses integers, this is mainly for explicit documentation.
Accepts exactly 1 argument.
Examples:
ceil(7) → 7
ceil(-3) → -3
--------------------------------------------------------------------------------
median(a, b, c, ...)
--------------------------------------------------------------------------------
Returns the median (middle value) of the arguments.
If even number of arguments, returns the lower middle value.
Accepts 1 or more arguments.
Examples:
median(1, 5, 3) → 3 (middle of sorted [1, 3, 5])
median(1, 2, 3, 4) → 2 (lower middle of [1, 2, 3, 4])
median(1d20, 1d20, 1d20) → Middle of 3 d20 rolls
median(2d6, 2d6, 2d6) → Roll 3 sets of 2d6, take middle sum
================================================================================
OPERATOR PRECEDENCE (highest to lowest)
================================================================================
1. Parentheses () - Grouping
2. Function calls - min(), max(), abs(), floor(), ceil(), median()
3. Unary minus - Negation (e.g., -5)
4. Exponentiation ^ - Right associative
5. Multiplication * / % - Left associative
6. Addition + - - Left associative
7. Comparison == != < <= > >= - Left associative
8. Ternary ? : - Right associative
================================================================================
PRACTICAL EXAMPLES BY GAME SYSTEM
================================================================================
D&D 5th Edition:
1d20 + 5 → Attack roll with +5 modifier
2d20kh1 + 3 → Advantage: keep higher d20, add +3
2d20kl1 + 3 → Disadvantage: keep lower d20, add +3
4d6kh3 → Ability score generation (drop lowest)
4d6ro=1kh3 → Generous ability scores (reroll 1s once)
8d6 → Fireball damage
1d20 >= 15 ? 1d8+5 : 0 → Attack: hit on 15+, deal 1d8+5
1d20 == 20 ? 4d6 : 2d6 → Crit doubles dice (approximation)
World of Darkness / Storyteller:
10d10cs>=8 → Roll pool, count 8+ as successes
7d10!cs>=8 → Explode on 10s, count successes
7d10!>=10cs>=8 → Explicit: explode on 10+, count 8+
Shadowrun:
12d6cs>=5 → Roll 12d6, count 5-6 as hits
Savage Worlds:
1d6! + 1d8! → Trait die + Wild die, both explode
max(1d6!, 1d8!) → Take better of exploding dice
Custom Mechanics:
max(2d6, 8) → Minimum 8 damage guarantee
min(10d6, 50) → Roll 10d6, cap at 50
3d6 >= 10 ? 2d6 : 1d6 → Conditional bonus damage
abs(1d6 - 1d6) → Difference between two rolls
================================================================================
ERROR CONDITIONS
================================================================================
These inputs should produce parse or evaluation errors:
- Empty expression: ""
- Invalid syntax: "3d" (missing sides), "d" (incomplete dice)
- Division by zero: "10 / 0"
- Invalid modifier: "3d6xx" (unknown modifier)
- Unclosed parentheses: "(3 + 5"
- Missing operand: "3 + * 2"
- Invalid function: "foo(3)" (unknown function)
- Wrong arity: "abs(1, 2)" (abs takes 1 arg)
================================================================================
IMPLEMENTATION NOTES
================================================================================
1. Dice rolls should use cryptographically secure random or high-quality PRNG
2. Exploding dice should cap recursion (suggest 100 iterations max)
3. Keep/drop with N > dice count: keep/drop all available dice
4. All arithmetic is integer-based; no floating point
5. Comparison operators in modifiers can use = as shorthand for ==
6. Negative numbers are supported: -5, -(3+2)
7. Parentheses can be nested arbitrarily deep
================================================================================
API USAGE
================================================================================
To evaluate a DEL expression via the Tactical Dice API:
POST /v1/dice/evaluate
Content-Type: application/json
{
"expression": "4d6kh3"
}
Response:
{
"result": 14,
"rolls": [6, 5, 3, 2],
"kept": [6, 5, 3],
"expression": "4d6kh3"
}
================================================================================
END OF SPECIFICATION
================================================================================