npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@justkd/roll

v2.1.6

Published

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.

Downloads

393

Readme

@justkd/roll

CircleCI

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.

Notes and Todo

  • .random - add tests for optional gaussian functionality
  • does setting maxHistory reset the history array? should it? if not how should it handle truncating?
  • audit/optimize tests - originally (mostly) for older version are they all still valid?

Install

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 === 99`

roll.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