Hacknet Node Automation 1.1

 avatar
user_3576964
javascript
a year ago
6.0 kB
31
Indexable
Never
/** @param {NS} ns */
export async function main(ns) {
  ns.disableLog('sleep');
  if (!ns.args.length) {
    return ns.tprint('Supply a target Cluster size');
  }

  const target = ns.args[0];
  const cluster = getMaxedNodes(ns);

  if (cluster >= target) {
    ns.tprint('Cluster target is less than current cluster size');
    ns.tprint(`Target: ${target} | Cluster: ${cluster}`);
    return;
  }

  const killMode = ns.args[1] === 'no-kill' ? false : true;

  if (!killMode) {
    ns.tprint('Starting in no-kill mode');
    ns.tprint('Running until manually terminated or target reached');
  }

  ns.print('Starting Hacknet node automation');

  let maxedCluster = await checkClusterStatus(ns, target);

  while (!maxedCluster) {
    await ns.sleep(500);
    await automateNodes(ns, target, killMode);

    maxedCluster = await checkClusterStatus(ns, target);
  }

  if (maxedCluster) {
    ns.print(`Cluster target of ${target} reached, exiting`);
  }
}

/** 
  @param {NS} ns
  @param {number} target
  @param {boolean} killMode
  @returns {Promise<void>}
*/
const automateNodes = async (ns, target, killMode) => {
  return new Promise((resolve) => {
    const { hacknet: hn } = ns;

    const { ownedNodes, maxedNodes, nextNodeCost, money } = getStats(ns);
    let currentNode = maxedNodes;

    if (maxedNodes === ownedNodes && ownedNodes < target) {
      if (money < nextNodeCost) {
        if (!killMode) {
          await handleNoKill(ns, resolve, money, 'New Node', nextNodeCost);
          return;
        } else {
          return terminate(ns);
        }
      } else {
        ns.print(`Upgrades finished for Node #${currentNode}`);
        currentNode = hn.purchaseNode();
        ns.print(`Purchased New Node: #${currentNode}`);
      }
    }
    const { level, ram, cores } = hn.getNodeStats(currentNode);
    const canUpgrade = getCanUpgrade(ns, currentNode, money);

    if (canUpgrade.all) {
      upgradeNode(ns, currentNode);
      handleSuccess(ns, currentNode, level, ram, cores);
      return resolve();
    }


    for (const component in canUpgrade) {
      if (component !== 'all') {
        if (!canUpgrade[component].isAffordable) {
          if (!killMode) {
            await handleNoKill(ns, resolve, money, component, canUpgrade[component].cost);
            return resolve();
          } else {
            terminate(ns);
            return resolve();
          }
        }
      }
    }
    return resolve();
  });
};

/** 
  @param {NS} ns
  @param {number} target
  @returns {Promise<boolean>}
*/
const checkClusterStatus = async (ns, target) => {
  return new Promise((resolve) => {
    const { hacknet: hn } = ns;
    const ownedNodes = hn.numNodes();

    if (ownedNodes === target) {
      const maxedNodes = getMaxedNodes(ns);

      if (maxedNodes === target) {
        return resolve(true);
      } else {
        ns.print('Maxed Nodes: ', maxedNodes);
      }
    }
    return resolve(false);
  });
};

/** @param {NS} ns */
const getMaxedNodes = (ns) => {
  const { hacknet: hn } = ns;
  const ownedNodes = hn.numNodes();
  let maxedNodes = 0;

  for (let i = 0; i < ownedNodes; i++) {
    const { level, cores, ram } = hn.getNodeStats(i);

    if (level === 200 && cores === 16 && ram === 64) {
      maxedNodes++;
    }
  }

  return maxedNodes;
};

/** 
  @param {NS} ns 
  @param {number} currentNode 
*/
const getUpgradeCosts = (ns, currentNode) => {
  const { hacknet: hn } = ns;
  return {
    levelCost: hn.getLevelUpgradeCost(currentNode),
    coreCost: hn.getCoreUpgradeCost(currentNode),
    ramCost: hn.getRamUpgradeCost(currentNode)
  };
};

/** 
  @param {NS} ns 
  @param {number} currentNode 
  @param {number} money 
*/
const getCanUpgrade = (ns, currentNode, money) => {
  let { levelCost, coreCost, ramCost } = getUpgradeCosts(ns, currentNode);

  const all =
    (money >= levelCost || levelCost === Infinity) &&
    (money >= coreCost || coreCost === Infinity) &&
    (money >= ramCost || ramCost === Infinity)
      ? true
      : false;

  return {
    all,
    level: {
      isAffordable: money < levelCost && levelCost !== Infinity ? false : true,
      cost: levelCost
    },
    core: {
      isAffordable: money < coreCost && coreCost !== Infinity ? false : true,
      cost: coreCost
    },
    ram: {
      isAffordable: money < ramCost && ramCost !== Infinity ? false : true,
      cost: ramCost
    }
  }
}

/** 
  @param {NS} ns 
  @param {number} currentNode 
*/
const upgradeNode = (ns, currentNode) => {
  const { hacknet: hn } = ns;

  hn.upgradeLevel(currentNode, 10);
  hn.upgradeCore(currentNode);
  hn.upgradeRam(currentNode);
};

/** @param {NS} ns */
const getStats = (ns) => {
  const { hacknet: hn } = ns;

  return {
    ownedNodes: hn.numNodes(),
    maxedNodes: getMaxedNodes(ns),
    nextNodeCost: hn.getPurchaseNodeCost(),
    money: ns.getPlayer().money
  };
};

/** 
  @param {NS} ns 
  @param {number} currentNode
  @param {number} level
  @param {number} ram
  @param {number} cores
*/
const handleSuccess = (ns, currentNode, level, ram, cores) => {
  ns.print(`Node #${currentNode} upgraded successfully`);
  ns.print(`Level: ${level} | RAM: ${ram} | Cores: ${cores}`);
};

/** 
 * @param {NS} ns
 * @param {Function} resolve
*/
const handleNoKill = async (ns, resolve, money, upgradeType, upgradeCost) => {
  ns.print('Not enough funds for next upgrade');
  ns.print(`Funds needed for ${upgradeType}: ${ns.formatNumber(upgradeCost)}`);
  ns.print(`Current funds: ${ns.formatNumber(money)}`);
  ns.print('Running in no-kill mode, restarting in 15 seconds');
  await ns.sleep(15000);
  return resolve();
};

/** @param {NS} ns */
const terminate = (ns) => {
  ns.tprint('Insufficient funds for automation, terminating manager.js');
  return ns.kill(ns.pid);
};