@randsum/notation
Version:
A flexible, type-safe dice notation parser and validator
330 lines (270 loc) • 5.44 kB
Markdown
# Randsum Dice Notation
## Overview
Dice notation is a compact way to represent dice rolls and their modifications. For example, `4d20+2` means "roll four twenty-sided dice, then add two".
Randsum extends standard dice notation with powerful modifiers like dropping lowest rolls, rerolling specific values, and ensuring unique results.
## Basic Syntax
All notation in randsum is case-insensitive (`2d8` = `2D8`).
### Standard Rolls
```typescript
// Roll one d20
roll(20) // number argument
roll('1d20') // notation string
roll({
sides: 20,
quantity: 1
})
// Roll four d6
roll('4d6')
roll({
sides: 6,
quantity: 4
})
```
### Custom-Faced Dice
Roll dice with non-numeric faces:
```typescript
// Roll a coin (heads/tails) four times
import { rollCustomFaces } from 'randsum'
rollCustomFaces('4d{HT}')
rollCustomFaces({
sides: ['H', 'T'],
quantity: 4
})
// Roll Fudge/Fate dice
rollCustomFaces('4d{++-- }') // Plus, minus, blank faces
rollCustomFaces({
sides: ['+', '+', '-', '-', ' ', ' '],
quantity: 4
})
```
Note: Custom-faced dice ignore modifiers and always return a total of 0.
## Modifiers
### Basic Arithmetic
```typescript
roll('4d6+2') // Add 2 to total
roll({
sides: 6,
quantity: 4,
modifiers: { plus: 2 }
})
roll('4d6-1') // Subtract 1 from total
roll({
sides: 6,
quantity: 4,
modifiers: { minus: 1 }
})
```
### Cap Modifiers
Limit roll values to specific ranges:
```typescript
roll('4d20C{>18}') // Cap rolls over 18 to 18
roll({
sides: 20,
quantity: 4,
modifiers: {
cap: { greaterThan: 18 }
}
})
roll('4d20C{<3}') // Cap rolls under 3 to 3
roll({
sides: 20,
quantity: 4,
modifiers: {
cap: { lessThan: 3 }
}
})
roll('4d20C{<2,>19}') // Cap rolls under 2 to 2 and over 19 to 19
roll({
sides: 20,
quantity: 4,
modifiers: {
cap: {
greaterThan: 19,
lessThan: 2
}
}
})
```
### Drop Modifiers
Drop specific dice from the results:
```typescript
roll('4d6L') // Drop lowest
roll({
sides: 6,
quantity: 4,
modifiers: { drop: { lowest: 1 } }
})
roll('4d6L2') // Drop 2 lowest
roll({
sides: 6,
quantity: 4,
modifiers: { drop: { lowest: 2 } }
})
roll('4d6H') // Drop highest
roll({
sides: 6,
quantity: 4,
modifiers: { drop: { highest: 1 } }
})
roll('4d6H2') // Drop 2 highest
roll({
sides: 6,
quantity: 4,
modifiers: { drop: { highest: 2 } }
})
// Drop by value
roll('4d20D{>17}') // Drop rolls over 17
roll({
sides: 20,
quantity: 4,
modifiers: { drop: { greaterThan: 17 } }
})
roll('4d20D{<5}') // Drop rolls under 5
roll({
sides: 20,
quantity: 4,
modifiers: { drop: { lessThan: 5 } }
})
roll('4d20D{8,12}') // Drop 8s and 12s
roll({
sides: 20,
quantity: 4,
modifiers: { drop: { exact: [8, 12] } }
})
```
### Reroll Modifiers
Reroll dice matching certain conditions:
```typescript
roll('4d20R{>17}') // Reroll results over 17
roll({
sides: 20,
quantity: 4,
modifiers: { reroll: { greaterThan: 17 } }
})
roll('4d20R{<5}') // Reroll results under 5
roll({
sides: 20,
quantity: 4,
modifiers: { reroll: { lessThan: 5 } }
})
roll('4d20R{8,12}') // Reroll 8s and 12s
roll({
sides: 20,
quantity: 4,
modifiers: { reroll: { exact: [8, 12] } }
})
roll('4d20R{<5}3') // Reroll under 5, max 3 attempts
roll({
sides: 20,
quantity: 4,
modifiers: {
reroll: {
lessThan: 5,
max: 3
}
}
})
```
### Replace Modifiers
Replace specific results with new values:
```typescript
roll('4d20V{8=12}') // Replace 8s with 12s
roll({
sides: 20,
quantity: 4,
modifiers: {
replace: {
from: 8,
to: 12
}
}
})
roll('4d20V{>17=20}') // Replace results over 17 with 20
roll({
sides: 20,
quantity: 4,
modifiers: {
replace: {
from: { greaterThan: 17 },
to: 20
}
}
})
roll('4d20V{<5=1}') // Replace results under 5 with 1
roll({
sides: 20,
quantity: 4,
modifiers: {
replace: {
from: { lessThan: 5 },
to: 1
}
}
})
```
### Unique Results
Force unique rolls within a pool:
```typescript
roll('4d20U') // All results must be unique
roll({
sides: 20,
quantity: 4,
modifiers: { unique: true }
})
roll('4d20U{5,10}') // Unique except 5s and 10s can repeat
roll({
sides: 20,
quantity: 4,
modifiers: {
unique: { notUnique: [5, 10] }
}
})
```
### Exploding Dice
Roll additional dice on maximum results:
```typescript
roll('4d20!') // Roll an extra d20 for each 20 rolled
roll({
sides: 20,
quantity: 4,
modifiers: { explode: true }
})
```
### Combining Modifiers
Modifiers can be chained together:
```typescript
roll('4d6L+2') // Drop lowest, add 2
roll({
sides: 6,
quantity: 4,
modifiers: {
drop: { lowest: 1 },
plus: 2
}
})
roll('2d20H!+1') // Drop highest, explode, add 1
roll({
sides: 20,
quantity: 2,
modifiers: {
drop: { highest: 1 },
explode: true,
plus: 1
}
})
roll('4d6R{<3}L') // Reroll under 3, then drop lowest
roll({
sides: 6,
quantity: 4,
modifiers: {
reroll: { lessThan: 3 },
drop: { lowest: 1 }
}
})
```
## Notes
- When using notation strings with custom faces, each face must be a single character
- The options object interface allows for multi-character custom faces
- See the [Getting Started Guide](GETTING_STARTED.md) for more usage examples
## Attribution
The extended notation syntax was inspired by [Sophie's Dice](https://sophiehoulden.com/dice/documentation/notation.html#keep).