Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

roll

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

roll - npm Package Compare versions

Comparing version
1.2.0
to
1.3.0
+170
README.md
# roll
Roll is a node.js package for rolling dice and adding modifiers (such as "2d6+1").
[![NPM](https://nodei.co/npm/roll.png?downloads=true&stars=true)](https://nodei.co/npm/roll/)
[![build status](https://secure.travis-ci.org/troygoode/node-roll.png)](http://travis-ci.org/troygoode/node-roll)
## How To Use (From Shell)
### Installation (via [npm](https://npmjs.org/package/roll))
```bash
$ npm install -g roll
```
### Usage
```bash
$ roll 2d6+3
9
$ roll d20
15
$ roll d%
99
$ roll -d 2d20
Dice: 13, 8
Total: 21
$ roll --detail 5d%
Dice: 45, 86, 86, 4, 72
Total: 293
```
## How To Use (As Library)
### Installation (via [npm](https://npmjs.org/package/roll))
```bash
$ npm install roll
```
### Usage
Get an instance of the library:
```javascript
var Roll = require('roll'),
roll = new Roll();
```
Rolling a single die:
```javascript
var oneDie = roll.roll('d6');
console.log(oneDie.result); //random number between 1 and 6 (inclusive)
```
Rolling multiple dice:
```javascript
var twoTwenties = roll.roll('2d20');
console.log(twoTwenties.result); //random number between 2 and 40 (inclusive)
```
Rolling multiple sets of dice:
```javascript
var bunchOfDice = roll.roll('2d20+1d12');
console.log(bunchOfDice.result); //random number between 3 and 52 (inclusive)
```
Rolling a percentage:
```javascript
var chance = roll.roll('d%'); //same as '1d100', 'd100', or '1d%'
console.log(chance.result); //random number between 1 and 100 (inclusive)
```
Simple calculation (+, -, *, /):
```javascript
var attack = roll.roll('2d6+2');
console.log(attack.result); //random number between 3 and 8 (inclusive)
```
Seeing what was rolled, rather than the sum:
```javascript
var yahtzee = roll.roll('5d6');
console.log(yahtzee.rolled); //yahtzee.rolled will return something like [5, 2, 4, 6, 1] rather than the sum
var blessedSneaker = roll.roll('2d20b1+1d4+5');
console.log(blessedSneaker.rolled); // blessedSneaker.rolled will return an array containing an array for each component that is a roll of the dice, in the order in which they occurred, e.g. [[19,3],[1]]
```
Getting the highest two dice of the set:
```javascript
var pickBestTwo = roll.roll('6d20b2'); //roll 6 dice and give me the 2 highest
console.log(pickBestTwo.calculations[1]); //pickBestTwo.calculations[0] is the same as .result, .calculations[1] is prior to the sum operation
```
Processing rolls without parsing a string:
```javascript
var attack = roll.roll({
quantity: 2,
sides: 6,
transformations: [ //can list n-number of pipeline operations to perform on the result
'sum', //take the array of rolled dice and sum them together
['add', 2] //add 2 to the sum
]
});
console.log(attack.result); //random number between 3 and 8 (inclusive)
```
Using custom transformations:
```javascript
var dropOnes = function(results){
return results.filter(function (result) {
return result !== 1;
});
};
var noOnes = roll.roll({
quantity: 5,
sides: 4,
transformations: [
dropOnes, // remove any 1s because we have teh lucky bootz
'sum'
]
});
```
Using a custom seed:
```javascript
var srand = require('srand'); //https://github.com/isaacs/node-srand (npm install srand)
srand.seed(1000);
roll = new Roll(function () {
return srand.random();
});
console.log(roll.roll('2d6+5').result);
```
Validating user input:
```javascript
var userInput = 'this isn\'t a valid roll',
valid = roll.validate(userInput);
if (!valid) {
console.error('"%s" is not a valid input string for node-roll!', userInput);
}
```
## Credits
Inspired by [Phillip Newton's Games::Dice](http://search.cpan.org/~pne/Games-Dice-0.02/Dice.pm).
## License
[MIT License](http://www.opensource.org/licenses/mit-license.php)
## Author
[Troy Goode](https://github.com/TroyGoode) ([troygoode@gmail.com](mailto:troygoode@gmail.com))
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
var InvalidInputError = require('./input-error.js'),
transformationFunctions = require('./transforms'),
transformationKeys = require('./keys'),
regex = /^(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?(([\+\-\/\*])(\d+|(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?))*$/,
roll,
cleaner,
sumResult = false,
filler = [];
roll = function (random) {
this.random = random || Math.random.bind(Math);
};
roll.prototype.validate = function (s) {
return regex.test(s);
};
roll.prototype.parse = function (s) {
if (!this.validate(s)) {
throw new InvalidInputError(s);
}
var match = regex.exec(s),
quantity = match[1], // 2d20+3 => 2
sides = match[2], // 2d20+3 => 20
hasTransformation = !!match[3], // 2d20+3 => true
operator,
transformationParameter,
transforms = [],
opIndex = 0,
segments = s.split(/[\+\-]/),
outsideRoll,
seg;
if (segments[0].indexOf('b') > -1 || segments[0].indexOf('w') > -1) {
transforms.push(transformationKeys[match[4]](parseInt(match[5], 10)));
}
for (seg = 1; seg < segments.length; seg += 1) {
opIndex += segments[seg - 1].length;
operator = s[opIndex]; // 2d20+3 => "+"
opIndex += 1;
transformationParameter = segments[seg]; // 2d20+3 => 3
if (transformationParameter.indexOf('d') > -1) { // perform a roll
outsideRoll = this.roll(transformationParameter, true);
transforms.push(transformationKeys[operator](outsideRoll.result));
} else {
transforms.push(transformationKeys[operator](parseInt(transformationParameter, 10)));
}
}
return {
quantity: quantity ? parseInt(quantity, 10) : 1,
sides: sides === '%' ? 100 : parseInt(sides, 10),
transformations: hasTransformation || transforms.length > 0 ? transforms.length > 0 ? transforms : transformationKeys[match[4]](parseInt(match[5], 10)) : ['sum'],
toString: function () {
return s;
}
};
};
roll.prototype.roll = function (input, invokedByParse) {
if (!input) {
throw new InvalidInputError();
} else if (typeof input === 'string') {
input = this.parse(input);
}
var rolled = [],
calculations = [],
carryFiller = [];
while (rolled.length < input.quantity) {
rolled.push(Math.floor((this.random() * input.sides) + 1));
}
filler.push(rolled);
calculations = input.transformations.reduce(function (previous, transformation) {
var transformationFunction,
transformationAdditionalParameter,
sumParam = false;
if (typeof transformation === 'function') { // lets you pass something custom in
transformationFunction = transformation;
} else if (typeof transformation === 'string') { // "sum"
transformationFunction = transformationFunctions[transformation];
} else if (transformation instanceof Array) { // ["add", 3]
if (transformation[0] instanceof Array) {
sumResult = true;
cleaner = transformation[1];
transformation = transformation[0];
} else if (transformation[1] instanceof Array) {
sumParam = true;
cleaner = transformation[0];
transformation = transformation[1];
}
transformationFunction = transformationFunctions[transformation[0]]; // fn for "add"
transformationAdditionalParameter = transformation[1];
}
if (sumParam === true && previous[0] instanceof Array) {
previous[0] = transformationFunctions[cleaner](previous[0]);
}
previous.unshift(transformationFunction(previous[0], transformationAdditionalParameter));
return previous;
}, [rolled]);
if (sumResult === true && calculations[0] instanceof Array) {
calculations[1] = calculations[0];
calculations[0] = transformationFunctions[cleaner](calculations[0]);
}
if (!invokedByParse) {
if (filler.length > 1) {
filler.unshift(filler.pop());
}
carryFiller = filler.length === 1 ? filler[0] : filler;
filler = [];
}
return {
input: input,
calculations: calculations,
rolled: carryFiller,
result: calculations[0]
};
};
module.exports = roll;
module.exports.InvalidInputError = InvalidInputError;
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
var util = require('util');
function InvalidInputError(input) {
this.name = 'InvalidInputError';
if (input) {
this.message = util.format('"%s" is not a valid input string for node-roll.', input);
} else {
this.message = 'No input string was supplied to node-roll.';
}
this.input = input;
}
InvalidInputError.prototype = new Error();
InvalidInputError.prototype.constructor = InvalidInputError;
module.exports = InvalidInputError;
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = {
'+': function (value) {
return [
'sum',
['add', value]
];
},
'-': function (value) {
return [
'sum',
['subtract', value]
];
},
'*': function (value) {
return [
'sum',
['multiply', value]
];
},
'/': function (value) {
return [
'sum',
['divide', value]
];
},
'b': function (value) {
return [
['best-of', value],
'sum'
];
},
'w': function (value) {
return [
['worst-of', value],
'sum'
];
}
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number + value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (rolledDice, value) {
var result = [],
sorted = rolledDice.sort(function (a, b) {
return b - a;
}),
i;
for (i = 0; i < sorted.length && i < (value / 1); i = i + 1) {
result.push(sorted[i]);
}
return result;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number / value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
module.exports = {
add: require('./add'),
'best-of': require('./best-of'),
divide: require('./divide'),
multiply: require('./multiply'),
subtract: require('./subtract'),
sum: require('./sum'),
'worst-of': require('./worst-of')
};
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number * value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number - value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (rolledDice) {
return rolledDice.reduce(function (sum, value) {
return sum + value;
}, 0);
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (rolledDice, value) {
var result = [],
sorted = rolledDice.sort(function (a, b) {
return a - b;
}),
i;
for (i = 0; i < sorted.length && i < (value / 1); i = i + 1) {
result.push(sorted[i]);
}
return result;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true, describe: true, it: true, beforeEach: true, __dirname: true, console: true, process: true */
(function () {
'use strict';
var exec = require('child_process').exec,
should = require('should'),
FakeRandom = require('./fake-random'),
random = new FakeRandom([ // can only test this library if we make things not actually random
0.24, // 20 * .24 => 4.8 => 5
0.62, // 20 * .62 => 12.4 => 13
0.51, // 20 * .51 => 10.2 => 11
0.13, // 20 * .13 => 2.6 => 3
0.66, // 20 * .66 => 13.2 => 14
0.33, // 20 * .66 => 6.6 => 7
0.12 // 20 * .12 => 2.4 => 2
]);
describe('bin/roll', function () {
beforeEach(random.reset.bind(random));
it('bin/roll garbage', function (done) {
exec((process.platform === 'win32' ? 'node ././bin/roll' : __dirname + '/../bin/roll') + ' garbage', function (err, stdout, stderr) {
if (err) {
should.exist(err);
stderr.should.eql('"garbage" is not a valid input string for node-roll.\n');
return done();
}
done('Should not be reachable.');
});
});
it('bin/roll 2d20', function (done) {
exec((process.platform === 'win32' ? 'node ././bin/roll' : __dirname + '/../bin/roll') + ' 2d20', function (err, stdout, stderr) {
if (err) {
return done(err);
}
/^\d+\n$/.test(stdout).should.eql(true);
done();
});
});
it('bin/roll -d 2d20', function (done) {
exec((process.platform === 'win32' ? 'node ././bin/roll' : __dirname + '/../bin/roll') + ' -d 2d20', function (err, stdout, stderr) {
if (err) {
return done(err);
}
stdout.should.match(/^Dice: (\d+)(,\s*\d+)*\nTotal: \d+\n$/);
done();
});
});
});
}());
+5
-2
{
"name": "roll",
"version": "1.2.0",
"version": "1.3.0",
"author": "Troy Goode <troygoode@gmail.com> (https://github.com/troygoode/)",

@@ -36,3 +36,3 @@ "description": "node.js package for rolling dice and adding modifiers. ex: 2d6+1",

"lint": "^1.1.2",
"mocha": "^3.0.2",
"mocha": "^8.2.1",
"should": "^11.1.0"

@@ -46,3 +46,6 @@ },

"roll": "bin/roll"
},
"dependencies": {
"minimist": "^1.2.5"
}
}

@@ -6,6 +6,4 @@ /*jslint indent: 2*/

var exec = require('child_process').exec,
var Roll = require('../src'),
should = require('should'),
fs = require('fs'),
Roll = require('../lib'),
FakeRandom = require('./fake-random'),

@@ -64,2 +62,11 @@ random = new FakeRandom([ // can only test this library if we make things not actually random

});
it('issue #11', function () {
// https://github.com/troygoode/node-roll/issues/11
var result = roll.roll('2d20w1+5');
result.rolled.length.should.equal(2);
result.rolled[0].should.equal(5);
result.rolled[1].should.equal(13);
result.result.should.equal(10); // worst(5, 13) + 5 = 5 + 5 = 10
});
});

@@ -183,2 +190,12 @@

it('2d20b1+1d4', function () {
var result = roll.roll('2d20b1+1d4'); // 5, 13, 11
result.result.should.equal(14);
});
it('2d20w1+1d4', function () {
var result = roll.roll('2d20w1+1d4'); // 5, 13, 11
result.result.should.equal(12);
});
it('validates input', function (done) {

@@ -202,24 +219,3 @@ try {

it('bin/roll garbage', function (done) {
exec((process.platform === 'win32' ? 'node ././bin/roll' : __dirname + '/../bin/roll') + ' garbage', function (err, stdout, stderr) {
if (err) {
should.exist(err);
stderr.should.eql('"garbage" is not a valid input string for node-roll.\n');
return done();
}
done('Should not be reachable.');
});
});
it('bin/roll 2d20', function (done) {
exec((process.platform === 'win32' ? 'node ././bin/roll' : __dirname + '/../bin/roll') + ' 2d20', function (err, stdout, stderr) {
if (err) {
return done(err);
}
/^\d+\n$/.test(stdout).should.eql(true);
done();
});
});
});
}());

Sorry, the diff of this file is not supported yet

/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
var InvalidInputError = require('./input-error.js'),
transformationFunctions = require('./transforms'),
transformationKeys = require('./keys'),
regex = /^(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?(([\+\-\/\*])(\d+|(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?))*$/,
roll,
cleaner,
sumResult = false,
filler = [];
roll = function (random) {
this.random = random || Math.random.bind(Math);
};
roll.prototype.validate = function (s) {
return regex.test(s);
};
roll.prototype.parse = function (s) {
if (!this.validate(s)) {
throw new InvalidInputError(s);
}
var match = regex.exec(s),
quantity = match[1], // 2d20+3 => 2
sides = match[2], // 2d20+3 => 20
hasTransformation = !!match[3], // 2d20+3 => true
operator,
transformationParameter,
transforms = [],
opIndex = 0,
segments = s.split(/[\+\-]/),
outsideRoll,
seg;
if (segments[0].indexOf('b') > -1) {
transforms.push(transformationKeys[match[4]](parseInt(match[5], 10)));
}
for (seg = 1; seg < segments.length; seg += 1) {
opIndex += segments[seg - 1].length;
operator = s[opIndex]; // 2d20+3 => "+"
opIndex += 1;
transformationParameter = segments[seg]; // 2d20+3 => 3
if (transformationParameter.indexOf('d') > -1) { // perform a roll
outsideRoll = this.roll(transformationParameter, true);
transforms.push(transformationKeys[operator](outsideRoll.result));
} else {
transforms.push(transformationKeys[operator](parseInt(transformationParameter, 10)));
}
}
return {
quantity: quantity ? parseInt(quantity, 10) : 1,
sides: sides === '%' ? 100 : parseInt(sides, 10),
transformations: hasTransformation || transforms.length > 0 ? transforms.length > 0 ? transforms : transformationKeys[match[4]](parseInt(match[5], 10)) : ['sum'],
toString: function () {
return s;
}
};
};
roll.prototype.roll = function (input, invokedByParse) {
if (!input) {
throw new InvalidInputError();
} else if (typeof input === 'string') {
input = this.parse(input);
}
var rolled = [],
calculations = [],
carryFiller = [];
while (rolled.length < input.quantity) {
rolled.push(Math.floor((this.random() * input.sides) + 1));
}
filler.push(rolled);
calculations = input.transformations.reduce(function (previous, transformation) {
var transformationFunction,
transformationAdditionalParameter,
sumParam = false;
if (typeof transformation === 'function') { // lets you pass something custom in
transformationFunction = transformation;
} else if (typeof transformation === 'string') { // "sum"
transformationFunction = transformationFunctions[transformation];
} else if (transformation instanceof Array) { // ["add", 3]
if (transformation[0] instanceof Array) {
sumResult = true;
cleaner = transformation[1];
transformation = transformation[0];
} else if (transformation[1] instanceof Array) {
sumParam = true;
cleaner = transformation[0];
transformation = transformation[1];
}
transformationFunction = transformationFunctions[transformation[0]]; // fn for "add"
transformationAdditionalParameter = transformation[1];
}
if (sumParam === true && previous[0] instanceof Array) {
previous[0] = transformationFunctions[cleaner](previous[0]);
}
previous.unshift(transformationFunction(previous[0], transformationAdditionalParameter));
return previous;
}, [rolled]);
if (sumResult === true && calculations[0] instanceof Array) {
calculations[1] = calculations[0];
calculations[0] = transformationFunctions[cleaner](calculations[0]);
}
if (!invokedByParse) {
if (filler.length > 1) {
filler.unshift(filler.pop());
}
carryFiller = filler.length === 1 ? filler[0] : filler;
filler = [];
}
return {
input: input,
calculations: calculations,
rolled: carryFiller,
result: calculations[0]
};
};
module.exports = roll;
module.exports.InvalidInputError = InvalidInputError;
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
var util = require('util');
function InvalidInputError(input) {
this.name = 'InvalidInputError';
if (input) {
this.message = util.format('"%s" is not a valid input string for node-roll.', input);
} else {
this.message = 'No input string was supplied to node-roll.';
}
this.input = input;
}
InvalidInputError.prototype = new Error();
InvalidInputError.prototype.constructor = InvalidInputError;
module.exports = InvalidInputError;
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = {
'+': function (value) {
return [
'sum',
['add', value]
];
},
'-': function (value) {
return [
'sum',
['subtract', value]
];
},
'*': function (value) {
return [
'sum',
['multiply', value]
];
},
'/': function (value) {
return [
'sum',
['divide', value]
];
},
'b': function (value) {
return [
['best-of', value],
'sum'
];
},
'w': function (value) {
return [
['worst-of', value],
'sum'
];
}
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number + value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (rolledDice, value) {
var result = [],
sorted = rolledDice.sort(function (a, b) {
return b - a;
}),
i;
for (i = 0; i < sorted.length && i < (value / 1); i = i + 1) {
result.push(sorted[i]);
}
return result;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number / value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
module.exports = {
add: require('./add'),
'best-of': require('./best-of'),
divide: require('./divide'),
multiply: require('./multiply'),
subtract: require('./subtract'),
sum: require('./sum'),
'worst-of': require('./worst-of')
};
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number * value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (number, value) {
return number - value;
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (rolledDice) {
return rolledDice.reduce(function (sum, value) {
return sum + value;
}, 0);
};
}());
/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
'use strict';
module.exports = function (rolledDice, value) {
var result = [],
sorted = rolledDice.sort(function (a, b) {
return a - b;
}),
i;
for (i = 0; i < sorted.length && i < (value / 1); i = i + 1) {
result.push(sorted[i]);
}
return result;
};
}());

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet