2021-02-15 13:34:05 +00:00
|
|
|
export type RandomParameters = {
|
|
|
|
modulus: number;
|
|
|
|
multiplier: number;
|
|
|
|
increment: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
const NUMERICAL_RECIPES_PARAMETERS: RandomParameters = {
|
|
|
|
modulus: Math.pow(2, 32),
|
|
|
|
multiplier: 1664525,
|
|
|
|
increment: 1013904223,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A simple linear congruential random number generator, as described in
|
|
|
|
* https://en.wikipedia.org/wiki/Linear_congruential_generator.
|
|
|
|
*/
|
|
|
|
export class Random {
|
|
|
|
private latestSeed: number;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
readonly seed: number = Date.now(),
|
|
|
|
readonly params: RandomParameters = NUMERICAL_RECIPES_PARAMETERS
|
|
|
|
) {
|
|
|
|
this.latestSeed = seed;
|
|
|
|
}
|
|
|
|
|
2021-03-07 01:35:58 +00:00
|
|
|
/**
|
|
|
|
* Create an exact replica of this instance.
|
|
|
|
*/
|
|
|
|
clone(): Random {
|
|
|
|
return new Random(this.latestSeed, this.params);
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:34:05 +00:00
|
|
|
/**
|
|
|
|
* Return a random number that is greater than or equal to zero, and less
|
|
|
|
* than one.
|
|
|
|
*/
|
|
|
|
next(): number {
|
|
|
|
this.latestSeed =
|
|
|
|
(this.params.multiplier * this.latestSeed + this.params.increment) %
|
|
|
|
this.params.modulus;
|
|
|
|
return this.latestSeed / this.params.modulus;
|
|
|
|
}
|
|
|
|
|
2021-03-07 01:35:58 +00:00
|
|
|
/**
|
|
|
|
* Return a random boolean with the given probability of being true.
|
|
|
|
*/
|
|
|
|
bool(trueProbability: number = 0.5): boolean {
|
|
|
|
return this.next() < trueProbability;
|
|
|
|
}
|
|
|
|
|
2021-02-15 13:34:05 +00:00
|
|
|
/**
|
2021-02-23 02:50:14 +00:00
|
|
|
* Return a random item from the given array. If the array is
|
|
|
|
* empty, an exception is thrown.
|
2021-02-15 13:34:05 +00:00
|
|
|
*/
|
|
|
|
choice<T>(array: T[]): T {
|
2021-02-23 02:50:14 +00:00
|
|
|
if (array.length === 0) {
|
|
|
|
throw new Error("Cannot choose randomly from an empty array!");
|
|
|
|
}
|
2021-02-15 13:34:05 +00:00
|
|
|
const idx = Math.floor(this.next() * array.length);
|
|
|
|
return array[idx];
|
|
|
|
}
|
2021-02-23 02:50:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempt to randomly choose *at most* the given number of unique items from
|
|
|
|
* the array. If the array is too small, fewer items are returned.
|
|
|
|
*/
|
|
|
|
uniqueChoices<T>(array: T[], howMany: number): T[] {
|
|
|
|
let choicesLeft = [...array];
|
|
|
|
const result: T[] = [];
|
|
|
|
|
|
|
|
for (let i = 0; i < howMany; i++) {
|
|
|
|
if (choicesLeft.length === 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const choice = this.choice(choicesLeft);
|
|
|
|
choicesLeft = choicesLeft.filter((item) => item !== choice);
|
|
|
|
result.push(choice);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2021-02-15 13:34:05 +00:00
|
|
|
}
|