Skip to content

Commit

Permalink
Fix #360 - Hard-Code BitNodeMultipliers
Browse files Browse the repository at this point in the history
We already had bits of these hard-coded everywhere. Might as well generate an array of them once and for all, store it in a central location, and let other scripts be simplified.
  • Loading branch information
alainbryden committed Oct 5, 2024
1 parent 24bb5b3 commit 7be7012
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 110 deletions.
15 changes: 7 additions & 8 deletions autopilot.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ let reservedPurchase = 0; // Flag to indicate whether we've reservedPurchase mon
let reserveForDaedalus = false, daedalusUnavailable = false; // Flags to indicate that we should be keeping 100b cash on hand to earn an invite to Daedalus
let lastScriptsCheck = 0; // Last time we got a listing of all running scripts
let killScripts = []; // A list of scripts flagged to be restarted due to changes in priority
let dictOwnedSourceFiles = [], unlockedSFs = [], bitnodeMults, nextBn = 0; // Info for the current bitnode
let dictOwnedSourceFiles = [], unlockedSFs = [], nextBn = 0; // Info for the current bitnode
let installedAugmentations = [], playerInstalledAugCount = 0, stanekLaunched = false; // Info for the current ascend
let daemonStartTime = 0; // The time we personally launched daemon.
let installCountdown = 0; // Start of a countdown before we install augmentations.
let bnCompletionSuppressed = false; // Flag if we've detected that we've won the BN, but are suppressing a restart
let resetInfo = (/**@returns{ResetInfo}*/() => undefined)(); // Information about the current bitnode
let bitNodeMults = (/**@returns{BitNodeMultipliers}*/() => undefined)(); // bitNode multipliers that can be automatically determined after SF-5

// Replacements for player properties deprecated since 2.3.0
function getTimeInAug() { return Date.now() - resetInfo.lastAugReset; }
Expand Down Expand Up @@ -103,7 +104,7 @@ async function startUp(ns) {
// Collect and cache some one-time data
const player = await getNsDataThroughFile(ns, 'ns.getPlayer()');
resetInfo = await getNsDataThroughFile(ns, 'ns.getResetInfo()');
bitnodeMults = await tryGetBitNodeMultipliers(ns);
bitNodeMults = await tryGetBitNodeMultipliers(ns);
dictOwnedSourceFiles = await getActiveSourceFiles(ns, false);
unlockedSFs = await getActiveSourceFiles(ns, true);
try {
Expand Down Expand Up @@ -191,10 +192,8 @@ async function checkOnDaedalusStatus(ns, player, stocksValue) {
return (4 in unlockedSFs) ? await getNsDataThroughFile(ns, 'ns.singularity.joinFaction(ns.args[0])', null, ["Daedalus"]) :
log(ns, "INFO: Please manually join the faction 'Daedalus' as soon as possible to proceed", false, 'info');
}
const bitNodeMults = await tryGetBitNodeMultipliers(ns, false) || { DaedalusAugsRequirement: 1 };
// Note: A change coming soon will convert DaedalusAugsRequirement from a fractional multiplier, to an integer number of augs. This should support both for now.
const reqDaedalusAugs = bitNodeMults.DaedalusAugsRequirement < 2 ? Math.round(30 * bitNodeMults.DaedalusAugsRequirement) : bitNodeMults.DaedalusAugsRequirement;
if (playerInstalledAugCount !== null && playerInstalledAugCount < reqDaedalusAugs)
// See if we have enough augmentations to attempt to join Daedalus
if (playerInstalledAugCount !== null && playerInstalledAugCount < bitNodeMults.DaedalusAugsRequirement)
return daedalusUnavailable = true; // Won't be able to unlock daedalus this ascend
// If we have sufficient augs and hacking, all we need is the money (100b)
const totalWorth = player.money + stocksValue;
Expand Down Expand Up @@ -597,8 +596,8 @@ async function shouldDelayInstall(ns, player, facmanOutput) {
if (!options['disable-wait-for-4s'] && !(await getNsDataThroughFile(ns, `ns.stock.has4SDataTIXAPI()`))) {
const totalWorth = player.money + await getStocksValue(ns);
const has4S = await getNsDataThroughFile(ns, `ns.stock.has4SData()`);
const totalCost = 25E9 * (bitnodeMults?.FourSigmaMarketDataApiCost || 1) +
(has4S ? 0 : 1E9 * (bitnodeMults?.FourSigmaMarketDataCost || 1));
const totalCost = 25E9 * bitNodeMults.FourSigmaMarketDataApiCost +
(has4S ? 0 : 1E9 * bitNodeMults.FourSigmaMarketDataCost);
const ratio = totalWorth / totalCost;
// If we're e.g. 50% of the way there, hold off, regardless of the '--wait-for-4s' setting
// TODO: If ratio is > 1, we can afford it - but stockmaster won't buy until it has e.g. 20% more than the cost
Expand Down
38 changes: 16 additions & 22 deletions daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ let recoveryThreadPadding = 1; // How many multiples to increase the weaken/grow
let daemonHost = null; // the name of the host of this daemon, so we don't have to call the function more than once.
let hasFormulas = true;
let currentTerminalServer = ""; // Periodically updated when intelligence farming, the current connected terminal server.
let dictSourceFiles = (/**@returns{{[bitnode: number]: number;}}*/() => undefined)(); // Available source files
let bitnodeMults = null; // bitnode multipliers that can be automatically determined after SF-5
let isInBn8 = false; // Flag indicating whether we are in BN8 (where lots of rules change)
let dictSourceFiles = (/**@returns{{[bitNode: number]: number;}}*/() => undefined)(); // Available source files
let bitNodeMults = (/**@returns{BitNodeMultipliers}*/() => undefined)();
let currentBn = 0, isInBn8 = false; // Flag indicating whether we are in BN8 (where lots of rules change)
let haveTixApi = false, have4sApi = false; // Whether we have WSE API accesses
let _cachedPlayerInfo = (/**@returns{Player}*/() => undefined)(); // stores multipliers for player abilities and other player info
let _ns = (/**@returns{NS}*/() => undefined)(); // Globally available ns reference, for convenience
Expand Down Expand Up @@ -196,7 +196,7 @@ function reservedMoney(ns) {
let playerMoney = ns.getServerMoneyAvailable("home");
if (!ownedCracks.includes("SQLInject.exe") && playerMoney > 200e6)
shouldReserve += 250e6; // Start saving at 200m of the 250m required for SQLInject
const fourSigmaCost = (bitnodeMults.FourSigmaMarketDataApiCost * 25000000000);
const fourSigmaCost = (bitNodeMults.FourSigmaMarketDataApiCost * 25000000000);
if (!have4sApi && playerMoney >= fourSigmaCost / 2)
shouldReserve += fourSigmaCost; // Start saving if we're half-way to buying 4S market access
return shouldReserve;
Expand Down Expand Up @@ -239,7 +239,8 @@ export async function main(ns) {
} catch {
resetInfo = { currentNode: 1, lastAugReset: Date.now() };
}
isInBn8 = resetInfo.currentNode === 8; // We do some things differently if we're in BN8 (Stock Market BN)
currentBn = resetInfo.currentNode;
isInBn8 = currentBn === 8; // We do some things differently if we're in BN8 (Stock Market BN)
dictSourceFiles = await getActiveSourceFiles_Custom(ns, getNsDataThroughFile);
log(ns, "The following source files are active: " + JSON.stringify(dictSourceFiles));

Expand Down Expand Up @@ -302,7 +303,7 @@ export async function main(ns) {
// These scripts are spawned periodically (at some interval) to do their checks, with an optional condition that limits when they should be spawned
let shouldUpgradeHacknet = async () => ((await whichServerIsRunning(ns, "hacknet-upgrade-manager.js", false)) === null) && reservedMoney(ns) < ns.getServerMoneyAvailable("home");
// In BN8 (stocks-only bn) and others with hack income disabled, don't waste money on improving hacking infrastructure unless we have plenty of money to spare
let shouldImproveHacking = () => bitnodeMults.ScriptHackMoneyGain != 0 && !isInBn8 || ns.getServerMoneyAvailable("home") > 1e12;
let shouldImproveHacking = () => bitNodeMults.ScriptHackMoneyGain != 0 && !isInBn8 || ns.getServerMoneyAvailable("home") > 1e12;
// Note: Periodic script are generally run every 30 seconds, but intervals are spaced out to ensure they aren't all bursting into temporary RAM at the same time.
periodicScripts = [
// Buy tor as soon as we can if we haven't already, and all the port crackers (exception: don't buy 2 most expensive port crackers until later if in a no-hack BN)
Expand Down Expand Up @@ -352,7 +353,7 @@ export async function main(ns) {
const allServers = await getNsDataThroughFile(ns, 'scanAllServers(ns)');
await getStaticServerData(ns, allServers); // Gather information about servers that will never change
await buildServerList(ns, false, allServers); // create the exhaustive server list
await establishMultipliers(ns); // figure out the various bitnode and player multipliers
await establishMultipliers(ns); // figure out the various bitNode and player multipliers
maxTargets = options['initial-max-targets'];
if (stockFocus) // Ensure we attempt to target at least all servers that represent stocks if in stock-focus mode
maxTargets = Math.max(maxTargets, Object.keys(serverStockSymbols).length);
Expand Down Expand Up @@ -817,7 +818,7 @@ async function doTargetingLoop(ns) {
}

// How much a weaken thread is expected to reduce security by
let actualWeakenPotency = () => bitnodeMults.ServerWeakenRate * weakenThreadPotency;
let actualWeakenPotency = () => bitNodeMults.ServerWeakenRate * weakenThreadPotency;

// Get a dictionary from retrieving the same infromation for every server name
async function getServersDict(ns, command, serverNames) {
Expand Down Expand Up @@ -948,7 +949,7 @@ class Server {
return this._isXpFarming;
}
serverGrowthPercentage() {
return this.serverGrowth * bitnodeMults.ServerGrowthRate * getPlayerHackingGrowMulti() / 100;
return this.serverGrowth * bitNodeMults.ServerGrowthRate * getPlayerHackingGrowMulti() / 100;
}
adjustedGrowthRate() {
return Math.min(maxGrowthRate, 1 + ((unadjustedGrowthRate - 1) / this.getMinSecurity()));
Expand Down Expand Up @@ -989,9 +990,9 @@ class Server {
}
}
// Taken from https://github.com/bitburner-official/bitburner-src/blob/dev/src/Hacking.ts#L43 (calculatePercentMoneyHacked)
const playerHackSkill = playerHackSkill();
const hackLevel = playerHackSkill();
const difficultyMult = (100 - hackDifficulty) / 100;
const skillMult = (playerHackSkill - (this.requiredHackLevel - 1)) / playerHackSkill;
const skillMult = (hackLevel - (this.requiredHackLevel - 1)) / hackLevel;
const percentMoneyHacked = (difficultyMult * skillMult * _cachedPlayerInfo.mults.hacking_money * bitNodeMults.ScriptHackMoney) / 240;
return this._percentStolenPerHackThread = Math.min(1, Math.max(0, percentMoneyHacked));
}
Expand Down Expand Up @@ -1249,7 +1250,7 @@ function getFlagsArgs(toolName, target, allowLooping = true) {
args.push(stockMode && (toolName == "hack" && shouldManipulateHack[target] || toolName == "grow" && shouldManipulateGrow[target]) ? 1 : 0);
if (["hack", "weak"].includes(toolName))
args.push(options['silent-misfires'] || // Optional arg to disable toast warnings about a failed hack if hacking money gain is disabled
(toolName == "hack" && (bitnodeMults.ScriptHackMoneyGain == 0 || isInBn8)) ? 1 : 0); // Disable automatically in BN8 (hack income disabled)
(toolName == "hack" && (bitNodeMults.ScriptHackMoneyGain == 0 || isInBn8)) ? 1 : 0); // Disable automatically in BN8 (hack income disabled)
args.push(allowLooping && loopingMode ? 1 : 0); // Argument to indicate whether the cycle should loop perpetually
return args;
}
Expand Down Expand Up @@ -1557,7 +1558,7 @@ async function farmHackXp(ns, fractionOfFreeRamToConsume = 1, verbose = false, n
singleServerLimit = 0;
lastCycleTotalRam = totalServerRam;
}
let tryAdvanceMode = bitnodeMults.ScriptHackMoneyGain != 0; // We can't attempt hack-based XP if it's impossible to gain hack income (XP will always be 1/4)
let tryAdvanceMode = bitNodeMults.ScriptHackMoneyGain != 0; // We can't attempt hack-based XP if it's impossible to gain hack income (XP will always be 1/4)
let singleServerMode = false; // Start off maximizing hack threads for best targets by spreading their weaken/grow threads to other servers
for (let i = 0; i < numTargets; i++) {
let lastSchedulingResult;
Expand Down Expand Up @@ -1868,16 +1869,9 @@ function getHomeProcIsAlive(ns) {

async function establishMultipliers(ns) {
log(ns, "establishMultipliers");

bitnodeMults = (await tryGetBitNodeMultipliers_Custom(ns, getNsDataThroughFile)) || {
// prior to SF-5, bitnodeMults stays null and these mults are set to 1.
ServerGrowthRate: 1,
ServerWeakenRate: 1,
FourSigmaMarketDataApiCost: 1,
ScriptHackMoneyGain: 1
};
bitNodeMults = await tryGetBitNodeMultipliers_Custom(ns, getNsDataThroughFile);
if (verbose)
log(ns, `Bitnode mults:\n  ${Object.keys(bitnodeMults).filter(k => bitnodeMults[k] != 1.0).map(k => `${k}: ${bitnodeMults[k]}`).join('\n  ')}`);
log(ns, `Bitnode mults:\n  ${Object.keys(bitNodeMults).filter(k => bitNodeMults[k] != 1.0).map(k => `${k}: ${bitNodeMults[k]}`).join('\n  ')}`);
}

class Tool {
Expand Down
10 changes: 4 additions & 6 deletions faction-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ let playerData = null, bitNode = 0, gangFaction = null;
let augsAwaitingInstall, startingPlayerMoney, stockValue = 0; // If the player holds stocks, their liquidation value will be determined
let factionNames = [], joinedFactions = [], desiredStatsFilters = [], purchaseFactionDonations = [];
let ownedAugmentations = [], simulatedOwnedAugmentations = [], effectiveSourceFiles = [], allAugStats = [], priorityAugs = [], purchaseableAugs = [];
let factionData = {}, augmentationData = {}, bitNodeMults = {};
let factionData = {}, augmentationData = {};
let bitNodeMults = (/**@returns{BitNodeMultipliers}*/() => undefined)();
let printToTerminal, ignorePlayerData;
let _ns; // Used to avoid passing ns to functions that don't need it except for some logs.

Expand Down Expand Up @@ -182,11 +183,8 @@ export async function main(ns) {
const sort = unshorten(options.sort || desiredStatsFilters[0]);
displayFactionSummary(ns, sort, options.u || options.unique, afterFactions, hideSummaryStats);

// Determine the current bitnode multipliers, or default to some sane assumptions if they aren't available (requires SF 5.1)
bitNodeMults = await tryGetBitNodeMultipliers(ns, false) || {
DaedalusAugsRequirement: bitNode == 6 || bitNode == 7 ? 35 : 30,
FactionWorkRepGain: bitNode == 2 ? 0.5 : bitNode == 4 ? 0.75 : bitNode == 13 ? 0.6 : bitNode == 14 ? 0.2 : 1,
};
// Determine the current bitnode multipliers
bitNodeMults = await tryGetBitNodeMultipliers(ns);

// Create the table of all augmentations, and the breakdown of what we can afford
await manageUnownedAugmentations(ns, omitAugs);
Expand Down
4 changes: 2 additions & 2 deletions gangs.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ async function initialize(ns) {
// Initialize information about gang members and crimes
allTaskNames = await getNsDataThroughFile(ns, 'ns.gang.getTaskNames()')
allTaskStats = await getGangInfoDict(ns, allTaskNames, 'getTaskStats');
multGangSoftcap = (await tryGetBitNodeMultipliers(ns))?.GangSoftcap || 1;
multGangSoftcap = (await tryGetBitNodeMultipliers(ns)).GangSoftcap;
myGangMembers = await getNsDataThroughFile(ns, 'ns.gang.getMemberNames()');
const dictMembers = await getGangInfoDict(ns, myGangMembers, 'getMemberInformation');
for (const member of Object.values(dictMembers)) // Initialize the current activity of each member
Expand Down Expand Up @@ -230,7 +230,7 @@ async function onTerritoryTick(ns, myGangInfo) {

// Update gang members in case someone died in a clash
myGangMembers = await getNsDataThroughFile(ns, 'ns.gang.getMemberNames()');
const canRecruit = await getNsDataThroughFile(ns, 'ns.gang.canRecruitMember()');
const canRecruit = await getNsDataThroughFile(ns, 'ns.gang.canRecruitMember()');
if (canRecruit)
await doRecruitMember(ns) // Recruit new members if available
const dictMembers = await getGangInfoDict(ns, myGangMembers, 'getMemberInformation');
Expand Down
Loading

0 comments on commit 7be7012

Please sign in to comment.