UNPKG

dice-table

Version:

CLI and SDK for tabletop RPG dice rolling

544 lines (403 loc) 12.4 kB
# 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. 🎲