export type RNG = (max?: number, min?: number) => number export function random(max: number = 1, min: number = 0, rng?: RNG): number { if (rng) return rng(max, min) if (max < min) [max, min] = [min, max] return Math.floor(Math.random() * (max - min + 1)) + min } export function pick(items: T[], rng?: RNG) { if (items.length === 0) return null return items[random(items.length - 1, 0, rng)] ?? null } export type WeightItem = readonly [T, number] export function pickWeight(items: WeightItem[], rng?: RNG) { if (items.length === 0) return null const totalWeight = items.reduce((sum, [, weight]) => sum + weight, 0) let mark = random(totalWeight - 1, 0, rng) for (const [item, weight] of items) { if (mark < weight) return item mark -= weight } return null } export function sum(arr: number[]) { return arr.reduce((sum, v) => sum + v, 0) } export function shuffle(array: T[], rng?: RNG): T[] { const result = [...array] for (let i = result.length - 1; i > 0; i--) { const j = random(i, 0, rng) const temp = result[i] result[i] = result[j]! result[j] = temp! } return result } export function keys( obj: T, filter?: K[], ): Exclude[] { const allKeys = Object.keys(obj) as (keyof T)[] if (!filter || filter.length === 0) return allKeys as Exclude[] const filterSet = new Set(filter) return allKeys.filter(key => !filterSet.has(key)) as Exclude[] } // 定义获取字段值的方法签名 export type GetValueFn = ( fieldName: string, ) => string | number | boolean | null | undefined export function format(str: string, getValueFn: GetValueFn): string { if (!str) return '' return str.replace( /\{([^}]+)\}/g, (match: string, fieldName: string): string => { const value = getValueFn(fieldName) if (value === undefined || value === null) return match return String(value) }, ) } export function zoneFit(value: number, zone: [number, number]) { const max = Math.max(zone[0], zone[1]) if (value > max) return max const min = Math.min(zone[0], zone[1]) if (value < min) return min return value }