Class representing a pseudorandom number manager. Includes Mersenne Twister uniform distribution, Box Mueller gaussian distribution, n-sided die rolling, history of variable max size, elementary statistics, and scale/clip/round convenience functions.
npm install @justkd/roll!npm
Class representing a pseudorandom number manager. Includes Mersenne Twister uniform distribution, Box Mueller gaussian distribution, n-sided die rolling, history of variable max size, elementary statistics, and scale/clip/round convenience functions. Each instance
can be seeded but also automatically generates its own random seed on creation.
.random - add tests for optional gaussian functionality
npm i @justkd/roll
`
`
yarn add @justkd/roll
`Exports
`
import { Roll } from '@justkd/roll';
import type { Seed } from '@justkd/roll';
`Basic Use - Static Methods
Use the static method .random to generate a random number in the range 0-1.
`
const rand = Roll.random(); // float 0-1
`
Use the static method .d to generate a random integer in the range 1-n.
`
const d20 = Roll.d(20); // int 1-20
`Basic Use - Instance Methods
Use instance methods to track history and expose statistic functions.
`
const roll = new Roll();let i = 10;
while (i--) roll.random();
const history = roll.history(); // retrieve the internal history
const avg = roll.mean(); // retrieve the current average
`Extended Use
Roll can use either uniform (normal) or gaussian distribution.
`
const roll = new Roll();/ convenience function, same as
.uniform() /
const random = roll.random(); / random number in the range 0-1, uniform (normal) distribution /
const uniform = roll.uniform();
/ random number in the range 0-1, gaussian distribution, undefined skew (same as 0 skew) /
const gaussian = roll.gaussian();
/ random number in the range 0-1, gaussian distribution, skewed RIGHT (normalized -1 to 1) /
const gaussianSkew = roll.gaussian(0.15);
`Roll can use either uniform (normal) or gaussian distribution (part II).
`
const roll = new Roll();/ random integer in the range 1-6, uniform (normal) distribution /
const d6 = roll.d(6);
/ random integer in the range 1-8, gaussian distribution, no skew /
const d8 = roll.d(8, 0);
/ random integer in the range 1-12, gaussian distribution, skewed LEFT (normalized -1 to 1) /
const d12 = roll.d(12, 0.85);
/ random integer in the range 1-20, gaussian distribution, skewed RIGHT (normalized -1 to 1) /
const d20 = roll.d(20, -0.35);
`Each instance of
Roll can be given a maxHistory.
`
const roll = new Roll({ maxHistory: 1000 });let i = roll.maxHistory();
while (i--) roll.random();
const stats = {
history: roll.history(),
mean: roll.mean(),
median: roll.median(),
modes: roll.modes(),
stdDev: roll.stdDev(), // standard deviation normalized 0-1
};
`Note: While each instance of
Roll will allow using both normal and gaussian distribution, you should use different instances if you need to track the distribution over time.
`
const maxHistory = 1000;const uniformRoll = new Roll({ maxHistory });
const gaussianRoll = new Roll({ maxHistory });
let i = maxHistory;
while (i--) {
uniformRoll.d(10);
gaussianRoll.d(31, 0.24);
};
const uniformStats = {
history: uniformRoll.history(),
mean: uniformRoll.mean(),
median: uniformRoll.median(),
modes: uniformRoll.modes(),
stdDev: uniformRoll.stdDev(),
};
const gaussianStats = {
history: gaussianRoll.history(),
mean: gaussianRoll.mean(),
median: gaussianRoll.median(),
modes: gaussianRoll.modes(),
stdDev: gaussianRoll.stdDev(),
};
`Set the max history in the constructor or with the instance method.
`
/ set the initial maxHistory to 99, default is maxHistory === Infinity /
const roll = new Roll({ maxHistory: 99 });/ returns the current
maxHistory if no param is given /
console.log(roll.maxHistory()); // maxHistory === 99roll.maxHistory(10); // set instance
maxHistory to 10
console.log(roll.maxHistory()); // maxHistory === 10
` Giving
Roll a max history will allow you to calculate rolling statistics.
`
const roll = new Roll({ maxHistory: 10 });setInterval(() => {
const rand = roll.d(6);
const avg = roll.mean();
const history = roll.history();
console.log(rand, avg, history);
}, 1000);
`Round to
n decimal places using the static method Roll.round().
`
const roll = new Roll();const rand = roll.random();
const n = 2;
const rounded = Roll.round(rand, n);
const whole = Roll.round(rand * 100, 0)
// Roll.round(0.75, 0) === 1
// Roll.round(0.5, 0) === 1
// Roll.round(0.49, 0) === 0
// Roll.round(0.75, 1) === 0.8
// Roll.round(0.424242, 3) === 0.424
`Numbers can be scaled from any known range to another using the static method
Roll.scale().
`
const n = Roll.random();
const r1 = [0, 1]; // initial range
const r2 = [5, 72]; // target range
const scaled = Roll.scale(n, r1, r2); // if n === 0.5 then scaled === 33.5// Roll.scale(0.5, [0, 1], [-1, 1]) === 0
// Roll.scale(0.75, [0, 1], [0, 2]) === 1.5
// Roll.scale(55, [2.3, 98.6], [33.42, 87.55]) === 63.04254413291797
// Roll.scale(12975.2123, [0, 9001], [0, 1]) === 1.4415300855460504
`Numbers can be clipped to a minimum and maximum allowed value using the static method
Roll.clip().
`
const n = Roll.random();
const min = 0.2;
const max = 0.8;
const clipped = Roll.clip(n, [min, max]);// Roll.clip(1, [0, 0.5]) === 0.5
// Roll.clip(-1, [1.5, 10]) === 1.5
// Roll.clip(0.5, [0, 1]) === 0.5
`Generate a whole number between 1 and MAX_SAFE_INTEGER using gaussian distribution.
`
const getGaussianWhole = () => {
const n = Roll.random(0);
const scaled = Roll.scale(n, [0, 1], [1, Number.MAX_SAFE_INTEGER]);
const whole = Roll.round(scaled, 0);
return whole;
}
console.log(getGaussianWhole())
`Generate and set a new random seed with the static method
Roll.createRandomSeed() and instance method roll.seed().
`
const roll = new Roll();/ returns the current
seed if no param is given /
console.log(roll.seed());const newSeed = Roll.createRandomSeed();
/ set the seed for the given instance (this will clear the history) /
roll.seed(newSeed);
`API - Instance Methods
`
/**
* Allowed seed types.
* A number, number[], Uint32Array, or undefined representing a seed value.
* @typedef {( number | number[] | Uint32Array | undefined )} Seed
*/
type Seed = number | number[] | Uint32Array | undefined/**
* Generates a 53-bit random real in the interval [0, 1] with uniform distribution.
* @returns {number}
* @readonly
*/
readonly uniform: () => number
/**
* Generates a 53-bit random real in the interval [0, 1] with gaussian distribution.
* @param {number} [skew=0] - In the range [-1,1]. Negative values skew data RIGHT,
* and positive values skew data LEFT.
* @returns {number}
* @readonly
*/
readonly gaussian: (skew: number = 0) => number
/**
* Convenience function. Same as
.gaussian if skew is a number, same as
* .uniform if skew is undefined.
* @param {number} [skew] - In the range [-1, 1]. Negative values skew data RIGHT,
* positive values skew data LEFT.
* @note Pass 0 to use gaussian distribution without skew.
* @returns {number}
* @readonly
*/
readonly random: (skew?: number) => number/**
* Simulates a die-rolling metaphor. First generates a 53-bit random real in the
* interval [0, 1] with uniform or gaussian distribution, then scales it to a range
*
[1, n] where n is the number of sides, then rounds to whole number.
* @param {number} sides - The number of sides the die should represent.
* Allows but ignores decimal places.
* @param {number} [skew] - In the range [-1, 1]. Negative values skew data RIGHT,
* positive values skew data LEFT.
* @note Pass 0 to use gaussian distribution without skew.
* @returns {number}
* @readonly
*/
readonly d: (sides: number, skew?: number) => number/**
* Set the seed or get the current seed if no param is given. Automatically
* clears
history when setting a new seed.
* @param {Seed} [seed] - Unsigned 32-bit integer or number array of arbitrary size/values.
* @returns {Seed} Returns the current seed.
* @readonly
*/
readonly seed: (seed?: Seed) => Seed/**
* Retrieve a copy of the internal history array with no references.
* @returns {number[]} Returns the current
history.
* @readonly
*/
readonly history: () => number[]/**
* Set the maximum
history size or get the current size if no param is given.
* Default on instance creation is Infinity.
* @param {number} [size] - The desired maximum history size.
* @returns {number} Returns the current maxHistory.
* @readonly
*/
readonly maxHistory: (size?: number) => number/**
* Reset the internal
history array. Does not change current maxHistory.
* @readonly
*/
readonly clearHistory: () => void/**
* Calculate the statistical mean of a given
number[] or the current history.
* @param {number[]} [arr] - Target array on which to operate.
* Defaults to the current history if arr is undefined.
* @returns {number}
* @readonly
*/
readonly mean: (arr?: number[]) => number/**
* Calculate the statistical median of a given
number[] or the current history.
* @param {number[]} [arr] - Target array on which to operate.
* Defaults to the current history if arr is undefined.
* @returns {number}
* @readonly
*/
readonly median: (arr?: number[]) => number/**
* Calculate the statistical modes of a given
number[] or the current history.
* @param {number[]} [arr] - Target array on which to operate.
* Defaults to the current history if arr is undefined.
* @returns {number[]}
* @readonly
*/
readonly modes: (arr?: number[]) => number[]/**
* Calculate the standard deviation of a given
number[] or the current history.
* @param {number[]} [arr] - Target array on which to operate.
* Defaults to the current history if arr is undefined.
* @returns {number} Standard deviation normalized [0, 1].
* @readonly
*/
readonly stdDev: (arr?: number[]) => number
`API - Static Methods
`
/**
* Convenience function. Same as .gaussian if skew is a number,
* same as .uniform if skew is undefined.
* @param {number} [skew] - In the range [-1, 1]. Negative values skew data RIGHT,
* positive values skew data LEFT.
* @note Pass 0 to use gaussian distribution without skew.
* @returns {number}
* @static
*/
static random(skew?: number): number/**
* Convenience function to generate a randomly seeded random number
* in the range 1-sides.
* @param {number} sides - The desired number of sides to simulate.
* @param {number} [skew] - In the range [-1,1]. If
skew is a number,
* Roll.d will use gaussian distribution instead of normal distribution.
* @note Pass 0 to use gaussian distribution without skew.
* @returns {number}
* @static
*/
static d(sides: number, skew?: number): number/**
* Generate a random seed array using
window.crypto. Falls back to node.crypto
* or a final fallback to using Math.random() to fill an array.
* @return {number[]} Randomly generated number[] of random size [20,623] and values.
* @static
*/
static createRandomSeed(): number[]/**
* Scale a value from a known range to a new range.
* @param {number} value - The initial value.
* @param {[number, number]} r1 - The initial range [min, max].
* @param {[number, number]} [r2] - The target range [min, max].
* @note Shorthand: If
r1 is not [0, 1] and r2 is undefined,
* the actual r1 is assumed to be [0, 1] and the actual r2
* is assumed to be the given r1.
* @returns {number}
* @static
* @example
* const n = 50
* const scaled = Roll.scale(n, [0, 100], [0, 1]) // scaled === 0.5
* @example
* // we can use the shorthand when we know the input value is in the range [0, 1]
* const n = Roll.random()
* const scaled = Roll.scale(n, [0, 10]) // scaled === 5
*/
static scale(
value: number,
r1: [number, number],
r2: [number, number],
): number/**
* Limit a value to a hard minimum and maximum.
* @param {number} value - The initial value.
* @param {[number, number]} range - Two-element array containing
* the minimum and maximum possible values.
* @returns {number}
* @static
*/
static clip(
value: number,
range: [number, number],
): number
/**
* Round a value to a specific number of places.
* Decimal values < 5 (for any given place) are rounded down.
* @param {number} value - The initial value.
* @param {number} [places=0] - The desired number of decimal places.
*
0 results in a whole number. Default is places=0.
* @returns {number}
* @static
*/
static round(
value: number,
places: number
): number
``