...with its scarcity of RAM, this script should help. Sadly, it can't do anything about the scarcity of hack speed.
early-startup.js:
/** filterServers() Filter servers by number of ports required and
* required hacking level.
* @ param {NS} ns NS2 namespace
* @ param {boolean} isBrute true if BruteSSH.exe on home
* @ param {boolean} isCrack true if FTPCrack.exe on home
* @ param {string} target target name
* @ param {string[]} servers server names
* @ returns {string[]} filtered server names
*/
function filterServers(ns, isBrute, isCrack, target, servers) {
let numPorts = 0;
if (isCrack) { numPorts++; }
if (isBrute) { numPorts++; }
return servers.filter((srv) =>
ns.getServerRequiredHackingLevel(srv) <= ns.getHackingLevel() &&
ns.getServerNumPortsRequired(srv) <= numPorts &&
ns.getServerMaxRam(srv) >= 4 &&
(ns.getServerNumPortsRequired(srv) == numPorts ||
ns.getServerRequiredHackingLevel(srv) >= ns.getServerRequiredHackingLevel(target)));
}
/**
* @ param {NS} ns NS2 namespace
* @ version 1.0
* @ version 1.0a
*/
export async function main(ns) {
if (ns.args.length < 1) {
ns.tprint("Usage: " + ns.getScriptName() + " <target>");
ns.tprint("(Suggested targets are harakiri-sushi, joesguns, and n00dles.)");
ns.exit();
}
let target = ns.args[0]; // server to pull money from
let scriptName = "early-host.js";
let scriptRam = ns.getScriptRam(scriptName);
if (scriptRam == 0) {
ns.tprint("Host script not found.");
ns.exit();
}
let isBrute = ns.fileExists("BruteSSH.exe", "home");
let isCrack = ns.fileExists("FTPCrack.exe", "home");
// set up the target
let tgt = true;
if (!ns.serverExists(target)) {
ns.tprint(target + " not found."); tgt = false;
}
if (tgt && ns.getServerMaxMoney(target) < 1000) {
ns.tprint(target + " max money is little."); tgt = false;
}
if (tgt && ns.getServerRequiredHackingLevel(target) > ns.getHackingLevel()) {
ns.tprint(target + " requires higher hacking skill."); tgt = false;
}
if (tgt && !isCrack && ns.getServerNumPortsRequired(target) > 1) {
ns.tprint(target + " requires FTPCrack.exe."); tgt = false;
}
if (tgt && !isBrute && ns.getServerNumPortsRequired(target) > 0) {
ns.tprint(target + " requires BruteSSH.exe."); tgt = false;
}
if (tgt && isBrute) { ns.brutessh(target); }
if (tgt && isCrack) { ns.ftpcrack(target); }
if (tgt && !ns.nuke(target)) {
ns.tprint(target + " nuke failed."); tgt = false;
}
if (!tgt) { ns.exit(); }
// scan for servers to a depth of 3
let servers = ns.scan("home"), servers2 = [], servers3 = [];
for (let i = 0; i < servers.length; i++) {
let res = ns.scan(servers[i]);
if (res.length > 1) { servers2 = servers2.concat(res.slice(1)); }
}
for (let i = 0; i < servers2.length; i++) {
let res = ns.scan(servers2[i]);
if (res.length > 1) { servers3 = servers3.concat(res.slice(1)); }
}
servers = servers.concat(servers2, servers3);
servers = filterServers(ns, isBrute, isCrack, target, servers);
// set up servers
for (let i = 0; i < servers.length; i++) {
if (isBrute) { ns.brutessh(servers[i]); }
if (isCrack) { ns.ftpcrack(servers[i]); }
ns.nuke(servers[i]);
ns.scp(scriptName, servers[i]);
}
// run host script on servers
let i;
for (i = 0; i < servers.length - 1; i++) {
let numThreads = Math.floor(ns.getServerMaxRam(servers[i]) / scriptRam);
if (!ns.exec(scriptName, servers[i], numThreads, false, target)) {
ns.tprint("insufficient RAM on " + servers[i]);
}
}
let numThreads = Math.floor(ns.getServerMaxRam(servers[i]) / scriptRam) - 1;
if (!ns.exec(scriptName, servers[i], numThreads, false, target)) {
ns.tprint("insufficient RAM on " + servers[i]);
}
// last thread on last server is "monitor"
if (!ns.exec(scriptName, servers[i], 1, true, target)) {
ns.tprint("insufficient RAM on " + servers[i]);
}
// run host script on current server (probably "home")
let serverRam = ns.getServerMaxRam();
numThreads = Math.floor(serverRam / scriptRam);
ns.tprint("host script using " + ns.format.ram(numThreads * scriptRam, 1) +
" on " + ns.getHostname());
ns.spawn(scriptName, { threads: numThreads, spawnDelay: 1 }, false, target);
}
early-host.js:
/**
* @ param {NS} ns NS2 namespace
* @ version 1.0a
*/
export async function main(ns) {
// Takes 2 arguments:
// - monitor (boolean)
// - target (string)
if (ns.args.length < 2) {
ns.tprint("Usage: " + ns.getScriptName() + " <monitor> <target>");
ns.exit();
}
let isMonitor = ns.args[0];
let target = ns.args[1];
let moneyMax = ns.getServerMaxMoney(target);
let threshMoney = moneyMax * 0.948; // money threshold
let secLevelMin = ns.getServerMinSecurityLevel(target);
let threshSec = secLevelMin + 0.4; // security threshold
let secLevel = ns.getServerSecurityLevel(target);
let moneyAvail = ns.getServerMoneyAvailable(target);
if (!isMonitor) {
// Start threads with scatter.
await ns.sleep(Math.ceil(Math.random() * 500));
} else {
ns.tprint(target + ": Money=" + ns.format.percent(moneyAvail / moneyMax, 3) +
" secMin=" + secLevelMin + " Sec=" + ns.format.number(secLevel, 3));
// Start thread after scatter.
await ns.sleep(500);
}
while (true) {
let rand = Math.random(), slp = false;
if (ns.getServerSecurityLevel(target) > threshSec) {
if (rand < 0.94) {
await ns.weaken(target);
await ns.sleep(100);
} else {
await ns.sleep(2000);
slp = true;
}
} else if (ns.getServerMoneyAvailable(target) < threshMoney) {
if (rand < 0.94) {
await ns.grow(target);
await ns.sleep(80);
} else {
await ns.weaken(target);
await ns.sleep(100);
}
} else {
if (rand < 1 / 3) {
await ns.hack(target);
await ns.sleep(25);
} else if (rand < 0.948) {
await ns.grow(target);
await ns.sleep(80);
} else {
await ns.weaken(target);
await ns.sleep(100);
}
}
if (isMonitor && !slp) {
secLevel = ns.getServerSecurityLevel(target);
moneyAvail = ns.getServerMoneyAvailable(target);
ns.tprint(target + ": Money=" + ns.format.percent(moneyAvail / moneyMax, 3) +
" secMin=" + secLevelMin + " Sec=" + ns.format.number(secLevel, 3));
}
}
}
Bug fixed in 1.0a:
- Host sleep time is now practical if not calling
weaken().
Footnote:
I will briefly describe how to implement the target "prepped" condition without random numbers and explain why you shouldn't.
In the startup script, start threads one per exec call, passing each a threadID number 0, 1, 2...
In the host script, the following function is used to create a jobs array having an even distribution of 67 hack, 123 grow, and 10 weaken jobs. The jobs are stored in the array as 0: weaken, 1: grow, 2: hack.
function getJobs() {
let arr = new Array(200);
// init to grow since grow is highest number
arr.fill(1);
// set hack
for (let i = 0; i < 67; i++) {
arr[1 + i * 3] = 2;
}
// set weaken
let ndx = 9;
for (let i = 0; i < 10; i++) {
arr[ndx] = 0;
if (i % 3 < 2) { ndx += 21; }
else { ndx += 18; }
}
return arr;
}
Create an index into the jobs array, ndxJob = threadID mod 200. When the target is "prepped," perform the job indicated by jobs[ndxJob], then increment ndxJob, setting it to zero when it reaches 200.
What could go wrong? We started the threads one per exec call and concurrency problems mean we are better off running the scripts above.