boardgame.io
Version:
library for turn-based games
110 lines (97 loc) • 2.88 kB
text/typescript
// Inlined version of Alea from https://github.com/davidbau/seedrandom.
// Converted to Typescript October 2020.
/*
* Copyright 2015 David Bau.
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software
* and associated documentation files (the "Software"),
* to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
export interface AleaState {
c: number;
s0: number;
s1: number;
s2: number;
}
class Alea {
c: number;
s0: number;
s1: number;
s2: number;
constructor(seed: string | number) {
const mash = Mash();
// Apply the seeding algorithm from Baagoe.
this.c = 1;
this.s0 = mash(' ');
this.s1 = mash(' ');
this.s2 = mash(' ');
this.s0 -= mash(seed);
if (this.s0 < 0) {
this.s0 += 1;
}
this.s1 -= mash(seed);
if (this.s1 < 0) {
this.s1 += 1;
}
this.s2 -= mash(seed);
if (this.s2 < 0) {
this.s2 += 1;
}
}
next() {
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 = Math.trunc(t)));
}
}
function Mash() {
let n = 0xefc8249d;
const mash = function (data: string | number) {
const str = data.toString();
for (let i = 0; i < str.length; i++) {
n += str.charCodeAt(i);
let 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
};
return mash;
}
function copy(f: AleaState, t: Partial<AleaState>) {
t.c = f.c;
t.s0 = f.s0;
t.s1 = f.s1;
t.s2 = f.s2;
return t as AleaState;
}
type PRNG = Alea['next'] & { state?: () => AleaState };
export function alea(seed: string | number, state?: AleaState): PRNG {
const xg = new Alea(seed);
const prng = xg.next.bind(xg) as PRNG;
if (state) copy(state, xg);
prng.state = () => copy(xg, {});
return prng;
}