import Alea from "alea";
import * as Simplex from "simplex-noise";
/**
* A seedable random instance
*/
class SeededRandom {
/**
* @param {String|Number} seed
*/
constructor(seed = null) {
this.gen = new Alea(seed === null ? Math.random() : seed);
}
/**
* Get a decimal number between min and max
* @param {Number} min
* @param {Number} max
* @returns {Number}
*/
float(min, max) {
if (typeof min == "undefined" || typeof max == "undefined") return this.gen();
if (max < min) {
let t = min;
min = max;
max = t;
}
return min + this.gen() * (max - min);
}
/**
* Get a boolean
* @returns {Boolean}
*/
bool() {
return this.chance(0.5);
}
/**
* Get a boolean with a defined chance
* @returns {Boolean}
*/
chance(p = 0.5) {
return this.gen() < p;
}
/**
* Get a random entry of an array
* @param {Array} arr
* @param {Boolean} remove remove the entry from the array
* @returns {mixed} an entry of the array
*/
entry(arr, remove = false) {
if (!arr.length) throw new Error("Cannot get entry of empty array");
let i = Math.floor(this.float() * arr.length);
let item = arr[i];
if (remove) arr.splice(i, 1);
return item;
}
/**
*
* @param {Number} min
* @param {Number} max
* @returns {Number} an integer
*/
int(min, max) {
if (max < min) {
let t = min;
min = max;
max = t;
}
return Math.floor(min + Math.floor(this.float() * (max - min + 1)));
}
/**
* Return a key from a weighted map, this is useful for dynamic loot tables, the weights do not need to add up to 100%.
* The following example demonstrates how the propability of the items is calculated by the weight
*
* @example
* let r = new SeededRandom("seed");
*
* let loot = {
* stone_sword: 10,
* diamond_axe: 5,
* wooden_sword: 15
* }
*
* let item = r.weighted(loot);
*
* // "wooden_sword" with a propability of 50%
* // "diamond_axe" with a propability of 16.6%
* // "stone_sword" with a propability of 33.3%
*
* @param {Object} map the weight map
* @returns {String} a key of the map
*/
weighted(map) {
let arr = [];
let sum = 0;
for (const m in map) {
arr.push({
chance: map[m],
ret: m
});
sum += map[m];
}
let s = this.int(0, sum);
for (let i = 0; i < arr.length; i++) {
s -= arr[i].chance;
if (s <= 0) return arr[i].ret;
}
}
/**
* Get a simplex noise instance
* @returns {Simplex}
* @see https://www.npmjs.com/package/simplex-noise
*/
getSimplex() {
let s = new Simplex(this.gen);
//TODO this needs an own class
s.sumOcatave = function(x, y, scale = 0.1, num_iterations = 16, persistence = 0.5, low = 0, high = 1) {
let maxAmp = 0;
let amp = 1;
let freq = scale;
let noise = 0;
//add successively smaller, higher-frequency terms
for (let i = 0; i < num_iterations; ++i) {
noise += s.noise2D(x * freq, y * freq) * amp
maxAmp += amp
amp *= persistence
freq *= 2
}
//take the average value of the iterations
noise /= maxAmp
//normalize the result
noise = noise * (high - low) / 2 + (high + low) / 2
return noise
};
return s;
}
/**
* Export the state of the internal alea instance
* @returns {Array}
* @see https://www.npmjs.com/package/alea
*/
exportState() {
return this.gen.exportState();
}
/**
* Import the state of the internal alea instance
* @param {Array} state
* @returns {Alea} the alea instance
* @see https://www.npmjs.com/package/alea
*/
importState(state) {
return this.gen = Alea.importState(state);
}
/**
* Create a new instance from an exported state
* @param {Array} state
* @returns {SeededRandom}
*/
static fromState(state) {
let s = new SeededRandom();
s.importState(state);
return s;
}
}
export default SeededRandom;