* Maybe add functionality that allows on-the-fly hot loading of settings from a file.
* add a job cap thing that prevents running more jobs if the first worker in a cycle finishes. add a thing to worker script that writes to a file (or to `window`) its identifier, when it started and when it finishes. add a padding optimiser that detects when tail collision occurs and increases padding each cycle if it does occur, and decreases it by half of how much it increases everytime no tail collision occurs
* do we need to Add delay between each script execution?
*/
import {
array_get_servers
}
from "lib_servers.js";
import {
array_get_servers_rooted
}
from "lib_root.js";
import {
float_get_server_ram_total,
float_get_server_ram_free
}
from "lib_ram.js";
import {
object_parse
}
from "lib_minimist.js";
// main
export const main = async function(ns) {
// variables
// name of purchased servers
let string_servers_bought_name = "server";
// maximum amount of jobs to spawn per cycle, used to prevent using up too much IRL RAM
let integer_job_cap = 1000;
// duration between each job
let float_padding_seconds = 1;
// precision of the percentage to steal calculator
let float_precision = 0.001;
// the maximum percentage of cash that should be stolen from a server
let float_steal_cap = 0.9;
// time period used for checking the time in seconds
let float_period_check_seconds = 10;
// target
let string_server_target = "";
// ram utilisiation threshold. upgrade ram when reached.
let float_ram_utilisation_threshold = 0.9;
// helper scripts
const string_helper_file_ram = "ram.js";
const string_helper_file_servers = "servers.js";
const string_helper_file_tor = "tor.js";
const string_helper_file_programs = "programs.js";
const string_helper_file_botnet = "botnet.js";
// argument parsing
// options
const object_arguments = object_parse(ns.args);
for (const string_argument in object_arguments) {
if (object_arguments.hasOwnProperty(string_argument)) {
const argument_value = object_arguments[string_argument];
switch (string_argument) {
// options
case "c":
// fall-through
case "check-delay":
float_period_check_seconds = argument_value;
break;
case "d":
// fall-through
case "job-delay":
float_padding_seconds = argument_value;
break;
case "i":
// fall-through
case "target":
string_server_target = argument_value;
break;
case "j":
// fall-through
case "job-cap":
integer_job_cap = argument_value;
break;
case "n":
// fall-through
case "server-name":
string_servers_bought_name = argument_value;
break;
case "p":
// fall-through
case "precision":
float_precision = argument_value;
break;
case "r":
// fall-through
case "ram-utilisation":
float_ram_utilisation_threshold = argument_value;
break;
case "s":
// fall-through
case "steal-cap":
float_steal_cap = argument_value;
break;
}
}
}
let array_helpers = [
{
file: string_helper_file_ram,
threads: 1,
args: [float_period_check_seconds, float_ram_utilisation_threshold]
},
{
file: string_helper_file_servers,
threads: 1,
args: [float_period_check_seconds, string_servers_bought_name, float_ram_utilisation_threshold]
},
{
file: string_helper_file_tor,
threads: 1,
args: [float_period_check_seconds]
},
{
file: string_helper_file_programs,
threads: 1,
args: [float_period_check_seconds, ["BruteSSH.exe", "FTPCrack.exe", "relaySMTP.exe", "HTTPWorm.exe", "SQLInject.exe", "DeepscanV1.exe", "DeepscanV2.exe", "Autolink.exe"]]
},
{
file: string_helper_file_botnet,
threads: 1,
args: [float_period_check_seconds]
}
];
// flags
for (const string_argument in object_arguments) {
if (object_arguments.hasOwnProperty(string_argument)) {
const argument_value = object_arguments[string_argument];
if (
(string_argument === "a" && argument_value) ||
(string_argument === "ram" && !argument_value)
) {
array_helpers.splice(integer_get_index_of_file(array_helpers, string_helper_file_ram), 1);
}
if (
(string_argument === "e" && argument_value) ||
(string_argument === "servers" && !argument_value)
) {
array_helpers.splice(integer_get_index_of_file(array_helpers, string_helper_file_servers), 1);
}
if (
(string_argument === "o" && argument_value) ||
(string_argument === "tor" && !argument_value)
) {
array_helpers.splice(integer_get_index_of_file(array_helpers, string_helper_file_tor), 1);
}
if (
(string_argument === "g" && argument_value) ||
(string_argument === "programs" && !argument_value)
) {
array_helpers.splice(integer_get_index_of_file(array_helpers, string_helper_file_programs), 1);
}
if (
(string_argument === "b" && argument_value) ||
(string_argument === "botnet" && !argument_value)
) {
array_helpers.splice(integer_get_index_of_file(array_helpers, string_helper_file_botnet), 1);
}
}
}
// main loop
await void_script_executor(ns, array_helpers);
let integer_time_finishes = Date.now();
while (true) {
if (integer_time_finishes <= Date.now()) {
integer_time_finishes = await void_runner(ns, integer_job_cap, float_precision, float_steal_cap, float_padding_seconds, string_server_target) + Date.now();
}
// arbitrarily check every float_period_check_seconds to see if the current time is greater than the time that the runner is supposed to finish.
await ns.sleep(float_period_check_seconds * 1000);
}
};
// functions
// returns the index of the scripts array which matches the filename input
const integer_get_index_of_file = function(array_scripts, string_file) {
for (let integer_indices_0 = 0; integer_indices_0 < array_scripts.length; ++integer_indices_0) {
const object_script = array_scripts[integer_indices_0];
const string_script_file = object_script.file;
if (string_script_file === string_file) {
return integer_indices_0;
}
}
};
const object_get_constants = function(ns) {
// from BitNode/BitNodeMultipliers
const object_get_bitnode_multipliers = function(ns) {
// try {
// return ns.getBitNodeMultipliers();
// }
// catch (error) {
// ns.tprint(JSON.stringify(error));
// return {
// HackingLevelMultiplier: 1,
// StrengthLevelMultiplier: 1,
// DefenseLevelMultiplier: 1,
// DexterityLevelMultiplier: 1,
// AgilityLevelMultiplier: 1,
// CharismaLevelMultiplier: 1,
// ServerGrowthRate: 1,
// ServerMaxMoney: 1,
// ServerStartingMoney: 1,
// ServerStartingSecurity: 1,
// ServerWeakenRate: 1,
// HomeComputerRamCost: 1,
// PurchasedServerCost: 1,
// PurchasedServerLimit: 1,
// PurchasedServerMaxRam: 1,
// CompanyWorkMoney: 1,
// CrimeMoney: 1,
// HacknetNodeMoney: 1,
// ManualHackMoney: 1,
// ScriptHackMoney: 1,
// CodingContractMoney: 1,
// ClassGymExpGain: 1,
// CompanyWorkExpGain: 1,
// CrimeExpGain: 1,
// FactionWorkExpGain: 1,
// HackExpGain: 1,
// FactionPassiveRepGain: 1,
// FactionWorkRepGain: 1,
// RepToDonateToFaction: 1,
// AugmentationMoneyCost: 1,
// AugmentationRepCost: 1,
// InfiltrationMoney: 1,
// InfiltrationRep: 1,
// FourSigmaMarketDataCost: 1,
// FourSigmaMarketDataApiCost: 1,
// CorporationValuation: 1,
// BladeburnerRank: 1,
// BladeburnerSkillCost: 1,
// DaedalusAugsRequirement: 1,
// };
// }
return {
HackingLevelMultiplier: 1,
StrengthLevelMultiplier: 1,
DefenseLevelMultiplier: 1,
DexterityLevelMultiplier: 1,
AgilityLevelMultiplier: 1,
CharismaLevelMultiplier: 1,
ServerGrowthRate: 1,
ServerMaxMoney: 1,
ServerStartingMoney: 1,
ServerStartingSecurity: 1,
ServerWeakenRate: 1,
HomeComputerRamCost: 1,
PurchasedServerCost: 1,
PurchasedServerLimit: 1,
PurchasedServerMaxRam: 1,
CompanyWorkMoney: 1,
CrimeMoney: 1,
HacknetNodeMoney: 1,
ManualHackMoney: 1,
ScriptHackMoney: 1,
CodingContractMoney: 1,
ClassGymExpGain: 1,
CompanyWorkExpGain: 1,
CrimeExpGain: 1,
FactionWorkExpGain: 1,
HackExpGain: 1,
FactionPassiveRepGain: 1,
FactionWorkRepGain: 1,
RepToDonateToFaction: 1,
AugmentationMoneyCost: 1,
AugmentationRepCost: 1,
InfiltrationMoney: 1,
InfiltrationRep: 1,
FourSigmaMarketDataCost: 1,
FourSigmaMarketDataApiCost: 1,
CorporationValuation: 1,
BladeburnerRank: 1,
BladeburnerSkillCost: 1,
DaedalusAugsRequirement: 1,
};
};
const object_get_stats = function(ns) {
// try {
// return ns.getStats();
// }
// catch (error) {
// ns.tprint(JSON.stringify(error));
// return {
// hacking: ns.getHackingLevel(),
// strength: 1,
// defense: 1,
// dexterity: 1,
// agility: 1,
// charisma: 1,
// intelligence: 1
// };
// }
return {
hacking: ns.getHackingLevel(),
strength: 1,
defense: 1,
dexterity: 1,
agility: 1,
charisma: 1,
intelligence: 1
};
};
// object_constants are from https://github.com/danielyxie/bitburner/blob/master/src/Constants.js
const object_constants = {
// factor used in determining the amount security increases by from a grow or hack
ServerFortifyAmount: 0.002,
// amount security decreases by from a weaken
ServerWeakenAmount: 0.05,
// base percentage cash increases by from a grow
ServerBaseGrowthRate: 1.03,
// max percentage cash increases by from a grow (accounts for server security)
ServerMaxGrowthRate: 1.0035,
// hacking multipliers
object_hacking_multipliers: ns.getHackingMultipliers(),
// bitnode multipliers
object_bitnode_multipliers: object_get_bitnode_multipliers(ns),
// player stats
object_stats: object_get_stats(ns),
// filenames and ram cost of helper object_scripts
array_workers: ["weaken.js", "grow.js", "hack.js"]
};
return object_constants;
};
// returns true if a script is running on any server
const boolean_script_running = function(ns, string_script) {
const array_servers = array_get_servers(ns);
for (let integer_indices_0 = 0; integer_indices_0 < array_servers.length; ++integer_indices_0) {
const string_server = array_servers[integer_indices_0];
const array_scripts_running = ns.ps(string_server);
if (array_scripts_running.length > 0) {
for (let integer_indices_1 = 0; integer_indices_1 < array_scripts_running.length; ++integer_indices_1) {
const object_script = array_scripts_running[integer_indices_1];
const string_script_to_check = object_script.filename;
if (string_script_to_check == string_script) {
return true;
}
}
}
}
return false;
};
// copies files to all rooted servers
const void_copy_files_to_string_servers_rooted = function(ns, array_files, string_source) {
const array_servers_rooted = array_get_servers_rooted(ns);
for (let integer_indices_0 = 0; integer_indices_0 < array_servers_rooted.length; ++integer_indices_0) {
for (let integer_indices_1 = 0; integer_indices_1 < array_files.length; ++integer_indices_1) {
ns.scp(array_files[integer_indices_1], string_source, array_servers_rooted[integer_indices_0]);
}
}
};
// copies and runs scripts on any server that has enough available RAM
const void_script_executor = async function(ns, array_scripts) {
const array_servers_rooted_sorted_by_ram = array_get_servers_rooted_sorted_by_ram(ns);
for (let integer_indices_0 = 0; integer_indices_0 < array_scripts.length; ++integer_indices_0) {
const object_script = array_scripts[integer_indices_0];
const string_file = object_script.file;
const float_ram = ns.getScriptRam(string_file);
const integer_threads = object_script.threads;
const array_arguments = object_script.args;
// copy script to rooted servers
void_copy_files_to_string_servers_rooted(ns, [string_file], ns.getHostname());
if (!boolean_script_running(ns, string_file)) {
// use servers with the biggest rams first
for (let integer_indices_0 = array_servers_rooted_sorted_by_ram.length - 1; integer_indices_0 >= 0; --integer_indices_0) {
const string_server_used = array_servers_rooted_sorted_by_ram[integer_indices_0];
let float_server_used_ram_free_current = float_get_server_ram_free(ns, string_server_used);
if (float_server_used_ram_free_current >= float_ram * integer_threads) {
try {
await ns.exec(string_file, string_server_used, integer_threads, ...array_arguments);
}
catch (error) {
ns.tprint(JSON.stringify(error));
}
break;
}
}
}
}
};
// targetting
// sort an array of servers by their amounts of RAM, from lowest to highest
const void_sort_by_server_ram = function(ns, array_servers) {
return array_servers.sort((string_element_0, string_element_1) => (float_get_server_ram_total(ns, string_element_0) - float_get_server_ram_total(ns, string_element_1)));
};
// returns the score of a server which is calculated by taking into account its max cash, growth, and required hacking level. adapted from `calculatePercentMoneyHacked` in Hacking.js
const float_get_server_score = function(ns, string_server_target) {
const float_player_hacking_level = ns.getHackingLevel();
return ns.getServerMaxMoney(string_server_target) * ns.getServerGrowth(string_server_target) * ((float_player_hacking_level - (ns.getServerRequiredHackingLevel(string_server_target) - 1)) * Math.pow(float_player_hacking_level, -1));
};
// sort an array of servers by their score, from lowest to highest
const void_sort_by_server_scores = function(ns, array_servers) {
return array_servers.sort((string_element_0, string_element_1) => float_get_server_score(ns, string_element_0) - float_get_server_score(ns, string_element_1));
};
// weaken, grow, hack
// returns integer_threads_required if it's less than or equal to integer_threads_available, otherwise returns integer_threads_available
const integer_get_corrected_threads = function(ns, integer_threads_required, integer_threads_available) {
if (integer_threads_required > integer_threads_available) {
return integer_threads_available;
}
else {
return integer_threads_required;
}
};
// weaken stuff
// the threads required for weaken to cause string_server_target's security to decrease by float_weaken_amount. adapted from `weaken` in NetscriptFunctions.js and `weaken` in Server.ts
const integer_get_threads_required_for_weaken = function(ns, float_weaken_amount) {
const object_constants = object_get_constants(ns);
return float_weaken_amount * Math.pow(object_constants.ServerWeakenAmount, -1) * Math.pow(object_constants.object_bitnode_multipliers.ServerWeakenRate, -1);
};
// returns the threads required for weaken to cause string_server_target's security to reach minimum
const integer_get_threads_required_for_weaken_minimum_security = function(ns, string_server_target, float_server_target_security) {
return Math.ceil(integer_get_threads_required_for_weaken(ns, float_server_target_security - ns.getServerMinSecurityLevel(string_server_target)));
};
// returns the threads required for weaken to cause string_server_target's security to reach minimum if possible, otherwise, return max threads that string_server_used can provide
const integer_get_threads_weaken = function(ns, float_server_used_ram_free, string_server_target, float_server_target_security) {
const string_weaken = object_get_constants(ns).array_workers[0];
const integer_threads_available = Math.trunc(float_server_used_ram_free / ns.getScriptRam(string_weaken));
const integer_threads_required = integer_get_threads_required_for_weaken_minimum_security(ns, string_server_target, float_server_target_security);
return integer_get_corrected_threads(ns, integer_threads_required, integer_threads_available);
};
// returns the security decrease from the weaken threads used. Adapted from `weaken` in NetscriptFunctions.js and `weaken` in Server.ts
const float_get_security_decrease_from_weaken = function(ns, integer_threads_weaken) {
return integer_threads_weaken * object_get_constants(ns).ServerWeakenAmount;
};
// grow stuff
// returns the number of threads of `grow` needed to grow `string_server_target` by the percentage `float_growth` when it has security of `float_server_security`. float_growth = How much the server is being grown by, in DECIMAL form (e.g. 1.5 rather than 50). adapted from `numCycleForGrowth` in https://github.com/danielyxie/bitburner/blob/master/src/Server/ServerHelpers.ts
const integer_get_threads_for_growth = function(ns, string_server_target, float_server_target_security, float_growth) {
const object_constants = object_get_constants(ns);
let ajdGrowthRate = 1 + (object_constants.ServerBaseGrowthRate - 1) / float_server_target_security;
if (ajdGrowthRate > object_constants.ServerMaxGrowthRate) {
ajdGrowthRate = object_constants.ServerMaxGrowthRate;
}
const serverGrowthPercentage = ns.getServerGrowth(string_server_target) / 100;
const cycles = Math.log(float_growth)/(Math.log(ajdGrowthRate) * object_constants.object_hacking_multipliers.growth * serverGrowthPercentage * object_constants.object_bitnode_multipliers.ServerGrowthRate);
return Math.ceil(cycles);
};
// Inverse function of integer_get_threads_for_growth. Returns the percentage growth in decimal form (e.g., 2 = 100% growth).
const float_get_growth_from_threads = function(ns, string_server_target, float_server_target_security, integer_threads) {
const object_constants = object_get_constants(ns);
let ajdGrowthRate = 1 + (object_constants.ServerBaseGrowthRate - 1) / float_server_target_security;
if (ajdGrowthRate > object_constants.ServerMaxGrowthRate) {
ajdGrowthRate = object_constants.ServerMaxGrowthRate;
}
const serverGrowthPercentage = ns.getServerGrowth(string_server_target) / 100;
const float_growth = Math.pow(ajdGrowthRate, integer_threads * object_constants.object_hacking_multipliers.growth * serverGrowthPercentage * object_constants.object_bitnode_multipliers.ServerGrowthRate);
return float_growth;
};
// returns the threads required by grow to grow string_server_target's cash to its maximum when security is at float_server_target_security and current cash is at float_server_target_cash
const integer_get_threads_required_for_grow_maximum_cash = function(ns, string_server_target, float_server_target_security, float_server_target_cash) {
return integer_get_threads_for_growth(ns, string_server_target, float_server_target_security, ns.getServerMaxMoney(string_server_target) / float_server_target_cash);
};
// returns the threads required by grow to grow string_server_target's cash to its maximum if possible, otherwise, return max threads that string_server_used can provide
const integer_get_threads_grow = function(ns, float_server_used_ram_free, string_server_target, float_server_target_security, float_server_target_cash) {
const string_grow = object_get_constants(ns).array_workers[1];
const integer_threads_available = Math.trunc(float_server_used_ram_free / ns.getScriptRam(string_grow));
const integer_threads_required = integer_get_threads_required_for_grow_maximum_cash(ns, string_server_target, float_server_target_security, float_server_target_cash);
return integer_get_corrected_threads(ns, integer_threads_required, integer_threads_available);
};
// returns the security increase from the growth threads used. Adapted from `processSingleServerGrowth` in ServerHelpers.ts and `fortify` in Server.ts
const float_get_security_increase_from_grow = function(ns, integer_threads_grow) {
return 2 * object_get_constants(ns).ServerFortifyAmount * integer_threads_grow;
};
// hack stuff
// returns the percentage of the available cash in string_server_target that is stolen when it is hacked when it has float_server_target_security. adapted from calculatePercentMoneyHacked() in https://github.com/danielyxie/bitburner/blob/master/src/Hacking.js . See also `hackDifficulty` in https://github.com/danielyxie/bitburner/blob/master/src/Server.js
const float_get_percentage_of_cash_from_available_per_hack = function(ns, string_server_target, float_server_target_security) {
const object_constants = object_get_constants(ns);
const balanceFactor = 240;
const difficultyMult = (100 - float_server_target_security) / 100;
const skillMult = (ns.getHackingLevel() - (ns.getServerRequiredHackingLevel(string_server_target) - 1)) / ns.getHackingLevel();
const percentMoneyHacked = difficultyMult * skillMult * object_constants.object_hacking_multipliers.money / balanceFactor;
if (percentMoneyHacked < 0) { return 0; }
if (percentMoneyHacked > 1) { return 1; }
return percentMoneyHacked * object_constants.object_bitnode_multipliers.ScriptHackMoney;
};
// returns the threads required to steal "float_percentage_to_steal" of available money in string_server_target
const integer_get_threads_required_to_hack_percentage = function(ns, string_server_target, float_server_target_security, float_percentage_to_steal) {
return Math.ceil(float_percentage_to_steal / float_get_percentage_of_cash_from_available_per_hack(ns, string_server_target, float_server_target_security));
};
// returns the threads required to steal "float_percentage_to_steal" of available money in string_server_target if possible, otherwise, return max threads that string_server_used can provide
const integer_get_threads_hack = function(ns, float_server_used_ram_free, string_server_target, float_server_target_security, float_percentage_to_steal) {
const string_hack = object_get_constants(ns).array_workers[2];
const integer_threads_available = Math.trunc(float_server_used_ram_free / ns.getScriptRam(string_hack));
const integer_threads_required = integer_get_threads_required_to_hack_percentage(ns, string_server_target, float_server_target_security, float_percentage_to_steal);
return integer_get_corrected_threads(ns, integer_threads_required, integer_threads_available);
};
// returns the security increase from the hack threads. adapted from `hack` in NetscriptFunctions.js and `fortify` in Server.ts
const float_get_security_increase_from_hack = function(ns, string_server_target, float_server_target_security, float_server_target_cash, integer_threads_hack) {
let maxThreadNeeded = Math.ceil(1/float_get_percentage_of_cash_from_available_per_hack(ns, string_server_target, float_server_target_security)*(float_server_target_cash/ns.getServerMaxMoney(string_server_target)));
if (isNaN(maxThreadNeeded)) {
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
maxThreadNeeded = 1e6;
}
return object_get_constants(ns).ServerFortifyAmount * Math.min(integer_threads_hack, maxThreadNeeded);
};
// percentage to steal stuff
// returns the threads required by grow to grow a string_server_target's money back to its original value after stealing float_percentage_to_steal of it, and assuming security is at float_server_target_security
const integer_get_threads_required_for_cash_grow_after_percentage_stolen = function(ns, string_server_target, float_server_target_security, float_percentage_to_steal) {
return integer_get_threads_for_growth(ns, string_server_target, float_server_target_security, Math.pow(1 - float_percentage_to_steal, -1));
};
// should return true if there is enough ram to provide the threads required by weaken to weaken to minimum security, then by grow to grow string_server_target's cash back to maximum after stealing float_percentage_to_steal of the cash, then by weaken to weaken to minimum security again if possible, otherwise, returns false. assumes security is at float_server_target_security
const boolean_is_ram_enough_after_hack_percentage = function(ns, float_server_used_ram_free, string_server_target, float_server_target_cash, float_server_target_security, float_percentage_to_steal) {
const string_weaken = object_get_constants(ns).array_workers[0];
const string_grow = object_get_constants(ns).array_workers[1];
const float_server_target_security_after_hack = float_server_target_security + float_get_security_increase_from_hack(ns, string_server_target, float_server_target_security, float_server_target_cash, integer_get_threads_hack(ns, float_server_used_ram_free, string_server_target, float_server_target_security, float_percentage_to_steal));
const integer_threads_required_for_weaken_minimum_security_after_hack = integer_get_threads_required_for_weaken_minimum_security(ns, string_server_target, float_server_target_security_after_hack);
const float_server_target_security_after_weaken = float_server_target_security_after_hack - float_get_security_decrease_from_weaken(ns, integer_threads_required_for_weaken_minimum_security_after_hack);
const integer_threads_required_for_cash_grow_after_percentage_stolen = integer_get_threads_required_for_cash_grow_after_percentage_stolen(ns, string_server_target, float_server_target_security_after_weaken, float_percentage_to_steal);
const float_server_target_security_after_grow = float_server_target_security_after_weaken + float_get_security_increase_from_grow(ns,integer_threads_required_for_cash_grow_after_percentage_stolen);
const integer_threads_required_for_weaken_minimum_security_after_grow = integer_get_threads_required_for_weaken_minimum_security(ns, string_server_target, float_server_target_security_after_grow);
const float_ram_required = (integer_threads_required_for_weaken_minimum_security_after_hack * ns.getScriptRam(string_weaken)) + (integer_threads_required_for_cash_grow_after_percentage_stolen * ns.getScriptRam(string_grow)) + (integer_threads_required_for_weaken_minimum_security_after_grow * ns.getScriptRam(string_weaken));
if (float_ram_required < float_server_used_ram_free) {
return true;
}
else {
return false;
}
};
// returns the number of cycles of bisection to be done to reach a certain precision, rounded up to the nearest integer
const integer_get_cycles_for_bisection_precision = function(ns, float_precision) {
return Math.ceil(Math.log(Math.pow(float_precision, -1)) * Math.pow(Math.log(2), -1));
};
// this should return optimum percentage to steal such that cash stolen at most is as high as float_steal_cap and string_server_target's security is able to be weakened to minimum with one weaken after the hack, its cash grown to 100% after one grow after the weaken, then its security weakened again to minimum with one weaken, all with the ram it has remaining after the hack by using a binary search algorithm
const float_get_percentage_to_steal = function(ns, float_server_used_ram_free, string_server_target, float_server_target_cash, float_server_target_security, float_precision, float_steal_cap) {
const integer_cycles_for_bisection_precision = integer_get_cycles_for_bisection_precision(ns, float_precision);
let float_ceiling = 1;
let float_floor = 0;
let float_percentage_to_steal = (float_ceiling + float_floor) * 0.5;
for (let integer_indices_0 = 0; integer_indices_0 < integer_cycles_for_bisection_precision; ++integer_indices_0) {
if (boolean_is_ram_enough_after_hack_percentage(ns, float_server_used_ram_free, string_server_target, float_server_target_cash, float_server_target_security, float_percentage_to_steal)) {
float_floor = float_percentage_to_steal;
}
else {
float_ceiling = float_percentage_to_steal;
}
float_percentage_to_steal = (float_ceiling + float_floor) * 0.5;
if (float_percentage_to_steal > float_steal_cap) {
break;
}
}
// cap which can be used so not all money is stolen, which can be bad because it's harder to grow from 0 in most cases
if (float_percentage_to_steal > float_steal_cap) {
return float_steal_cap;
}
else {
return float_percentage_to_steal;
}
};
// scheduling
const array_get_servers_rooted_sorted_by_ram = function(ns) {
let array_servers_rooted = array_get_servers_rooted(ns);
void_sort_by_server_ram(ns, array_servers_rooted);
return array_servers_rooted;
};
const array_get_servers_rooted_sorted_by_score = function(ns) {
let array_servers_rooted = array_get_servers_rooted(ns);
void_sort_by_server_scores(ns, array_servers_rooted);
return array_servers_rooted;
};
const string_get_server_rooted_hackable_with_score_biggest = function(ns) {
const array_servers_rooted_sorted_by_score = array_get_servers_rooted_sorted_by_score(ns);
// iterate through array in reverse
for (let integer_indices_0 = array_servers_rooted_sorted_by_score.length - 1; integer_indices_0 >= 0; --integer_indices_0) {
const string_server_target = array_servers_rooted_sorted_by_score[integer_indices_0];
if (ns.getHackingLevel() >= ns.getServerRequiredHackingLevel(string_server_target)) {
return string_server_target;
}
}
};
const string_job_decider_prepare = function(ns, string_server_target, float_server_target_security, float_server_target_cash) {
if (float_server_target_security > ns.getServerMinSecurityLevel(string_server_target)) {
return "weaken";
}
if (float_server_target_cash < ns.getServerMaxMoney(string_server_target)) {
return "grow";
}
return "";
};
const string_job_decider = function(ns, string_server_target, float_server_target_security, float_server_target_cash) {
if (float_server_target_security > ns.getServerMinSecurityLevel(string_server_target)) {
return "weaken";
}
else {
if (float_server_target_cash < ns.getServerMaxMoney(string_server_target)) {
return "grow";
}
else {
return "hack";
}
}
};
const boolean_can_server_run_script_threads = function(ns, float_server_used_ram_free, float_script_ram, integer_threads) {
if (float_script_ram * integer_threads > float_server_used_ram_free) {
return false;
}
else {
return true;
}
};
// Returns time it takes to complete a hack on a server, in seconds. Adapted from `calculateHackingTime` in Hacking.js
const float_get_time_hack = function(ns, string_server_target, float_server_target_security) {
const object_constants = object_get_constants(ns);
const difficultyMult = ns.getServerRequiredHackingLevel(string_server_target) * float_server_target_security;
const baseDiff = 500;
const baseSkill = 50;
const diffFactor = 2.5;
const intFactor = 0.1;
const hack_stat = object_constants.object_stats.hacking;
const int = object_constants.object_stats.intelligence;
var skillFactor = (diffFactor * difficultyMult + baseDiff);
// tslint:disable-next-line
skillFactor /= (hack_stat + baseSkill + (intFactor * int));
const hackTimeMultiplier = 5;
const hackingTime = hackTimeMultiplier * skillFactor / object_constants.object_hacking_multipliers.speed;
return hackingTime;
};
// Returns time it takes to complete a grow operation on a server, in seconds. Adapted from `calculateGrowTime` in Hacking.js
const float_get_time_grow = function(ns, string_server_target, float_server_target_security) {
const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * float_get_time_hack(ns, string_server_target, float_server_target_security);
};
// Returns time it takes to complete a weaken operation on a server, in seconds. Adapted from `calculateHackingTime` in Hacking.js
const float_get_time_weaken = function(ns, string_server_target, float_server_target_security) {
const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * float_get_time_hack(ns, string_server_target, float_server_target_security);
};
// makes a schedule
const array_make_schedule = function(ns, integer_job_cap, float_precision, float_steal_cap, float_padding_seconds, string_server_target, string_decider) {
const array_workers = object_get_constants(ns).array_workers;
const string_weaken = array_workers[0];
const string_grow = array_workers[1];
const string_hack = array_workers[2];
const array_servers_rooted_sorted_by_ram = array_get_servers_rooted_sorted_by_ram(ns);
const float_server_target_security_minimum = ns.getServerMinSecurityLevel(string_server_target);
const float_server_target_cash_maximum = ns.getServerMaxMoney(string_server_target);
const float_time_weaken = float_get_time_weaken(ns, string_server_target, ns.getServerSecurityLevel(string_server_target));
const float_time_grow = float_get_time_grow(ns, string_server_target, ns.getServerSecurityLevel(string_server_target));
const float_time_hack = float_get_time_hack(ns, string_server_target, ns.getServerSecurityLevel(string_server_target));
// tripwires
let boolean_end_loop_array_servers = false;
let boolean_end_loop_server = false;
let float_server_target_security_current = ns.getServerSecurityLevel(string_server_target);
let float_server_target_cash_current = ns.getServerMoneyAvailable(string_server_target);
let array_schedule = [];
let integer_array_schedule_length = 0;
let integer_time_job_finishes_seconds = Math.max(float_time_weaken, float_time_grow, float_time_hack);
// iterate through servers in reverse to use servers with the biggest rams first
for (let integer_indices_0 = array_servers_rooted_sorted_by_ram.length - 1; integer_indices_0 >= 0; --integer_indices_0) {
if (integer_array_schedule_length >= integer_job_cap ||
boolean_end_loop_array_servers) {
break;
}
const string_server_used = array_servers_rooted_sorted_by_ram[integer_indices_0];
let float_server_used_ram_free_current = float_get_server_ram_free(ns, string_server_used);
while (
float_server_used_ram_free_current > 0 &&
integer_array_schedule_length < integer_job_cap &&
!boolean_end_loop_array_servers &&
!boolean_end_loop_server
) {
const string_job = string_decider(ns, string_server_target, float_server_target_security_current, float_server_target_cash_current);
const schedule_item = {
string_job: string_job,
string_server_used: string_server_used,
string_server_target: string_server_target,
float_server_target_security_before: float_server_target_security_current,
float_server_target_cash_before: float_server_target_cash_current
};
switch (string_job) {
case "": {
boolean_end_loop_array_servers = true;
break;
}
case "weaken": {
if (!boolean_can_server_run_script_threads(ns, float_server_used_ram_free_current, ns.getScriptRam(string_weaken), 1)) {
// start using a server with more ram
boolean_end_loop_server = true;
break;
}
const integer_threads_weaken = integer_get_threads_weaken(ns, float_server_used_ram_free_current, string_server_target, float_server_target_security_current);
float_server_used_ram_free_current -= integer_threads_weaken * ns.getScriptRam(string_weaken);
const float_server_target_security_uncorrected = float_server_target_security_current - float_get_security_decrease_from_weaken(ns, integer_threads_weaken);
if (float_server_target_security_uncorrected < float_server_target_security_minimum) {
float_server_target_security_current = float_server_target_security_minimum;
}
else {
float_server_target_security_current = float_server_target_security_uncorrected;
}
schedule_item.float_server_target_security_current = float_server_target_security_current;
schedule_item.float_delay_seconds = integer_time_job_finishes_seconds - float_time_weaken + float_padding_seconds;
integer_time_job_finishes_seconds += float_padding_seconds;
schedule_item.integer_time_job_finishes_seconds = integer_time_job_finishes_seconds;
schedule_item.integer_threads = integer_threads_weaken;
array_schedule.push(schedule_item);
++integer_array_schedule_length;
break;
}
case "grow": {
if (!boolean_can_server_run_script_threads(ns, float_server_used_ram_free_current, ns.getScriptRam(string_grow), 1)) {
// start using a server with more ram
boolean_end_loop_server = true;
break;
}
if (float_server_target_cash_current === 0) {
float_server_target_cash_current = 1; // counts 0 cash as 1 so it can still grow. taken from `grow` in NetscriptFunctions.js
}
const integer_threads_grow = integer_get_threads_grow(ns, float_server_used_ram_free_current, string_server_target, float_server_target_security_current, float_server_target_cash_current);
float_server_used_ram_free_current -= integer_threads_grow * ns.getScriptRam(string_grow);
const float_server_target_cash_current_uncorrected = float_server_target_cash_current * float_get_growth_from_threads(ns, string_server_target, float_server_target_security_current, integer_threads_grow);
if (float_server_target_cash_current_uncorrected > float_server_target_cash_maximum) {
float_server_target_cash_current = float_server_target_cash_maximum;
}
else {
float_server_target_cash_current = float_server_target_cash_current_uncorrected;
}
// the following is adapted from `processSingleServerGrowth` in ServerHelpers.ts and `fortify` in Server.ts
float_server_target_security_current += float_get_security_increase_from_grow(ns, integer_threads_grow);
schedule_item.float_server_target_security_current = float_server_target_security_current;
schedule_item.float_delay_seconds = integer_time_job_finishes_seconds - float_time_grow + float_padding_seconds;
integer_time_job_finishes_seconds += float_padding_seconds;
schedule_item.integer_time_job_finishes_seconds = integer_time_job_finishes_seconds;
schedule_item.integer_threads = integer_threads_grow;
array_schedule.push(schedule_item);
++integer_array_schedule_length;
break;
}
case "hack": {
if (!boolean_can_server_run_script_threads(ns, float_server_used_ram_free_current, ns.getScriptRam(string_hack), 1)) {
// start using a server with more ram
boolean_end_loop_server = true;
break;
}
const integer_threads_hack = integer_get_threads_hack(ns, float_server_used_ram_free_current, string_server_target, float_server_target_security_current, float_get_percentage_to_steal(ns, float_server_used_ram_free_current, string_server_target, float_server_target_cash_current, float_server_target_security_current, float_precision, float_steal_cap));
float_server_used_ram_free_current -= integer_threads_hack * ns.getScriptRam(string_hack);
// the following is adapted from `hack` in NetscriptFunctions.js and `fortify` in Server.ts
const float_server_target_cash_before = float_server_target_cash_current;
const float_server_target_cash_current_uncorrected = float_server_target_cash_current - Math.floor(float_server_target_cash_current * float_get_percentage_of_cash_from_available_per_hack(ns, string_server_target, float_server_target_security_current)) * integer_threads_hack;
if (float_server_target_cash_current_uncorrected < 0) {
float_server_target_cash_current = 0;
}
else {
float_server_target_cash_current = float_server_target_cash_current_uncorrected;
}
float_server_target_security_current += float_get_security_increase_from_hack(ns, string_server_target, float_server_target_security_current, float_server_target_cash_before, integer_threads_hack);
schedule_item.float_server_target_security_current = float_server_target_security_current;
schedule_item.float_delay_seconds = integer_time_job_finishes_seconds - float_time_hack + float_padding_seconds;
integer_time_job_finishes_seconds += float_padding_seconds;
schedule_item.integer_time_job_finishes_seconds = integer_time_job_finishes_seconds;
schedule_item.integer_threads = integer_threads_hack;
array_schedule.push(schedule_item);
++integer_array_schedule_length;
break;
}
}
}
}
// remove jobs near the end that cause security to not equal minimum. in other words, the target should have minimum security when the schedule finishes. this is to make the jobs in the next schedule run as quick as possible. TODO: make it so that we don't have to do this deleting step, instead, the array making steps above should already take into this into account and don't make more items than is needed.
let array_schedule_edited = array_schedule;
let boolean_have_not_seen_item_with_security_minimum = true;
while (boolean_have_not_seen_item_with_security_minimum) {
for (let integer_indices_1 = array_schedule_edited.length - 1; integer_indices_1 >= 0; --integer_indices_1) {
if (!boolean_have_not_seen_item_with_security_minimum) {
break;
}
const object_job = array_schedule_edited[integer_indices_1];
if (object_job.float_server_target_security_current === float_server_target_security_minimum) {
boolean_have_not_seen_item_with_security_minimum = false;
break;
}
else {
array_schedule_edited.splice(integer_indices_1, 1);
}
}
}
if (boolean_have_not_seen_item_with_security_minimum) {
return array_schedule;
}
else {
return array_schedule_edited;
}
};
const void_schedule_runner = async function(ns, array_schedule) {
const array_workers = object_get_constants(ns).array_workers;
for (let integer_indices_0 = 0; integer_indices_0 < array_schedule.length; ++integer_indices_0) {
const string_job = array_schedule[integer_indices_0].string_job;
const string_server_used = array_schedule[integer_indices_0].string_server_used;
const string_server_target = array_schedule[integer_indices_0].string_server_target;
const integer_threads = array_schedule[integer_indices_0].integer_threads;
const float_delay = array_schedule[integer_indices_0].float_delay_seconds * 1000;
const identifier = integer_indices_0;
let string_script = "";
switch (string_job) {
case "weaken": {
string_script = array_workers[0];
break;
}
case "grow": {
string_script = array_workers[1];
break;
}
case "hack": {
string_script = array_workers[2];
break;
}
}
await ns.exec(string_script, string_server_used, integer_threads, string_server_used, string_server_target, float_delay, identifier);
}
};
// returns the the time it'll take to finish in milliseconds.
const void_runner = async function(ns, integer_job_cap, float_precision, float_steal_cap, float_padding_seconds, string_server_target_argument) {
let integer_time_start = Date.now();
let string_server_target = string_server_target_argument;
if (string_server_target_argument === "") {
string_server_target = string_get_server_rooted_hackable_with_score_biggest(ns);
}
// copy scripts to rooted servers
void_copy_files_to_string_servers_rooted(ns, [object_get_constants(ns).array_workers], ns.getHostname());
// prepare the target if necessary
while (true) {
if (
ns.getServerSecurityLevel(string_server_target) === ns.getServerMinSecurityLevel(string_server_target) &&
ns.getServerMoneyAvailable(string_server_target) === ns.getServerMaxMoney(string_server_target)
) {
break;
}
const array_schedule_prepare = array_make_schedule(ns, integer_job_cap, float_precision, float_steal_cap, float_padding_seconds, string_server_target, string_job_decider_prepare);
if (array_schedule_prepare.length > 0) {
await void_schedule_runner(ns, array_schedule_prepare);
await ns.sleep((array_schedule_prepare[array_schedule_prepare.length - 1].integer_time_job_finishes_seconds * 1000) - integer_time_start + Date.now());
}
}
// make and run actual hacking schedule
const array_schedule = array_make_schedule(ns, integer_job_cap, float_precision, float_steal_cap, float_padding_seconds, string_server_target, string_job_decider);
await void_schedule_runner(ns, array_schedule);
return (array_schedule[array_schedule.length - 1].integer_time_job_finishes_seconds * 1000) - integer_time_start + Date.now();
};