dice-table
Version:
CLI and SDK for tabletop RPG dice rolling
544 lines (403 loc) • 12.4 kB
Markdown
# Dice Table
A powerful, fast, and secure dice rolling utility for tabletop RPGs. Works both as a CLI tool and an importable library for Node.js projects.
## Features
- **Zero Dependencies** - Core engine uses only Node.js built-ins
- **Cryptographic Randomness** - Uses `crypto.randomInt()` for fair, secure rolls
- **Complete Notation Support** - All standard tabletop RPG dice notation including shorthand
- **CLI & Library** - Use from command line or integrate into your projects
- **Dual Output Modes** - Human-readable and JSON output for applications
- **Comprehensive Error Handling** - Helpful hints for humans, structured errors for apps
- **Fast & Lightweight** - Optimized for high performance
## Installation
### Global CLI Installation
```bash
npm install -g dice-table
```
### Library Installation
```bash
npm install dice-table
```
## Quick Start
### Command Line Usage
```bash
# Basic roll
dice roll 1d20
# Roll with modifier
dice roll 1d20+5
# Advantage roll (D&D 5e) - shorthand notation
dice roll 2d20kh
# Generate ability scores
dice roll 4d6kh3 --times 6
# Statistical analysis
dice stats 3d6 --iterations 10000
# JSON output for applications
dice roll 2d6+5 --json
```
### Library Usage
```javascript
const dice = require('dice-table');
// Simple roll
const result = dice.roll('2d6+5');
console.log(result.total); // 7-17
// Multiple rolls
const results = dice.rollMany('1d20+5', 6);
// Statistical analysis
const stats = dice.stats('4d6kh3', { iterations: 10000 });
// Error handling
if (result.valid) {
console.log(`Rolled: ${result.total}`);
} else {
console.error(`Error: ${result.error}`);
}
```
## Dice Notation Reference
### Basic Notation
| Expression | Description |
|------------|-------------|
| `1d6` | Roll one six-sided die |
| `2d20` | Roll two twenty-sided dice |
| `3d8+5` | Roll three d8s and add 5 |
| `1d20-2` | Roll one d20 and subtract 2 |
### Keep/Drop Mechanics
| Expression | Description |
|------------|-------------|
| `2d20kh` | Roll 2d20, keep highest 1 (advantage - shorthand) |
| `2d20kh1` | Roll 2d20, keep highest 1 (advantage - explicit) |
| `2d20kl1` | Roll 2d20, keep lowest 1 (disadvantage) |
| `4d6kh3` | Roll 4d6, keep highest 3 (ability scores) |
| `4d6dh1` | Roll 4d6, drop highest 1 |
| `4d6dl1` | Roll 4d6, drop lowest 1 |
### Complex Expressions
| Expression | Description |
|------------|-------------|
| `1d20+1d8+5` | Attack roll with weapon damage |
| `2d20kh1+1d6+3` | Advantage attack with sneak attack |
| `3d6+2d4-1` | Multiple dice types with modifier |
### Supported Die Types
- **Standard RPG Dice**: d4, d6, d8, d10, d12, d20, d100
## CLI Reference
### Commands
#### `dice roll <expression> [options]`
Roll dice using standard RPG notation.
**Options:**
- `--times, -t <N>` - Roll the expression N times (max: 1000)
- `--json` - Output result in JSON format for applications
- `--help, -h` - Show help for this command
**Examples:**
```bash
dice roll 1d20 # Single d20
dice roll 2d6+5 # 2d6 with +5 modifier
dice roll 4d6kh3 # 4d6 keep highest 3
dice roll 2d20kh+5 # Advantage with modifier (shorthand)
dice roll 3d6 --times 6 # Roll 6 times for stats
dice roll 1d20+5 --json # JSON output for apps
```
#### `dice stats <expression> [options]`
Analyze the statistical distribution of a dice expression.
**Options:**
- `--iterations <N>` - Number of simulations (default: 10000, range: 100-100000)
- `--json` - Output result in JSON format for applications
- `--help, -h` - Show help for this command
**Examples:**
```bash
dice stats 3d6 # Basic 3d6 analysis
dice stats 4d6kh3 # Ability score generation
dice stats 1d20+5 --iterations 50000 # High-precision analysis
dice stats 2d20kh --json # JSON output for analysis
```
#### `dice help [command]`
Show help information.
**Examples:**
```bash
dice help # General help
dice help roll # Help for roll command
dice help stats # Help for stats command
```
## Error Handling
Dice Table provides comprehensive error handling for both human users and applications:
### CLI Error Handling
```bash
# Human-readable errors with helpful hints
$ dice roll invalid
Error: Invalid dice expression format. Use format like "2d6", "1d20+5", or "4d6kh3"
Hint: Use format like "2d6", "1d20+5", or "4d6kh3"
# JSON errors for applications
$ dice roll invalid --json
{"success":false,"error":"Invalid dice expression format. Use format like \\"2d6\\", \\"1d20+5\\", or \\"4d6kh3\\"","hint":"Use format like \\"2d6\\", \\"1d20+5\\", or \\"4d6kh3\\""}
# Parameter validation
$ dice roll 1d20 --times -1
Error: --times must be at least 1, got: -1
Hint: Use a positive number: --times 5
```
### Library Error Handling
```javascript
const result = dice.roll('invalid expression');
if (result.valid) {
console.log(`Result: ${result.total}`);
} else {
console.error(`Error: ${result.error}`);
}
```
## Library API
### Core Functions
#### `dice.roll(expression)`
Execute a single dice roll.
```javascript
const result = dice.roll('2d6+5');
// Result structure:
{
expression: '2d6+5',
dice: [
{ type: 'd6', value: 4, kept: true, dropped: false },
{ type: 'd6', value: 3, kept: true, dropped: false }
],
modifier: 5,
total: 12,
details: '[4, 3] + 5 = 12',
valid: true
}
// Error handling:
const invalid = dice.roll('invalid');
// Returns: { error: 'Error message', expression: 'invalid', valid: false }
```
#### `dice.rollMany(expression, times)`
Roll multiple times and return array of results.
```javascript
const results = dice.rollMany('1d20+5', 6);
// Returns array of 6 roll results
// Error handling:
const invalid = dice.rollMany('bad', 5);
// Returns array with error objects if expression is invalid
```
#### `dice.parse(expression)`
Parse a dice expression without rolling.
```javascript
const parsed = dice.parse('4d6kh3');
// Returns parsed structure:
{
valid: true,
expression: '4d6kh3',
parts: [
{
count: 4,
sides: 6,
keep: { type: 'highest', count: 3 }
}
]
}
```
#### `dice.validate(expression)`
Validate a dice expression.
```javascript
const validation = dice.validate('2d20kh1');
// Returns:
{ valid: true }
// Or for invalid expressions:
{ valid: false, error: 'Error message' }
```
#### `dice.stats(expression, options)`
Get statistical analysis of a dice expression.
```javascript
const stats = dice.stats('3d6', { iterations: 10000 });
// Returns comprehensive statistics:
{
expression: '3d6',
iterations: 10000,
theoretical: { min: 3, max: 18 },
actualRange: { min: 3, max: 18 },
statistics: {
mean: 10.5,
median: 11,
mode: 10,
standardDeviation: 2.96
},
percentiles: {
p5: 6,
p25: 8,
p50: 11,
p75: 13,
p95: 15
}
}
```
### Utility Functions
#### `dice.rollCli(expression, options)`
Roll dice and format for CLI display.
```javascript
const output = dice.rollCli('2d6+5');
console.log(output);
// "Rolling 2d6+5: [4, 3] + 5 = 12"
```
#### `dice.rollData(expression)`
Roll dice and return structured data for APIs.
```javascript
const data = dice.rollData('1d20+5');
// Returns clean data structure suitable for JSON APIs
```
## Use Cases
### Discord Bot
```javascript
const dice = require('dice-table');
bot.on('message', msg => {
if (msg.content.startsWith('!roll ')) {
const expression = msg.content.slice(6);
const result = dice.roll(expression);
if (result.valid) {
msg.reply(`🎲 ${result.details}`);
} else {
msg.reply(`❌ ${result.error}`);
}
}
});
```
### Game Engine
```javascript
const dice = require('dice-table');
class Character {
rollAttack() {
const result = dice.roll(`1d20+${this.attackBonus}`);
return result.total >= this.target.ac;
}
rollDamage() {
return dice.roll(`${this.weapon.damage}+${this.strengthMod}`);
}
rollAbilityScores() {
return dice.rollMany('4d6kh3', 6);
}
// Advantage/disadvantage
rollWithAdvantage() {
return dice.roll('2d20kh'); // Shorthand notation
}
}
```
### Web API
```javascript
const express = require('express');
const dice = require('dice-table');
app.post('/api/roll', (req, res) => {
const { expression } = req.body;
const result = dice.rollData(expression);
res.json(result);
});
app.get('/api/stats/:expression', (req, res) => {
const stats = dice.stats(req.params.expression);
res.json(stats);
});
```
### Campaign Management
```javascript
const dice = require('dice-table');
// Generate NPC stats
function generateNPC() {
const stats = dice.rollMany('4d6kh3', 6);
const hp = dice.roll('8d8+16'); // For a high-level character
return {
abilities: stats.map(r => r.total),
hitPoints: hp.total
};
}
// Mass combat simulation
function simulateBattle(rounds = 10) {
const results = [];
for (let i = 0; i < rounds; i++) {
const attack = dice.roll('1d20+8');
const damage = attack.total >= 15 ? dice.roll('2d6+4') : null;
results.push({ attack: attack.total, damage: damage?.total || 0 });
}
return results;
}
```
## Advanced Features
### Statistical Analysis
The stats function provides comprehensive analysis:
```javascript
const analysis = dice.stats('4d6kh3', { iterations: 100000 });
console.log(`Mean: ${analysis.statistics.mean}`);
console.log(`Standard Deviation: ${analysis.statistics.standardDeviation}`);
console.log(`95th Percentile: ${analysis.percentiles.p95}`);
```
### Input Validation
```javascript
function safeRoll(expression) {
const validation = dice.validate(expression);
if (!validation.valid) {
throw new Error(`Invalid dice expression: ${validation.error}`);
}
return dice.roll(expression);
}
```
## Performance & Constraints
### Performance
- **Single Roll**: < 1ms
- **10,000 Rolls**: < 100ms
- **Complex Expression (2d20kh1+1d8+5)**: < 2ms
- **Statistical Analysis (10k iterations)**: < 500ms
### Constraints & Limits
- **Dice Count**: 1-999 dice per component
- **Die Sides**: d4, d6, d8, d10, d12, d20, d100 only
- **Modifiers**: -9999 to +9999
- **CLI Times**: 1-1000 rolls
- **CLI Iterations**: 100-100,000 for stats
- **Total Dice**: Maximum 999 dice across all components
### Memory Usage
- **Zero Dependencies**: No external packages
- **Lightweight**: Small package size
- **Efficient**: Minimal memory footprint
## Security
- **Cryptographic Randomness**: Uses `crypto.randomInt()` for true randomness
- **Input Validation**: All expressions validated before execution
- **No Eval**: Parser doesn't use `eval()` or similar unsafe functions
- **Bounded**: All inputs have reasonable limits to prevent abuse
## Contributing
### Development Setup
```bash
git clone https://github.com/yourusername/dice-table.git
cd dice-table
npm install
npm test
```
### Running Tests
```bash
npm test # Run all tests
npm run test:parser # Test parser only
npm run test:roller # Test roller only
npm run test:cli # Test CLI only
```
### Project Structure
```
dice-table/
index.js # Library entry point
cli.js # CLI entry point
lib/ # Core modules
parser.js # Expression parser
roller.js # Random number generation
validator.js # Input validation
formatter.js # Output formatting
stats.js # Statistical analysis
package.json
```
### Adding Features
1. Add functionality to appropriate module in `lib/`
2. Export new functions from `index.js`
3. Add comprehensive tests
4. Update documentation
5. Submit pull request
## Changelog
### v1.0.0
- Initial release
- Complete dice notation support including shorthand (2d20kh)
- CLI interface with dual output modes (human/JSON)
- Library API with comprehensive error handling
- Statistical analysis with Monte Carlo simulation
- Cryptographic randomness using crypto.randomInt()
- Parameter validation with helpful error messages
## License
ISC License - see LICENSE file for details.
## Support
For help and support:
- Use `dice --help` for CLI usage
- Use `dice <command> --help` for command-specific help
- Check error messages for helpful hints
- Review examples in this README
---
**Dice Table** - Roll with confidence. 🎲