UNPKG

gamekit-utils

Version:

Minimal, fast and useful utilities for randomness, array manipulation and math — built for games, UI logic and generative design.

178 lines (166 loc) 4.79 kB
'use strict'; /** * Returns a random element from an array. * Throws an error if the array is empty. * * @example * random([1, 2, 3]); // → 2 */ const random = (arr) => { if (!Array.isArray(arr)) { throw new TypeError("Expected an array"); } if (arr.length === 0) { throw new RangeError("Cannot select a random element from an empty array"); } const index = Math.floor(Math.random() * arr.length); return arr[index]; }; /** * Returns a new array with the same elements in random order (Fisher–Yates). * * Note: Only the array structure is copied. Objects inside are not cloned. * * @example * shuffle([1, 2, 3]); // → [2, 3, 1] */ const shuffle = (arr) => { const result = [...arr]; // shallow copy for (let i = result.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [result[i], result[j]] = [result[j], result[i]]; } return result; }; /** * Picks `n` unique random elements from the array. * * @throws if n > arr.length */ function pickN(arr, n) { if (!Array.isArray(arr)) { throw new TypeError("Expected an array"); } if (n > arr.length) { throw new RangeError("Cannot pick more elements than array contains"); } return shuffle(arr).slice(0, n); } /** * Returns true with the given percentage chance (0–100). * * @example * chance(25) // ~25% chance to return true */ function chance(percent) { if (typeof percent !== "number" || isNaN(percent)) { throw new TypeError("Chance percent must be a valid number"); } if (percent < 0 || percent > 100) { throw new RangeError("Chance percent must be between 0 and 100"); } return Math.random() < percent / 100; } /** * Clamps a number between min and max bounds. * * @example * clamp(5, 1, 10) // → 5 * clamp(-2, 0, 10) // → 0 * clamp(12, 0, 10) // → 10 */ function clamp(val, min, max) { if (min > max) { throw new RangeError("clamp: min cannot be greater than max"); } return Math.min(Math.max(val, min), max); } /** * Linearly interpolate between two values. * @param a The start value. * @param b The end value. * @param t The interpolation factor, clamped between 0 and 1. * @returns The interpolated value. */ function lerp(a, b, t) { return a + (b - a) * t; } /** * Normalizes a value t from the range [a, b] to [0, 1]. * * @param a The start of the range. * @param b The end of the range. * @param t The value to normalize. * @returns The normalized value in [0, 1]. * * @example * normalize(10, 20, 15) // → 0.5 * normalize(0, 100, 25) // → 0.25 */ function normalize(a, b, t) { if (a === b) throw new RangeError('normalize: a and b cannot be equal'); return (t - a) / (b - a); } /** * Creates a 2D array. * @param rows The number of rows. * @param cols The number of columns. * @param value The initial value or a function to generate values. * @returns A 2D array filled with the specified value. */ function create2D(rows, cols, value) { const isFn = typeof value === "function"; return Array.from({ length: rows }, (_, r) => Array.from({ length: cols }, (_, c) => isFn ? value(r, c) : value)); } function getNeighbors(row, col, grid, diagonals = false) { const directions = diagonals ? [ [-1, 0], // up [1, 0], // down [0, -1], // left [0, 1], // right [-1, -1], // up-left [-1, 1], // up-right [1, -1], // down-left [1, 1], // down-right ] : [ [-1, 0], // up [1, 0], // down [0, -1], // left [0, 1], // right ]; const neighbors = []; for (const [dr, dc] of directions) { const r = row + dr; const c = col + dc; if (grid[r]?.[c] !== undefined) { neighbors.push({ row: r, col: c, value: grid[r][c] }); } } return neighbors; } /** * Checks if the given phase is within the specified range in a cyclic manner. * @param from The start of the range (inclusive). * @param to The end of the range (exclusive). * @param phase The current phase to check. * @param cycle The length of the cycle. * @returns 1 if the phase is within the range, 0 otherwise. */ function pulse(from, to, phase, cycle) { const mod = ((phase % cycle) + cycle) % cycle; return mod >= from && mod < to ? 1 : 0; } exports.chance = chance; exports.clamp = clamp; exports.create2D = create2D; exports.getNeighbors = getNeighbors; exports.lerp = lerp; exports.normalize = normalize; exports.pickN = pickN; exports.pulse = pulse; exports.random = random; exports.shuffle = shuffle; //# sourceMappingURL=index.cjs.map