├── .npmignore ├── .gitignore ├── .travis.yml ├── bin └── droll-cli.js ├── test ├── droll-cli.test.js └── droll.test.js ├── LICENSE ├── droll.min.js ├── Gruntfile.js ├── package.json ├── README.md └── droll.js /.npmignore: -------------------------------------------------------------------------------- 1 | test/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "6" 5 | - "5" 6 | - "4" 7 | - "0.11" 8 | - "0.10" 9 | - "0.8" 10 | before_install: 11 | - npm update -g npm 12 | - npm install -g grunt-cli 13 | -------------------------------------------------------------------------------- /bin/droll-cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var droll = require('../droll'); 6 | 7 | if (process.argv[2] === undefined) { 8 | console.error('Missing Formula'); 9 | process.exit(1); 10 | } 11 | 12 | if (droll.validate(process.argv[2])) { 13 | console.log(droll.roll(process.argv[2]).toString()); 14 | process.exit(); 15 | } else { 16 | console.error('Invalid Formula'); 17 | process.exit(1); 18 | } 19 | -------------------------------------------------------------------------------- /test/droll-cli.test.js: -------------------------------------------------------------------------------- 1 | /*jshint expr: true*/ 2 | 3 | var should = require('should'), 4 | child_process = require('child_process'); 5 | 6 | describe('droll-cli ', function() { 7 | 8 | it('should output to stdout but not to stderr for a valid formula', function() { 9 | child_process.exec('./bin/droll-cli.js 3d6+1', function(error, stdout, stderr) { 10 | stdout.should.not.be.empty; 11 | stderr.should.be.empty; 12 | }); 13 | }); 14 | 15 | it('should output to stderr but not to stdout for an invalid formula', function() { 16 | child_process.exec('./bin/droll-cli.js 36+1', function(error, stdout, stderr) { 17 | stdout.should.be.empty; 18 | stderr.should.not.be.empty; 19 | }); 20 | }); 21 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Ethan Zimmerman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /droll.min.js: -------------------------------------------------------------------------------- 1 | /*! droll v0.2.1 http://github.com/thebinarypenguin/droll */ 2 | !function(a){"use strict";function b(){this.numDice=0,this.numSides=0,this.modifier=0,this.minResult=0,this.maxResult=0,this.avgResult=0}function c(){this.rolls=[],this.modifier=0,this.total=0}var d={};c.prototype.toString=function(){return 1===this.rolls.length&&0===this.modifier?this.rolls[0]+"":this.rolls.length>1&&0===this.modifier?this.rolls.join(" + ")+" = "+this.total:1===this.rolls.length&&this.modifier>0?this.rolls[0]+" + "+this.modifier+" = "+this.total:this.rolls.length>1&&this.modifier>0?this.rolls.join(" + ")+" + "+this.modifier+" = "+this.total:1===this.rolls.length&&this.modifier<0?this.rolls[0]+" - "+Math.abs(this.modifier)+" = "+this.total:this.rolls.length>1&&this.modifier<0?this.rolls.join(" + ")+" - "+Math.abs(this.modifier)+" = "+this.total:void 0},d.parse=function(a){var c=null,d=new b;return(c=a.match(/^([1-9]\d*)?d([1-9]\d*)([+-]\d+)?$/i))?(d.numDice=c[1]-0||1,d.numSides=c[2]-0,d.modifier=c[3]-0||0,d.minResult=1*d.numDice+d.modifier,d.maxResult=d.numDice*d.numSides+d.modifier,d.avgResult=(d.maxResult+d.minResult)/2,d):!1},d.validate=function(a){return d.parse(a)?!0:!1},d.roll=function(a){var b=null,e=new c;if(b=d.parse(a),!b)return!1;for(var f=0;f.js', 'bin/*.js', 'test/*.js'], 9 | }, 10 | 11 | // Minify 12 | uglify: { 13 | options: { 14 | banner: '/*! <%= pkg.name %> v<%= pkg.version %> <%= pkg.repository.url %> */\n' 15 | }, 16 | all: { 17 | files: { 18 | '<%= pkg.name %>.min.js': ['<%= pkg.name %>.js'] 19 | } 20 | } 21 | }, 22 | 23 | // Test 24 | mochacli: { 25 | options: { 26 | reporter: 'spec' 27 | }, 28 | all: ['test/*.js'] 29 | }, 30 | 31 | // Bump, Tag, Push 32 | bump: { 33 | options: { 34 | updateConfigs: ['pkg'], 35 | commitFiles: ['-a'], 36 | pushTo: 'origin' 37 | } 38 | }, 39 | 40 | // Publish 41 | shell: { 42 | publish: { 43 | command : "npm publish" 44 | } 45 | } 46 | }); 47 | 48 | grunt.loadNpmTasks('grunt-contrib-jshint'); 49 | grunt.loadNpmTasks('grunt-contrib-uglify'); 50 | grunt.loadNpmTasks('grunt-mocha-cli'); 51 | grunt.loadNpmTasks('grunt-bump'); 52 | grunt.loadNpmTasks('grunt-shell'); 53 | 54 | grunt.registerTask('default', ['jshint', 'mochacli']); 55 | grunt.registerTask('lint', ['jshint']); 56 | grunt.registerTask('test', ['mochacli']); 57 | 58 | grunt.registerTask("release", "Release a new version, push it and publish it", function(target) { 59 | if (!target) { 60 | target = "patch"; 61 | } 62 | return grunt.task.run("bump-only:" + target, "uglify", "bump-commit", "shell:publish"); 63 | }); 64 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "droll", 3 | "version" : "0.2.1", 4 | "description" : "A dice rolling library that uses standard dice notation", 5 | "keywords" : ["die", "dice", "dice notation", "roll", "random", "rpg", "game"], 6 | "author" : "Ethan Zimmerman (http://thebinarypenguin.com)", 7 | "repository" : { 8 | "type" : "git", 9 | "url" : "http://github.com/thebinarypenguin/droll" 10 | }, 11 | "bugs" : "http://github.com/thebinarypenguin/droll/issues", 12 | "licenses" : [ 13 | { 14 | "type" : "MIT", 15 | "url" : "http://raw.github.com/thebinarypenguin/droll/master/LICENSE" 16 | } 17 | ], 18 | "main" : "droll.js", 19 | "bin" : { 20 | "droll" : "./bin/droll-cli.js" 21 | }, 22 | "scripts" : { 23 | "test" : "grunt --verbose" 24 | }, 25 | "devDependencies" : { 26 | "mocha" : "1.14.x", 27 | "should" : "2.1.x", 28 | "grunt" : "0.4.x", 29 | "grunt-contrib-jshint" : "0.7.x", 30 | "grunt-contrib-uglify" : "0.2.x", 31 | "grunt-mocha-cli" : "1.4.x", 32 | "grunt-bump" : "0.0.x", 33 | "grunt-shell" : "0.6.x" 34 | }, 35 | "engines" : { 36 | "node" : ">=0.8.0" 37 | }, 38 | "private" : false 39 | } -------------------------------------------------------------------------------- /test/droll.test.js: -------------------------------------------------------------------------------- 1 | /*jshint expr: true*/ 2 | 3 | var should = require('should'), 4 | droll = require('../droll'); 5 | 6 | 7 | describe('droll#parse(formula)', function() { 8 | 9 | it('should return a correctly-formatted object when given a valid formula', function() { 10 | var result = droll.parse('3d6+1'); 11 | 12 | result.should.have.properties( 13 | 'numDice', 14 | 'numSides', 15 | 'modifier', 16 | 'minResult', 17 | 'maxResult', 18 | 'avgResult' 19 | ); 20 | 21 | result.numDice.should.equal(3); 22 | result.numSides.should.equal(6); 23 | result.modifier.should.equal(1); 24 | result.minResult.should.equal(4); 25 | result.maxResult.should.equal(19); 26 | result.avgResult.should.equal(11.5); 27 | }); 28 | 29 | it('should return false when given an invalid formula', function() { 30 | droll.validate('d').should.be.false; 31 | }); 32 | 33 | it('should be case insensitive', function() { 34 | droll.validate('D4').should.not.be.false; 35 | }); 36 | }); 37 | 38 | 39 | describe('droll#validate(formula)', function() { 40 | 41 | it('should return true when given a valid formula', function() { 42 | droll.validate('3d6+1').should.be.true; 43 | }); 44 | 45 | it('should return false when given an invalid formula', function() { 46 | droll.validate('d').should.be.false; 47 | }); 48 | }); 49 | 50 | 51 | describe('droll#roll(formula)', function() { 52 | 53 | it('should return a correctly-formatted object when given a valid formula', function() { 54 | var result = droll.roll('3d6+1'); 55 | 56 | result.should.have.properties('rolls', 'modifier', 'total'); 57 | 58 | result.rolls.should.have.length(3); 59 | 60 | result.rolls[0].should.be.within(1, 6); 61 | result.rolls[1].should.be.within(1, 6); 62 | result.rolls[2].should.be.within(1, 6); 63 | 64 | result.modifier.should.equal(1); 65 | 66 | result.total.should.be.within(4, 19); 67 | }); 68 | 69 | it('should return false when given an invalid formula', function() { 70 | droll.roll('d').should.be.false; 71 | }); 72 | }); 73 | 74 | 75 | describe('String representations of DrollResult objects', function() { 76 | it('should be formatted correctly', function() { 77 | droll.roll('d8').toString().should.match(/^[1-8]$/); 78 | droll.roll('2d8').toString().should.match(/^[1-8] \+ [1-8] = \d+$/); 79 | droll.roll('d8+20').toString().should.match(/^[1-8] \+ 20 = \d+$/); 80 | droll.roll('2d8+20').toString().should.match(/^[1-8] \+ [1-8] \+ 20 = \d+$/); 81 | droll.roll('d8-20').toString().should.match(/^[1-8] \- 20 = \-\d+$/); 82 | droll.roll('2d8-20').toString().should.match(/^[1-8] \+ [1-8] \- 20 = \-\d+$/); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Droll is a JavaScript dice-rolling library. It accepts input in 4 | [standard dice notation](http://en.wikipedia.org/wiki/Dice_notation) and works in both Node.js 5 | and browser environments. 6 | 7 | An optional executable is also included in the package for use directly from the command line. 8 | 9 | [![Build Status](https://travis-ci.org/thebinarypenguin/droll.svg?branch=master)](https://travis-ci.org/thebinarypenguin/droll) 10 | 11 | # Installation 12 | 13 | With npm (recommended) 14 | 15 | `npm install droll` 16 | 17 | Without npm 18 | 19 | Download [droll.js](http://raw.github.com/thebinarypenguin/droll/master/droll.js) or 20 | [droll.min.js](http://raw.github.com/thebinarypenguin/droll/master/droll.min.js) 21 | 22 | 23 | # Usage 24 | 25 | Node.js 26 | 27 | ```javascript 28 | var droll = require('droll'); 29 | 30 | var result = droll.roll('3d6+1'); 31 | 32 | console.log(result); 33 | ``` 34 | 35 | Browser 36 | 37 | ```html 38 | 39 | 46 | ``` 47 | 48 | 49 | # Optional Executable 50 | 51 | Droll ships with an optional executable that can be installed via npm like this 52 | 53 | ``` 54 | npm install droll -g 55 | ``` 56 | 57 | And used from the command line like this 58 | 59 | ``` 60 | $ droll 3d6+1 61 | 6 + 5 + 5 + 1 = 17 62 | ``` 63 | 64 | 65 | # Public Methods 66 | 67 | 68 | ### validate(formula) 69 | 70 | * __formula__ `String` The dice formula in standard dice notation. 71 | 72 | Returns true if `formula` is valid dice notation or false otherwise. 73 | 74 | 75 | ### roll(formula) 76 | 77 | * __formula__ `String` The dice formula in standard dice notation. 78 | 79 | Rolls the dice defined by `formula` and returns a `DrollResult` object on success or false 80 | otherwise. 81 | 82 | The `DrollResult` object contains the following properties 83 | 84 | * __rolls__ `Array` The result of each die roll. 85 | * __modifier__ `Number` The optional modifier. The default is 0. 86 | * __total__ `Number` The sum of the rolls plus the modifier. 87 | 88 | The `DrollResult` object also has a custom `toString()` method for pretty printing the result. 89 | It returns strings that look like `6 + 5 + 5 + 1 = 17` or `4 + 2 - 1 = 5` or even just `7`. 90 | 91 | 92 | ### parse(formula) 93 | 94 | * __formula__ `String` The dice formula in standard dice notation. 95 | 96 | Parses `formula` into its component pieces and returns a `DrollFormula` object on success or false 97 | otherwise. 98 | 99 | The `DrollFormula` object contains the following properties 100 | 101 | * __numDice__ `Number` The number of dice to roll. 102 | * __numSides__ `Number` The number of sides on each die. 103 | * __modifier__ `Number` The optional modifier. The default is 0. 104 | * __minResult__ `Number` The minimum result that can be returned by this formula. 105 | * __maxResult__ `Number` The maximum result that can be returned by this formula. 106 | * __avgResult__ `Number` The average result returned by this formula. ((max + min) / 2) 107 | -------------------------------------------------------------------------------- /droll.js: -------------------------------------------------------------------------------- 1 | (function(root) { 2 | 3 | "use strict"; 4 | 5 | var droll = {}; 6 | 7 | // Define a "class" to represent a formula 8 | function DrollFormula() { 9 | this.numDice = 0; 10 | this.numSides = 0; 11 | this.modifier = 0; 12 | 13 | this.minResult = 0; 14 | this.maxResult = 0; 15 | this.avgResult = 0; 16 | } 17 | 18 | // Define a "class" to represent the results of the roll 19 | function DrollResult() { 20 | this.rolls = []; 21 | this.modifier = 0; 22 | this.total = 0; 23 | } 24 | 25 | /** 26 | * Returns a string representation of the roll result 27 | */ 28 | DrollResult.prototype.toString = function() { 29 | if (this.rolls.length === 1 && this.modifier === 0) { 30 | return this.rolls[0] + ''; 31 | } 32 | 33 | if (this.rolls.length > 1 && this.modifier === 0) { 34 | return this.rolls.join(' + ') + ' = ' + this.total; 35 | } 36 | 37 | if (this.rolls.length === 1 && this.modifier > 0) { 38 | return this.rolls[0] + ' + ' + this.modifier + ' = ' + this.total; 39 | } 40 | 41 | if (this.rolls.length > 1 && this.modifier > 0) { 42 | return this.rolls.join(' + ') + ' + ' + this.modifier + ' = ' + this.total; 43 | } 44 | 45 | if (this.rolls.length === 1 && this.modifier < 0) { 46 | return this.rolls[0] + ' - ' + Math.abs(this.modifier) + ' = ' + this.total; 47 | } 48 | 49 | if (this.rolls.length > 1 && this.modifier < 0) { 50 | return this.rolls.join(' + ') + ' - ' + Math.abs(this.modifier) + ' = ' + this.total; 51 | } 52 | }; 53 | 54 | /** 55 | * Parse the formula into its component pieces. 56 | * Returns a DrollFormula object on success or false on failure. 57 | */ 58 | droll.parse = function(formula) { 59 | var pieces = null; 60 | var result = new DrollFormula(); 61 | 62 | pieces = formula.match(/^([1-9]\d*)?d([1-9]\d*)([+-]\d+)?$/i); 63 | if (!pieces) { return false; } 64 | 65 | result.numDice = (pieces[1] - 0) || 1; 66 | result.numSides = (pieces[2] - 0); 67 | result.modifier = (pieces[3] - 0) || 0; 68 | 69 | result.minResult = (result.numDice * 1) + result.modifier; 70 | result.maxResult = (result.numDice * result.numSides) + result.modifier; 71 | result.avgResult = (result.maxResult + result.minResult) / 2; 72 | 73 | return result; 74 | }; 75 | 76 | /** 77 | * Test the validity of the formula. 78 | * Returns true on success or false on failure. 79 | */ 80 | droll.validate = function(formula) { 81 | return (droll.parse(formula)) ? true : false ; 82 | }; 83 | 84 | /** 85 | * Roll the dice defined by the formula. 86 | * Returns a DrollResult object on success or false on failure. 87 | */ 88 | droll.roll = function(formula) { 89 | var pieces = null; 90 | var result = new DrollResult(); 91 | 92 | pieces = droll.parse(formula); 93 | if (!pieces) { return false; } 94 | 95 | for (var a=0; a