UNPKG

boardgame.io

Version:
230 lines (225 loc) 7.06 kB
'use strict'; // Inlined version of Alea from https://github.com/davidbau/seedrandom. // Converted to Typescript October 2020. class Alea { constructor(seed) { 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) { 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, t) { t.c = f.c; t.s0 = f.s0; t.s1 = f.s1; t.s2 = f.s2; return t; } function alea(seed, state) { const xg = new Alea(seed); const prng = xg.next.bind(xg); if (state) copy(state, xg); prng.state = () => copy(xg, {}); return prng; } /* * Copyright 2017 The boardgame.io Authors * * Use of this source code is governed by a MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ /** * Random * * Calls that require a pseudorandom number generator. * Uses a seed from ctx, and also persists the PRNG * state in ctx so that moves can stay pure. */ class Random { /** * constructor * @param {object} ctx - The ctx object to initialize from. */ constructor(state) { // If we are on the client, the seed is not present. // Just use a temporary seed to execute the move without // crashing it. The move state itself is discarded, // so the actual value doesn't matter. this.state = state || { seed: '0' }; this.used = false; } /** * Generates a new seed from the current date / time. */ static seed() { return Date.now().toString(36).slice(-10); } isUsed() { return this.used; } getState() { return this.state; } /** * Generate a random number. */ _random() { this.used = true; const R = this.state; const seed = R.prngstate ? '' : R.seed; const rand = alea(seed, R.prngstate); const number = rand(); this.state = { ...R, prngstate: rand.state(), }; return number; } api() { const random = this._random.bind(this); const SpotValue = { D4: 4, D6: 6, D8: 8, D10: 10, D12: 12, D20: 20, }; // Generate functions for predefined dice values D4 - D20. const predefined = {}; for (const key in SpotValue) { const spotvalue = SpotValue[key]; predefined[key] = (diceCount) => { return diceCount === undefined ? Math.floor(random() * spotvalue) + 1 : Array.from({ length: diceCount }).map(() => Math.floor(random() * spotvalue) + 1); }; } function Die(spotvalue = 6, diceCount) { return diceCount === undefined ? Math.floor(random() * spotvalue) + 1 : Array.from({ length: diceCount }).map(() => Math.floor(random() * spotvalue) + 1); } return { /** * Similar to Die below, but with fixed spot values. * Supports passing a diceCount * if not defined, defaults to 1 and returns the value directly. * if defined, returns an array containing the random dice values. * * D4: (diceCount) => value * D6: (diceCount) => value * D8: (diceCount) => value * D10: (diceCount) => value * D12: (diceCount) => value * D20: (diceCount) => value */ ...predefined, /** * Roll a die of specified spot value. * * @param {number} spotvalue - The die dimension (default: 6). * @param {number} diceCount - number of dice to throw. * if not defined, defaults to 1 and returns the value directly. * if defined, returns an array containing the random dice values. */ Die, /** * Generate a random number between 0 and 1. */ Number: () => { return random(); }, /** * Shuffle an array. * * @param {Array} deck - The array to shuffle. Does not mutate * the input, but returns the shuffled array. */ Shuffle: (deck) => { const clone = [...deck]; let sourceIndex = deck.length; let destinationIndex = 0; const shuffled = Array.from({ length: sourceIndex }); while (sourceIndex) { const randomIndex = Math.trunc(sourceIndex * random()); shuffled[destinationIndex++] = clone[randomIndex]; clone[randomIndex] = clone[--sourceIndex]; } return shuffled; }, _private: this, }; } } /* * Copyright 2018 The boardgame.io Authors * * Use of this source code is governed by a MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT. */ const RandomPlugin = { name: 'random', noClient: ({ api }) => { return api._private.isUsed(); }, flush: ({ api }) => { return api._private.getState(); }, api: ({ data }) => { const random = new Random(data); return random.api(); }, setup: ({ game }) => { let { seed } = game; if (seed === undefined) { seed = Random.seed(); } return { seed }; }, playerView: () => undefined, }; exports.RandomPlugin = RandomPlugin; exports.alea = alea;