diff --git a/lib/random/random.js b/lib/random/random.js index eb4b901..38e1034 100644 --- a/lib/random/random.js +++ b/lib/random/random.js @@ -7,28 +7,31 @@ const {logger} = require('../logging') class random { /** - * Must be called before any other methods can be called to initialize MersenneTwister. - * @param {number|null|undefined} seed Value to initialize MersenneTwister. + * Must be called before any other methods can be called to initialize MersenneTwister + * @param {number|null|undefined} seed - Value to initialize MersenneTwister */ static init (seed) { if (seed === null || seed === undefined) { random.seed = new Date().getTime() } + random.twister = new MersenneTwister() random.twister.seed(random.seed) } /** - * Returns an integer in [0, limit). Uniform distribution. - * @param limit + * Returns an integer in [0, limit] (uniform distribution) + * @param {number} limit */ static number (limit) { if (!random.twister) { throw new Error('random.init must be called first.') } + if (limit === null || limit === undefined) { limit = 0xffffffff } + let x = (0x100000000 / limit) >>> 0 let y = (x * limit) >>> 0 let r @@ -39,19 +42,20 @@ class random { } /** - * Returns a float in [0, 1). Uniform distribution. + * Returns a float in [0, 1] (uniform distribution) */ static float () { if (!random.twister) { throw new Error('random.init must be called first.') } + return random.twister.real2() } /** - * Returns an integer in [start, limit]. Uniform distribution. - * @param start - * @param limit + * Returns an integer in [start, limit] (uniform distribution) + * @param {number} start + * @param {number} limit */ static range (start, limit) { if (!random.twister) { @@ -66,69 +70,93 @@ class random { /** * Returns a float in [1, limit]. The logarithm has uniform distribution. - * @param {*} limit + * @param {number} limit */ static ludOneTo (limit) { return Math.exp(random.float() * Math.log(limit)) } + /** + * Returns a random index from a list + * @param {Array} list + * @returns list[n] + */ static item (list) { - if (list === undefined || typeof list === 'string' || list.length === undefined) { + if (!Array.isArray(list)) { logger.traceback() throw new TypeError(`random.item() received invalid object: (${list})`) } + return list[random.number(list.length)] } /** - * Returns a random key of a provided object. - * @param {*} obj + * Returns a random key of a provided object + * @param {Object} obj */ static key (obj) { - let list = [] - for (let i in obj) { - list.push(i) - } - return random.item(list) + return random.item(Object.keys(obj)) } /** - * Return a random Boolean value. + * Return a random Boolean value */ static bool () { return random.item([true, false]) } + /** + * Recursively iterate over array until non-array item identified + * If item is a function, evaluate it with no args + * @param {*} obj + * @returns {*} + */ static pick (obj) { + if (!(Array.isArray(obj) || typeof obj === 'function')) { + logger.traceback() + throw new TypeError(`random.pick() received invalid object: (${obj})`) + } + if (typeof obj === 'function') { return obj() - } - if (Array.isArray(obj)) { + } else if (Array.isArray(obj)) { return random.pick(random.item(obj)) } + return obj } - static chance (limit) { - if (limit === null || limit === undefined) { - limit = 2 - } + /** + * Returns a boolean result based on limit + * @param limit + * @returns {boolean} + */ + static chance (limit = 2) { if (isNaN(limit)) { logger.traceback() throw new TypeError(`random.chance() received non-number type: (${limit})`) } + return random.number(limit) === 1 } - static choose (list, flat) { + /** + * Return an item from an array of arrays where the first index in each sub-array denotes the weight + * @param {Array} list - Array of arrays + * @param {Boolean} flat - Indicates whether we should iterate over the arrays recursively + * @returns {*} + */ + static choose (list, flat = false) { if (!(Array.isArray(list))) { logger.traceback() throw new TypeError(`random.choose() received non-array type: (${list})`) } + let total = 0 for (let i = 0; i < list.length; i++) { total += list[i][0] } + let n = random.number(total) for (let i = 0; i < list.length; i++) { if (n < list[i][0]) { @@ -140,9 +168,11 @@ class random { } n = n - list[i][0] } - if (flat === true) { + + if (flat) { return list[0][1] } + return random.pick([list[0][1]]) } @@ -157,6 +187,7 @@ class random { a.push(wa[i].v) } } + return a } @@ -164,6 +195,10 @@ class random { return random.bool() ? obj : '' } + /** + * Returns arr shuffled + * @param arr + */ static shuffle (arr) { let i = arr.length while (i--) { @@ -174,6 +209,11 @@ class random { } } + /** + * Returns a shuffled copy of arr + * @param arr + * @returns {*} + */ static shuffled (arr) { let newArray = arr.slice() random.shuffle(newArray)