/** @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);
};