Mirroar/hivemind

View on GitHub
src/utils/throttle.ts

Summary

Maintainability
A
2 hrs
Test Coverage

declare global {
    interface Memory {
        throttleInfo: {
            bucket: {
                normal: number;
                warning: number;
                critical: number;
            };
        };
    }

    interface CreepHeapMemory {
        _tO?: number;
    }
}

const throttleNumbers = [];
let throttleOffset = 0;

/**
 * Choose whether an operation should currently run based on priorities.
 *
 * @param {number} offset
 *   Offset to add to time, so not all operations get throttled on the same tick.
 * @param {number} minBucket
 *   Minimum amount of bucket needed for this operation to run.
 * @param {number} maxBucket
 *   Amount of bucket at which this operation should always run.
 *
 * @return {boolean}
 *   True if the operation is allowed to run.
 */
function throttle(offset: number, minBucket?: number, maxBucket?: number) {
    initThrottleMemory();

    if (!offset) offset = 0;
    if (typeof minBucket !== 'number') minBucket = Memory.throttleInfo.bucket.critical;
    if (typeof maxBucket !== 'number') maxBucket = Memory.throttleInfo.bucket.normal;

    const bucket = Game.cpu.bucket;
    if (bucket >= maxBucket) return false;
    if (bucket < minBucket) return true;

    const tick = (Game.time + offset) % throttleNumbers.length;
    const ratio = (bucket - minBucket) / (maxBucket - minBucket);

    if (ratio >= throttleNumbers[tick]) return false;

    return true;
}

/**
 * Gets a new offset for a throttled operation.
 *
 * @return {number}
 *   Offset to store for a throttled operation.
 */
function getThrottleOffset(): number {
    return throttleOffset++;
}

/**
 * Initializes memory with general throttling information.
 */
function initThrottleMemory(): void {
    if (!Memory.throttleInfo) {
        Memory.throttleInfo = {
            bucket: {
                normal: 8000,
                warning: 5000,
                critical: 2000,
            },
        };
    }

    if (throttleNumbers.length === 0) {
        const sequence = generateEvenSequence(8, 2);
        const max = sequence[0];

        _.each(sequence, (number, index) => {
            throttleNumbers[index] = 1 - (number / max);
        });

        throttleNumbers[0] = 1;
    }
}

/**
 * Generates a Van der Corput sequence.
 *
 * @param {number} power
 *   Number of "digits" relative to base to generate a sequence for.
 * @param {number} base
 *   Base for the sequence. Detemines spacing of the sequence.
 *
 * @return {number[]}
 *   The generated sequence, containing all numbers from 1 to base^power.
 */
function generateEvenSequence(power: number, base: number): number[] {
    const numbers: number[] = [];
    const digits: number[] = [];
    for (let i = 0; i < power; i++) {
        digits[i] = 0;
    }

    function increase(digit: number) {
        if (digit >= power) return;

        digits[digit]++;
        if (digits[digit] >= base) {
            digits[digit] = 0;
            increase(digit + 1);
        }
    }

    function getNumber() {
        let sum = 0;
        for (let i = 0; i < power; i++) {
            sum *= base;
            sum += digits[i];
        }

        return sum;
    }

    increase(0);
    let number = getNumber();
    const max = number * base;
    numbers.push(max);
    while (number !== 0) {
        numbers.push(number);
        increase(0);
        number = getNumber();
    }

    return numbers;
}

export {
    throttle,
    getThrottleOffset,
};