Bitburner Journal Days 4 through 7

Bitburner journal part 2.

Part 1 is here

so I mentioned i was in the middle of day 4 when i left off.

i had done logger.js and hacknet-manager.js

at this point i have a logger in any script after importing it, and i have hacknet manager making me early game ram money, but nothing spectacular.

it's been a few irl days. at the time of writing it (the hacknet) has made me $18b. i'll start off by saying i got a tor router, bought all the port openers and then i spent as much money as i could on ram.

that left me with $4b and a max of 4.1 TB of ram on the home machine, which is enough to do a fair bit of nonsense.

up until now i haven't run a hack(), weaken() or grow() on any servers, which makes me feel awful. exclusively hacknet money.

irl day 4.5

i spent most of the tail end of day 4 writing what became my host scanning script.

there are many like it, but this one is mine. at the moment there isn't a lot to it.

hosts.js

/** @class Hosts : class constructed to retrieve a scanned-servers-list. */
export class Hosts {
  /** @type {Map<string, Host>} servers : a map of servers, keyed by name.  */
  #dictionary;
  /** @param {NS} ns : needed for host function suppliers.  */
  constructor(ns) {
    this.#dictionary = new Map();
    // prime our hosts object with pending and scanned arrays
    let hosts = [ns.getHostname()];
    // loop over the pending array which grows concurrently
    while (hosts.length > 0) {
      // pop a host from the stack
      let host = hosts.pop();
      // make a server object to represent them
      if (!this.#dictionary.has(host)) {
        this.#dictionary.set(host, new Host(ns, host));
        // add their connections to our pending list
        hosts.push(...ns.scan(host));
      }
    }
  }
  get dictionary() { return this.#dictionary; }
  list() { return this.dictionary.values(); }
}

/** @class Host : An object containing utilities germane to a particular server */
export class Host {  
  /** @type {NS} ns : a captured instance of NS needed for the server's various functions. */
  #ns;
  /** @param {string} host */
  constructor(ns, host) {
    this.#ns = ns;
    this.name = host;
  }
  currentMoney() { return this.#ns.getServerMoneyAvailable(this.name); }
  idealMoney() { return this.#ns.getServerMaxMoney(this.name); }
  currentSecurity() { return this.#ns.getServerSecurityLevel(this.name); }
  idealSecurity() { return this.#ns.getServerMinSecurityLevel(this.name); }
  hackingLevel() { return this.#ns.getServerRequiredHackingLevel(this.name); }
  portsToNuke() { return this.#ns.getServerNumPortsRequired(this.name); }
  hasRoot() { return this.#ns.hasRootAccess(this.name); }
  usedRam() { return this.#ns.getServerUsedRam(this.name); }
  maxRam() { return this.#ns.getServerMaxRam(this.name); }
  currentRam() { return this.maxRam() - this.usedRam(); }    
  hasFile(s) { return this.#ns.fileExists(s, this.name); }
}

the idea is if i need a host list in a script, i import hosts.js and then i can just do this

import { Hosts } from "hosts.js";
...
const hosts = new Hosts(ns);

that gives me a hosts object with a dictionary property and a list() method for either fetching specific hosts by name, or simply iterating over the values of the dictionary as an array; i often need both.

the point later is to be able to write plain-english looking stuff like this:

if (host.currentRam() >= script.ramNeeded() && host.hasRoot()) {
    host.tryExecute(script.fileName); // where tryExecute copies the file if needed
}

anyway i'm getting ahead of myself, we're not there yet. after i finished hosts i mostly took a break for a couple of days, because i had too much irl stuff to do.

irl day 5, 6?

i didn't get to write much on either of these days, too busy. but i spent a small amount of time writing something that became useful not too long after.

exploits.js

/** @class Exploits : class holding a collection of 5 port-breaking exploits */
export class Exploits {
  /** @type {Exploit[]} list : an array of exploits ordered by cost */
  #list;
  /** @param {NS} ns : needed for exploit function suppliers.  */
  constructor(ns) {
    this.#list = [
      new Exploit("BruteSSH.exe", 0, (s) => ns.brutessh(s)),
      new Exploit("FTPCrack.exe", 1, (s) => ns.ftpcrack(s)),
      new Exploit("relaySMTP.exe", 2, (s) => ns.relaysmtp(s)),
      new Exploit("HTTPWorm.exe", 3, (s) => ns.httpworm(s)),
      new Exploit("SQLInject.exe", 4, (s) => ns.sqlinject(s))
    ];
  }
  /** @type {Exploit[]} list : the array of the five exploits */
  get list() { return this.#list; }  
}

/** @class Exploit : class representing an exploit and the {NS} method to run. */
export class Exploit {
  constructor(fileName, index, execute) {
    this.fileName = fileName;
    this.index = index;
    this.execute = execute;
  }
}

while not especially complicated, the point of exploits.js is just to contain the logic of the exploits and running them, and give me a clean list of them when i need it. frankly there's only one thing i need it for. more on day 7.

irl day 7

today is the day i came back and did some new stuff. i didn't get a ton done because i had just a couple of hours to work on it, but i managed to put the finishing touches on an automatic port opener, using my hosts.js and my exploits.js scripts in conjunction with each other.

i kept trying to do things in a static context and having weird bugs. either i don't know how static context works in javascript, or bitburner itself isn't a fan. i wanted to make it so that all scripts which use exploits and hosts merely initialize a static collection of them, so they could be shared across all scripts instead of instanced. i found this had really strange behaviors (e.g. scripts suddenly aborting with no logging or error). when i changed to instances of them, things "just worked". anecdotally, LogLevels works fine and it's all static. i just don't know what i did wrong. i'll stick with what works for now.

(update: yichizhng on the discord confirmed my suspicion that static contexts act strangely, recommended putting things into globalThis or even window. tempting)

root-auto.js

import { Hosts, Host } from "hosts.js";
import { Exploits } from "exploits.js";
import { Logger, LogLevels } from "logger.js";
/** @param {NS} ns */
export async function main(ns) {
  const logger = new Logger(ns, LogLevels.info);
  const hosts = new Hosts(ns); // seed hosts.
  const exploits = new Exploits(ns);
  const home = hosts.dictionary.get("home");
  let isDone = false;
  while (!isDone) {
    isDone = true;
    let portOpeners = 0;    
    for (const exploit of exploits.list) {
      logger.trace(`searching for exploit ${exploit.fileName}`);
      if (home.hasFile(exploit.fileName)) {
        portOpeners++;
        logger.trace(`found exploit ${exploit.fileName}, portOpeners set to ${portOpeners}`);
      }
    }
    // loop over hosts
    for (const host of hosts.list()) {
      if (host.hasRoot()) {
        continue;
      }
      logger.trace(`attempting to crack host ${host.name} with ${portOpeners} exploits.`);
      if (host.portsToNuke() > portOpeners) {
        isDone = false;
        continue;
      }
      // loop over exploits by index
      for (const exploit of exploits.list) {
        if (!home.hasFile(exploit.fileName)) {
          continue;
        }
        logger.debug(`exploiting host ${host.name} with ${exploit.fileName}`);
        exploit.execute(host.name);
      }
      logger.info(`nuking host ${host.name}`);
      ns.nuke(host.name);
    }
    await ns.sleep(1000);
  }
}

at long last a script that actually does things. this script automatically opens the ports of all hosts (and then it roots them) if and when it can, while monitoring your home machine to see if you've bought a port opener. i like how it works.

anyway i know it isn't much but i thought i'd share some nonzero progress.

tomorrow i think will start earnestly working towards the daemon, but i can't promise i won't get distracted. i might like to build a server-farm-manager, as those can be extremely handy in the early game.

hopefully more soon.

Update: made it more clear that the crack-auto.js is auto-nuking, not just opening ports. Changed the name from crack-auto to root-auto, since that's really what it's seeking to do: nuke every machine. idk, sometimes i quibble over names.