Last active
November 30, 2023 15:33
-
-
Save grimley517/c2d531976db057cede4ac8e367418971 to your computer and use it in GitHub Desktop.
Bitburner Hacknet Build automation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @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; | |
} | |
} | |
} |
Furthermore, the budget percentage is not really useful because the home server money will exponentially decay to trivial numbers even if you set the percentage as a low value.
I have also tried your script and can only agree that buying a new node is a very rare action because of the minimal/initial production increase rate.
I would try to estimate the potential production increasement by upgrading a new node with the cost of the first item within the filtered actionList.
This should lead to the real potential of investing into a new node.
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
For this line:
There is a big problem with treating new node purchases as regular
Action
. New nodes at the beginning only produce $8/s (actual value) which is trivial. But what matters is their protentional to get $23.596k/s (actual value).With a price of $4.125m (actual value) and profit of $8/s, a node will only pay for itself in 5+ days so it will not be purchased. But let's say we pay $500m (guesstimate--likely overestimate) to buy and fully upgrade the node but can get $23.596k/s. The node will make up for its total price in less than 6 hours.
Pretty much all the payback filtering logic is for this new node cost and it is just a bug.