Skip to content

Instantly share code, notes, and snippets.

@grimley517
Last active November 30, 2023 15:33
Show Gist options
  • Select an option

  • Save grimley517/c2d531976db057cede4ac8e367418971 to your computer and use it in GitHub Desktop.

Select an option

Save grimley517/c2d531976db057cede4ac8e367418971 to your computer and use it in GitHub Desktop.
Bitburner Hacknet Build automation
/** @param {NS} ns **/
export async function main(ns) {
var paybackLimit = 24 * 60 * 60;
var sleepMilliseconds = 5 * 1000;
var budgetPercentage = 0.5;
while (true) {
var budget = ns.getServerMoneyAvailable('home');
budget *= budgetPercentage; //Don't want to spend all money on hacknet, allow 33% but change this if wanted
var nodeNumber = ns.hacknet.numNodes();
var actionList = [];
if (nodeNumber > 0) {
for (var i = 0; i < nodeNumber; i++) {
var nodeActions = [];
nodeActions.push(new RamAction(ns, i));
nodeActions.push(new CoreAction(ns, i));
nodeActions.push(new LevelAction(ns, i));
nodeActions.forEach(action => actionList.push(action));
}
}
actionList.push(new NewNodeAction(ns, 1));
actionList = actionList.filter(action => action.payBackTime() < paybackLimit);
if (actionList.length > 0) {
actionList = actionList.filter(action => action.cost < budget);
if (actionList.length > 0) {// Second check, we don't want to escape the script if there are actions we will be able to afford later
actionList.sort(function (x, y) { x.payBackTime() - y.payBackTime() });
actionList[0].doAction();
}
await ns.sleep(sleepMilliseconds);
}
else {
ns.tprint(`All Hacknet Nodes are fully productive.`);
break;
}
}
}
class Action {
constants = {
MaxLevel: 200,
MaxRam: 64,
MaxCores: 16,
MoneyGainPerLevel: 1.5,
HackNetNodeMoneyBitNode: 0.25
}
sys;
nodeIndex;
cost;
originalProd;
prodIncrease;
name;
multProd;
ram;
level;
cores;
ns;
doAction = () => { return null; };
constructor(ns, nodeIndex) {
this.ns = ns;
this.sys = ns.hacknet;
this.nodeIndex = nodeIndex;
this.multProd = ns.getHacknetMultipliers().production;
var stats = this.sys.getNodeStats(nodeIndex);
this.name = stats.name;
this.ram = stats.ram;
this.level = parseFloat(stats.level);
this.cores = parseFloat(stats.cores);
this.originalProd = parseFloat(stats.production);
}
payBackTime() {
return this.cost / this.prodIncrease;
}
upgradedProdRate(ns) {
var updatedRate = this.calculateMoneyGainRate(this.level, this.ram, this.cores, this.multProd);
return updatedRate;
}
calculateMoneyGainRate(level, ram, cores, mult) {
const gainPerLevel = this.constants.MoneyGainPerLevel;
const levelMult = level * gainPerLevel;
const ramMult = Math.pow(1.035, ram - 1);
const coresMult = (cores + 5) / 6.0;
var result = levelMult;
result *= ramMult;
result *= coresMult;
result *= mult;
result *= this.constants.HackNetNodeMoneyBitNode;
return result;
}
}
class RamAction extends Action {
constructor(ns, nodeIndex) {
super(ns, nodeIndex);
this.cost = this.sys.getRamUpgradeCost(this.nodeIndex, 1);
if (isFinite(this.cost) && this.cost > 0) {
++this.ram;
this.prodIncrease = this.upgradedProdRate(ns);
this.prodIncrease -= this.originalProd;
this.doAction = () => {
this.sys.upgradeRam(nodeIndex, 1);
ns.print(`upgrading Ram on node ${this.nodeIndex}, payback time is ${Math.ceil(this.payBackTime() / 3600)} hours`);
}
}
else {
this.prodIncrease = 0;
}
}
}
class LevelAction extends Action {
constructor(ns, nodeIndex) {
super(ns, nodeIndex);
this.cost = this.sys.getLevelUpgradeCost(this.nodeIndex, 1);
if (isFinite(this.cost) && this.cost > 0) {
++this.level;
this.prodIncrease = this.upgradedProdRate(ns);
this.prodIncrease -= this.originalProd;
this.doAction = () => {
this.sys.upgradeLevel(nodeIndex, 1);
ns.print(`upgrading Level on node ${this.nodeIndex}, payback time is ${Math.ceil(this.payBackTime() / 3600)} hours`);
}
}
else {
this.prodIncrease = 0;
}
}
}
class CoreAction extends Action {
constructor(ns, nodeIndex) {
super(ns, nodeIndex);
this.cost = this.sys.getCoreUpgradeCost(this.nodeIndex, 1);
if (isFinite(this.cost) && this.cost > 0) {
++this.cores;
this.prodIncrease = this.upgradedProdRate(ns);
this.prodIncrease -= this.originalProd;
this.doAction = () => {
this.sys.upgradeCore(nodeIndex, 1);
ns.print(`upgrading Core on node ${this.nodeIndex}, payback time is ${Math.ceil(this.payBackTime() / 3600)} hours`);
}
}
else {
this.prodIncrease = 0;
}
}
}
class NewNodeAction extends Action {
constructor(ns, nodeIndex) {
super(ns, 0);
this.ram = 1;
this.level = 1;
this.cores = 1;
this.originalProd = 0;
this.cost = this.sys.getPurchaseNodeCost();
if (isFinite(this.cost) && this.cost > 0) {
this.prodIncrease = this.upgradedProdRate(ns);
this.prodIncrease -= this.originalProd;
this.doAction = () => {
this.sys.purchaseNode();
ns.print(`Purchasing a new node. payback time is ${Math.ceil(this.payBackTime() / 3600)} hours`);
}
}
else {
this.prodIncrease = 0;
}
}
}
@pauleHeisster

Copy link
Copy Markdown
payBackTime() {
    return this.cost / this.prodIncrease;
}

is the relative payBackTime of each Action
If changed to the absolute payBackTime (using this.multProd) this script behaves as expected (new nodes will be purchased and upgraded).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment