From c7cccc22df4c4b3e888aeb0efe0f31a0333f0136 Mon Sep 17 00:00:00 2001 From: Alain Bryden <2285037+alainbryden@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:01:14 -0400 Subject: [PATCH] BN9 tweaks - Configurable threshold for when to spend hashes to boost server income - Always spend hashes to boost income in BN9 - Fix premature toasts when our next purchase costs more than current hash capacity --- autopilot.js | 11 ++++++++--- spend-hacknet-hashes.js | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/autopilot.js b/autopilot.js index 371e7a7f..410ccfc2 100644 --- a/autopilot.js +++ b/autopilot.js @@ -1,7 +1,7 @@ import { log, getFilePath, getConfiguration, instanceCount, getNsDataThroughFile, runCommand, waitForProcessToComplete, getActiveSourceFiles, tryGetBitNodeMultipliers, getStocksValue, unEscapeArrayArgs, - formatMoney, formatDuration, getErrorInfo, tail + formatMoney, formatDuration, formatNumber, getErrorInfo, tail } from './helpers.js' const argsSchema = [ // The set of all command line arguments @@ -22,6 +22,7 @@ const argsSchema = [ // The set of all command line arguments ['disable-wait-for-4s', false], // If true, will doesn't wait for the 4S Tix API to be acquired under any circumstantes ['disable-rush-gangs', false], // Set to true to disable focusing work-for-faction on Karma until gangs are unlocked ['disable-casino', false], // Set to true to disable running the casino.js script automatically + ['spend-hashes-on-server-hacking-threshold', 0.1], // Threshold for how good hacking multipliers must be to merit spending hashes for boosting hack income. Set to a large number to disable this entirely. ['on-completion-script', null], // Spawn this script when we defeat the bitnode ['on-completion-script-args', []], // Optional args to pass to the script when we defeat the bitnode ['xp-mode-interval-minutes', 55], // Every time this many minutes has elapsed, toggle daemon.js to runing in --xp-only mode, which prioritizes earning hack-exp rather than money @@ -503,7 +504,10 @@ export async function main(ns) { '/Temp/getServerRequiredHackingLevel-all.txt', incomeByServer.map(s => s.hostname)); const [bestServer, gain] = incomeByServer.filter(s => dictServerHackReqs[s.hostname] <= player.skills.hacking) .reduce(([bestServer, bestIncome], target) => target.gainRate > bestIncome ? [target.hostname, target.gainRate] : [bestServer, bestIncome], [null, -1]); - if (bestServer && gain > 1) { + const spendHashesMultThreshold = options['spend-hashes-on-server-hacking-threshold']; + // If hacking gain multipliers are too low, assume the bitnode is meant to be won a different way and don't bother wasting hashes on boosting hack income + // The exception is that in BN9, despite high penalties, we're definitely meant to spend hashes to boost hack income + if (bestServer && (gain > spendHashesMultThreshold || resetInfo.currentNode == 9)) { // Check whether we should be spending hashes to reduce minimum security const serverMinSecurity = await getNsDataThroughFile(ns, 'ns.getServerMinSecurityLevel(ns.args[0])', null, [bestServer]); const shouldReduceMinSecurity = serverMinSecurity > 2; // Each purchase reduces by 2%. Can't go below 1, but not worth the cost to keep going below 2. @@ -525,7 +529,8 @@ export async function main(ns) { launchScriptHelper(ns, 'spend-hacknet-hashes.js', spendHashesArgs); } } else if (gain <= 1) - log_once(ns, `INFO: Hack income is currently too severely penalized to merit launching spend-hacknet-hashes.js to boost servers.`); + log_once(ns, `INFO: The best server (${bestServer})'s hack income multiplier (${formatNumber(gain)}) is currently too severely penalized ` + + `(< ${spendHashesMultThreshold}) to merit launching spend-hacknet-hashes.js to boost servers. (Configure with --spend-hashes-on-server-hacking-threshold)`); else log(ns, `WARNING: strServerIncomeInfo was not empty, but could not determine best server:\n${strServerIncomeInfo}`); } diff --git a/spend-hacknet-hashes.js b/spend-hacknet-hashes.js index 945b7567..f6e90e61 100644 --- a/spend-hacknet-hashes.js +++ b/spend-hacknet-hashes.js @@ -10,6 +10,7 @@ const argsSchema = [ ['spend-on-server', null], // The server to boost, for spend options that take a server argument: 'Reduce Minimum Security' and 'Increase Maximum Money' ['no-capacity-upgrades', false], // By default, we will attempt to upgrade the hacknet node capacity if we cannot afford any purchases. Set to true to disable this. ['reserve', null], // The amount of player money to leave unpent when considering buying capacity upgrades (defaults to the amount in reserve.txt on home) + ['ignore-reserve-if-upgrade-cost-less-than-pct', 0.01], // Hack to purchase capacity upgrades regardless of the curent global reserve if they cost less than this fraction of player money ['reserve-buffer', 1], // To avoid wasting hashes, spend if would be within this many hashes of our max capacity on the next tick. ['max-purchases-per-loop', 10000], // When we're producing hashes faster than we can spend them, this keeps things from getting hung up ]; @@ -152,11 +153,14 @@ export async function main(ns) { `from capacity, but were only looking to reserve ${formatHashes(hashesEarnedNextTick)} hashes (earning ${formatHashes(globalProduction)} hashes/sec).`; else continue; // Current hash capacity suffices, go back to sleep - // If we aren't allowed to purchase capacity upgrades by configuration, we may need to warn the user if we're running out of space - if (options['no-capacity-upgrades']) { // If we aren't allowed to purchase capacity upgrades by configuration, we may need to warn the user if we're running out of space + + // If we aren't allowed to purchase capacity upgrades by configuration (or can't afford it), + // we may need to warn the player via toast notification so that they can intervene. + // Don't create a toast notification unless we're nearing our capacity limit and at risk of wasting hashes. + const warnToast = remaining < hashesEarnedNextTick ? 'warning' : undefined; + if (options['no-capacity-upgrades']) { // If we aren't allowed to purchase capacity upgrades by configuration, warn the user so they can intervene log(ns, `WARNING: Upgrade your hacknet cache! spend-hacknet-hashes.js --no-capacity-upgrades is set, ` + - `so we cannot increase our hash capacity. ${capacityMessage}`, false, - remaining < hashesEarnedNextTick ? 'warning' : undefined); // Only warn via toast if we are running out of capacity + `so we cannot increase our hash capacity. ${capacityMessage}`, false, warnToast); } else { // Otherwise, try to upgrade hacknet capacity so we can save up for more upgrades if (!notifiedMaxCapacity) // Log that we want to increase hash capacity (unless we've previously seen that we are maxed out) log(ns, `INFO: ${capacityMessage}`); @@ -168,7 +172,9 @@ export async function main(ns) { const nextNodeCost = ns.hacknet.getPurchaseNodeCost(); const reservedMoney = options['reserve'] ?? Number(ns.read("reserve.txt") || 0); const playerMoney = ns.getServerMoneyAvailable('home'); - const spendableMoney = Math.max(0, playerMoney - reservedMoney); + const spendableMoney = Math.max(0, playerMoney - reservedMoney, + // Hack: Because managing global reserve is tricky. We tend to always want to purchase cheap upgrades + playerMoney * options['ignore-reserve-if-upgrade-cost-less-than-pct']); // If it's cheaper to buy a new hacknet node than to upgrade the cache of an existing one, do so if (nextNodeCost < nextCacheUpgradeCost && nextNodeCost < spendableMoney) { if (ns.hacknet.purchaseNode()) @@ -205,7 +211,8 @@ export async function main(ns) { if (Number.isFinite(nextCheapestCacheIncreaseCost)) { if (playerMoney > nextCheapestCacheIncreaseCost) message += ' Feel free to manually purchase this upgrade (despite the reserve/budget) if you deem it worthwhile.' - log(ns, `WARNING: spend-hacknet-hashes.js ${message}`, false, 'warning'); + log(ns, `WARNING: spend-hacknet-hashes.js ${message}`, false, warnToast); + } else if (nextPurchaseCost > capacity) // If we can't afford anything, and have maxed our hash capacity, we may as well shut down. return log(ns, `SUCCESS: We've maxed all purchases. ${message}`); // Shut down, because we will never be able to buy anything further. else if (!notifiedMaxCapacity) { // The first time we discover we are at max hash capacity (infinite cost) notify the user