103 lines
3.5 KiB
TypeScript
103 lines
3.5 KiB
TypeScript
export const nextFrame = async (): Promise<number> => new Promise((resolve) => requestAnimationFrame(resolve));
|
|
|
|
export const randInt = (min: number, max: number) => Math.round(min + (max - min - 1) * Math.random());
|
|
export const randBool = () => Math.random() < 0.5;
|
|
export const choice = <T>(array: T[]): T => array[randInt(0, array.length)];
|
|
export const weightedChoice = <T extends string | number>(options: [T, number][] | Partial<Record<T, number>>): T | null => {
|
|
if (!Array.isArray(options)) {
|
|
options = Object.entries(options) as [T, number][];
|
|
}
|
|
const sum = options.reduce((acc, o) => acc + o[1], 0);
|
|
|
|
const rnd = Math.random() * sum;
|
|
|
|
let weight = 0;
|
|
for (const [item, probability] of options) {
|
|
weight += probability;
|
|
if (rnd < weight) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
export const shuffle = <T>(array: T[]): T[] => {
|
|
const shuffledArray = [...array];
|
|
|
|
for (let i = shuffledArray.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
|
|
}
|
|
|
|
return shuffledArray;
|
|
}
|
|
|
|
export function zip<T1, T2, T3>(a1: Iterable<T1>, a2: Iterable<T2>, a3: Iterable<T3>): Generator<[T1, T2, T3]>;
|
|
export function zip<T1, T2>(a1: Iterable<T1>, a2: Iterable<T2>): Generator<[T1, T2]>;
|
|
export function zip<T1>(a1: Iterable<T1>): Generator<[T1]>;
|
|
export function* zip(...args: Iterable<any>[]) {
|
|
const iterators = args.map(i => i[Symbol.iterator]());
|
|
|
|
while (true) {
|
|
const nextValues = iterators.map(i => i.next());
|
|
if (nextValues.some(v => v.done)) return;
|
|
|
|
yield nextValues.map(v => v.value);
|
|
}
|
|
}
|
|
|
|
export function* enumerate<T>(iterable: Iterable<T>): Generator<[number, T]> {
|
|
let i = 0;
|
|
for (const item of iterable) {
|
|
yield [i++, item];
|
|
}
|
|
}
|
|
|
|
export const range = (size: number | string) => Object.keys((new Array(+size)).fill(0)).map(k => +k);
|
|
export const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));
|
|
export const lerp = (start: number, end: number, t: number) => (start + (end - start) * t);
|
|
|
|
export const prevent = (e: Event) => (e.preventDefault(), false);
|
|
|
|
export const intHash = (seed: number, ...parts: number[]) => {
|
|
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
|
for (let i = 0; i < parts.length; i++) {
|
|
const ch = parts[i];
|
|
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
}
|
|
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
return h1;
|
|
};
|
|
export const sinHash = (...data: number[]) => data.reduce((hash, n) => Math.sin((hash * 123.12 + n) * 756.12), 0) / 2 + 0.5;
|
|
export const throttle = function <T, A extends unknown[], R, F extends (this: T, ...args: A) => R>(func: F, ms: number, trailing = false): F {
|
|
let isThrottled = false;
|
|
let savedResult: R;
|
|
let savedThis: T;
|
|
let savedArgs: A | undefined;
|
|
|
|
const wrapper: F = function (...args: A) {
|
|
if (isThrottled) {
|
|
savedThis = this;
|
|
savedArgs = args;
|
|
} else {
|
|
savedResult = func.apply(this, args);
|
|
savedArgs = undefined;
|
|
|
|
isThrottled = true;
|
|
|
|
setTimeout(function () {
|
|
isThrottled = false;
|
|
if (trailing && savedArgs) {
|
|
savedResult = wrapper.apply(savedThis, savedArgs);
|
|
}
|
|
}, ms);
|
|
}
|
|
|
|
return savedResult;
|
|
} as F;
|
|
|
|
return wrapper;
|
|
}
|