diff --git a/src/js/core/rng.js b/src/js/core/rng.js deleted file mode 100644 index 7a6766cd..00000000 --- a/src/js/core/rng.js +++ /dev/null @@ -1,129 +0,0 @@ -// ALEA RNG - -function Mash() { - var n = 0xefc8249d; - return function (data) { - data = data.toString(); - for (var i = 0; i < data.length; i++) { - n += data.charCodeAt(i); - var h = 0.02519603282416938 * n; - n = h >>> 0; - h -= n; - h *= n; - n = h >>> 0; - h -= n; - n += h * 0x100000000; // 2^32 - } - return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 - }; -} - -/** - * @param {number|string} seed - */ -function makeNewRng(seed) { - // Johannes Baagøe , 2010 - var c = 1; - var mash = Mash(); - let s0 = mash(" "); - let s1 = mash(" "); - let s2 = mash(" "); - - s0 -= mash(seed); - if (s0 < 0) { - s0 += 1; - } - s1 -= mash(seed); - if (s1 < 0) { - s1 += 1; - } - s2 -= mash(seed); - if (s2 < 0) { - s2 += 1; - } - mash = null; - - var random = function () { - var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 - s0 = s1; - s1 = s2; - return (s2 = t - (c = t | 0)); - }; - - random.exportState = function () { - return [s0, s1, s2, c]; - }; - - random.importState = function (i) { - s0 = +i[0] || 0; - s1 = +i[1] || 0; - s2 = +i[2] || 0; - c = +i[3] || 0; - }; - - return random; -} - -export class RandomNumberGenerator { - /** - * - * @param {number|string=} seed - */ - constructor(seed) { - this.internalRng = makeNewRng(seed || Math.random()); - } - - /** - * Re-seeds the generator - * @param {number|string} seed - */ - reseed(seed) { - this.internalRng = makeNewRng(seed || Math.random()); - } - - /** - * @returns {number} between 0 and 1 - */ - next() { - return this.internalRng(); - } - - /** - * Random choice of an array - * @param {array} array - */ - choice(array) { - const index = this.nextIntRange(0, array.length); - return array[index]; - } - - /** - * @param {number} min - * @param {number} max - * @returns {number} Integer in range [min, max[ - */ - nextIntRange(min, max) { - assert(Number.isFinite(min), "Minimum is no integer"); - assert(Number.isFinite(max), "Maximum is no integer"); - assert(max > min, "rng: max <= min"); - return Math.floor(this.next() * (max - min) + min); - } - - /** - * @param {number} min - * @param {number} max - * @returns {number} Number in range [min, max[ - */ - nextRange(min, max) { - assert(max > min, "rng: max <= min"); - return this.next() * (max - min) + min; - } - - /** - * Updates the seed - * @param {number} seed - */ - setSeed(seed) { - this.internalRng = makeNewRng(seed); - } -} diff --git a/src/js/core/rng.ts b/src/js/core/rng.ts new file mode 100644 index 00000000..50bed98d --- /dev/null +++ b/src/js/core/rng.ts @@ -0,0 +1,81 @@ +class Alea { + private n = 0xefc8249d; + private c = 1; + + private s0: number; + private s1: number; + private s2: number; + + constructor(seed: string) { + // Johannes Baagøe , 2010 + this.s0 = this.mash(" "); + this.s1 = this.mash(" "); + this.s2 = this.mash(" "); + + this.s0 -= this.mash(seed); + if (this.s0 < 0) { + this.s0 += 1; + } + this.s1 -= this.mash(seed); + if (this.s1 < 0) { + this.s1 += 1; + } + this.s2 -= this.mash(seed); + if (this.s2 < 0) { + this.s2 += 1; + } + } + + protected next(): number { + const t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32 + this.s0 = this.s1; + this.s1 = this.s2; + return (this.s2 = t - (this.c = t | 0)); + } + + private mash(data: string): number { + for (let i = 0; i < data.length; i++) { + this.n += data.charCodeAt(i); + let h = 0.02519603282416938 * this.n; + this.n = h >>> 0; + h -= this.n; + h *= this.n; + this.n = h >>> 0; + h -= this.n; + this.n += h * 0x100000000; // 2^32 + } + return (this.n >>> 0) * 2.3283064365386963e-10; // 2^-32 + } +} + +export class RandomNumberGenerator extends Alea { + constructor(seed: string | number = Math.random()) { + super(seed.toString()); + } + + /** + * Random choice of an array + */ + choice(array: T[]): T { + const index = this.nextIntRange(0, array.length); + return array[index]; + } + + /** + * @returns Integer in range [min, max] + */ + nextIntRange(min: number, max: number): number { + assert(Number.isFinite(min), "Minimum is no integer"); + assert(Number.isFinite(max), "Maximum is no integer"); + assert(max > min, "rng: max <= min"); + return Math.floor(this.next() * (max - min) + min); + } + + /** + * @returns Number in range [min, max] + */ + nextRange(min: number, max: number): number { + assert(max > min, "rng: max <= min"); + return this.next() * (max - min) + min; + } +}