Mirroar/hivemind

View on GitHub
src/process/strategy/mining.ts

Summary

Maintainability
A
1 hr
Test Coverage
/* global PWR_OPERATE_SPAWN POWER_INFO */

import container from 'utils/container';
import hivemind from 'hivemind';
import Process from 'process/process';
import RemoteMiningOperation from 'operation/remote-mining';
import settings from 'settings-manager';
import stats from 'utils/stats';

declare global {
    interface StrategyMemory {
        remoteHarvesting?: {
            currentCount: number;
            lastCheck: number;
            rooms: string[];
        };
    }
}

export default class RemoteMiningProcess extends Process {
    /**
     * Manages which and how many rooms may be remotely mined.
     * @constructor
     *
     * @param {object} parameters
     *   Options on how to run this process.
     */
    constructor(parameters: ProcessParameters) {
        super(parameters);

        if (!Memory.strategy) {
            Memory.strategy = {};
        }

        if (!Memory.strategy.remoteHarvesting) {
            Memory.strategy.remoteHarvesting = {
                currentCount: 20,
                lastCheck: Game.time,
                rooms: [],
            };
        }
    }

    /**
     * Determines optimal number of remote mining rooms based on CPU and expansion
     * plans.
     */
    run() {
        const memory = Memory.strategy;
        const assignment = container.get('RemoteMinePrioritizer').getRoomsToMine(memory.remoteHarvesting.currentCount);
        memory.remoteHarvesting.rooms = assignment.rooms;

        this.adjustRemoteMiningCount(assignment.maxRooms);
        this.manageOperations();

        // @todo Reduce remote harvesting if we want to expand.
    }

    /**
     * Periodically Adjusts remote harvesting room count.
     *
     * @param {number} availableHarvestRoomCount
     *   Maximum number of harvest rooms that might be used.
     */
    adjustRemoteMiningCount(availableHarvestRoomCount) {
        const memory = Memory.strategy;

        if (!memory.remoteHarvesting.lastCheck || !hivemind.hasIntervalPassed(1000, memory.remoteHarvesting.lastCheck)) return;

        memory.remoteHarvesting.lastCheck = Game.time;

        if (Game.myRooms.length === 1 && Game.cpu.limit >= 20) {
            // Early game, make sure to remote mine as much as possible for a
            // quick start.
            memory.remoteHarvesting.currentCount = 20;
            return;
        }

        // Reduce count if we are over the available maximum.
        const availableHarvestRoomCountWithBuffer = availableHarvestRoomCount + 3;
        if (memory.remoteHarvesting.currentCount > availableHarvestRoomCountWithBuffer) {
            Game.notify('⚒ reduced remote mining count from ' + memory.remoteHarvesting.currentCount + ' to ' + availableHarvestRoomCountWithBuffer + ' because that is the maximum number of available rooms.');
            memory.remoteHarvesting.currentCount = availableHarvestRoomCountWithBuffer;
        }

        // Check past CPU and bucket usage.
        const longTermBucket = stats.getStat('bucket', 10_000) || 10_000;
        const shortTermBucket = stats.getStat('bucket', 1000) || 10_000;
        const cpuUsage = stats.getStat('cpu_total', 1000) || 0.5;
        if (longTermBucket >= 9500 && shortTermBucket >= 9500 && cpuUsage <= 0.95 * Game.cpu.limit) {
            // We've been having bucket reserves and CPU cycles to spare.
            if (memory.remoteHarvesting.currentCount < availableHarvestRoomCount) {
                memory.remoteHarvesting.currentCount++;
            }
        }
        else if (shortTermBucket <= 8000 // Bucket has seen some usage recently.
            && memory.remoteHarvesting.currentCount > 0) {
            memory.remoteHarvesting.currentCount--;
        }
    }

    /**
     * Creates or terminates remote mining operations based on selected rooms.
     */
    manageOperations() {
        const memory = Memory.strategy;

        // Create operations for selected rooms.
        for (const roomName of memory.remoteHarvesting.rooms) {
            if (!Game.operationsByType.mining['mine:' + roomName]) {
                const operation = new RemoteMiningOperation('mine:' + roomName);
                operation.setRoom(roomName);
            }
        }

        // Stop operations for rooms that are no longer selected.
        _.each(Game.operationsByType.mining, op => {
            if (!memory.remoteHarvesting.rooms.includes(op.getRoom())) {
                op.terminate();
            }
        });
    }
}