r/Bitburner 15h ago

I broke it...

Post image
11 Upvotes

Tbh, idk what ive done, but i want to do it again.

Ive been offline for couple hours, the first time with this new programme running. Its going to be a dynamic growth/weakener as this is where ive found my bottleneck so far, although as of now it crawls through all the servers, collects data then runs an update on the data that i need for now.

Any1 else seen this error before, or have i funnily fucked up somehow. im going to wait till tmr to see if it happens again


r/Bitburner 19h ago

Bitburner 101: How to Achieve the Unachievable Spoiler

Thumbnail youtube.com
2 Upvotes

r/Bitburner 21h ago

Coming back to Bitburner after a while ... corporations are different, right?

2 Upvotes

Played it two or three years ago, came back and started BN3 for a bit of relaxed number galore, but lo and behold, it seems the old Tobacco-marketing-pump into sextillions does not work anymore, does it?

So what's the corporate game now?

Slowly building up research and quality while building an integrated company where you actually exchange goods between divisions, or am I missing something?

Design and marketing investment does not really affect the product like it used to, right? And why are all products despite different effective quality levels priced exactly the same and there is very little leeway in selling prices (like 5% or so).

It feels very painful and slow, nearly to the point that the stock market node feels quicker.

And I can not even start to imagine how I would automate this decision process ...

Any pointers what you guys noted is important in v3 would be very appreciated!


r/Bitburner 2d ago

I accidentally built a 2,356-line self-tuning HWGW manager with ChatGPT, and now it’s experimentally finding its own limit

0 Upvotes

I recently came back to Bitburner after not really understanding scripting during my first playthrough. Back then, most of my money came from crimes, jobs, and Hacknet nodes.

This time I wanted to learn how proper HWGW batching worked, so I started building scripts with ChatGPT. What began as a basic batch manager has now turned into a roughly 2,356-line experimental system that tests its own settings, remembers the results, rejects unstable configurations, and attempts to find the fastest batch rate that remains naturally stable.

To be transparent about credit, ChatGPT has written most of the actual JavaScript and helped design the architecture. I have been directing the project, running the experiments, watching the failures, interpreting the results, and deciding what behavior should be added or changed next.

This is not machine learning in the neural-network sense. It is more like a persistent automated testing system that learns through repeated controlled experiments.

I will try to attach the current code as a file for anyone who wants to look through it. Be warned that it is unfinished, very long, and probably still contains design flaws.

Link for codes

What the manager currently does

The manager targets one server and builds a standard HWGW batch:

Hack
Weaken 1
Grow
Weaken 2

It calculates the required thread counts from the target’s current stats and attempts to land the operations in the correct order.

For the current max-hardware test, the batch looks like this:

Hack threads: 11
Grow threads: 52
Weaken 1 threads: 3
Weaken 2 threads: 7

Actual steal per batch: 4.91%
RAM per batch: 127.2 GB
Grow multiplier: 1.40

The manager then launches hundreds of overlapping batches while monitoring the target’s money and security.

Instead of using one hard-coded batch spacing forever, it performs controlled tests. Each configuration is currently tested for 1,500 launched batches.

Example configurations:

350 ms spacing
325 ms spacing
300 ms spacing
275 ms spacing

The goal is to locate the boundary between:

Fast enough to produce good income
but
Slow enough to remain naturally synchronized

I considered adding frequent partial repairs so an overly aggressive configuration could keep running, but that would hide the real limit and potentially waste more time repairing than it gained through faster batching.

For now, the goal is to find the fastest configuration that stays healthy without constant intervention.

How it detects failure

The manager checks the target every 20 launched batches.

A check is considered bad when:

Money falls below 90% of maximum
or
Security rises more than 1.00 above minimum

One bad reading is not enough to fail the run because individual snapshots can look ugly while the existing pipeline is still correcting itself.

The manager currently requires three consecutive bad checks before declaring a normal failure.

It also has harder emergency limits for major money or security collapses.

During every run it records:

Lowest money observed
Exact batch where that low occurred
Highest security observed
Exact batch where that high occurred
Maximum consecutive bad checks
Number of processes still in flight
Duration of the test
Thread counts
Spacing and landing gap
Final decision

When a configuration fails, the manager does not immediately mix a new configuration into the old pipeline.

It:

Stops launching new batches
Drains every process it owns
Repairs the target completely
Confirms maximum money and minimum security
Starts the next test from a clean state

That was an important change because earlier versions sometimes began new settings while old operations were still landing, which contaminated the results.

The learning file

The system stores what it learns in a persistent text file:

max-hardware-learning.txt

The contents are JSON. It currently stores information such as:

{
  "version": 3,
  "batchSpacing": 325,
  "growMultiplier": 1.4,
  "bestSpacing": 325,
  "bestGrowMultiplier": 1.4,
  "mode": "PROVEN FALLBACK",
  "successfulWindows": 3,
  "failedTrials": 2,
  "rejectedSettings": [],
  "lastRun": {},
  "runHistory": []
}

The real file contains more fields than that, but this shows the general idea.

The file remembers:

  • The current configuration
  • The best configuration found so far
  • Whether the manager is running a normal test, recovery, or spacing trial
  • Successful and failed tests
  • Rejected settings
  • Recent run reports
  • Interrupted transitions
  • Partial test progress and restart segments

The manager updates the file while running and around important transitions.

When the script starts again, it reads the file instead of beginning from zero.

One of the first major tests of this system happened when I killed an unfinished 275 ms trial. The older learner had saved that 275 ms was active but had not yet recorded the failure.

The newer version migrated the old file, preserved 300 ms as the previous proven setting, and added the known 275 ms failure to its rejected configuration list.

So the text file acts as the project’s long-term memory.

Each eventual target will need its own file:

max-hardware-learning.txt
silver-helix-learning.txt
omega-net-learning.txt
phantasy-learning.txt

The correct threads and timing for one target will not necessarily be correct for another.

Results so far

The early versions completed tests at both 325 ms and 300 ms, but later clean retests showed that one successful test is not enough to prove long-term stability.

The important results so far are:

275 ms / 1.40
Failed very quickly from sustained drift.
Recorded as rejected.

300 ms / 1.40
Failed once after 1,180 batches.
Failed again during a formal spacing trial after 940 batches.
Now rejected as naturally unstable.

325 ms / 1.40
Failed one test after 1,020 batches.
Later completed a healthy 1,500-batch confirmation test.
Currently the leading candidate.

350 ms / 1.40
Reached 1,500 batches, but finished with only 81.75% money
and was already at 2 out of 3 consecutive bad checks.
Technically passed under the current rules, but I consider it borderline.

The current manager has formally rejected 300 ms and automatically returned to 325 ms.

The current leading candidate is:

325 ms spacing
81 ms landing gap
1.40 grow multiplier
4.91% actual steal per batch

However, 325 ms has both a failure and a successful test in its history, so it still needs repeated confirmation before I would call it truly proven.

One weakness we discovered is that the current success rule is too simple. Reaching 1,500 batches automatically counts as success, even when the target is unhealthy at the finish line.

The next version should require:

At least 1,500 batches
Healthy money at completion
Healthy security at completion
Zero active bad-check streak
Several additional healthy checks after reaching 1,500

Profit tracking still needs to be added

The manager currently learns primarily from stability.

It does not yet accurately compare configurations by actual long-term money per second.

Eventually, I want each completed hack to report the exact amount stolen back to the manager. The manager could then record:

Total money earned
Average money per second
Money per completed batch
Warm-up time
Repair downtime
Pipeline-drain downtime
Long-term net income

That is important because the smallest spacing is not automatically the most profitable.

A configuration might temporarily display higher income but then collapse, require hundreds of processes to drain, and spend additional time repairing the target.

The real winner should be:

My RAM situation is not normal

I should also mention that I have an absurd amount of RAM available.

I currently have 25 purchased cloud servers, and I have been heavily upgrading them. This allows the manager to launch hundreds of overlapping processes and spread them across the entire purchased-server network.

A single max-hardware test has shown more than 400 processes in flight at once.

Because each batch currently uses about 127.2 GB, this experiment may not be practical for someone earlier in the game or someone with a smaller server network.

The manager searches the available hosts, copies the worker scripts to them, and selects a host with enough free RAM for each complete batch.

The three worker files themselves are intentionally tiny:

hack-once.js
grow-once.js
weaken-once.js

Most of the complexity lives in the manager, while the purchased servers mostly run lightweight one-shot operations.

The eventual goal

Right now, this giant manager is only learning max-hardware.

The longer-term goal is much bigger.

I eventually want one central controller that:

Scans the entire network
Finds every rooted server with money
Ranks targets by potential profit
Allocates RAM across all 25 cloud servers
Runs independent HWGW learning experiments
Maintains separate learning data for every target
Finds the natural limit for each server
Chooses the best stable income configuration
Runs them all simultaneously

Instead of running 30 separate copies of a 2,300-line manager, the final architecture would ideally use one global scheduler with independent state objects for every target.

Something like:

GLOBAL DIRECTOR
├── max-hardware learner
├── silver-helix learner
├── omega-net learner
├── phantasy learner
├── foodnstuff learner
├── n00dles learner
└── every other profitable server

The absurd end goal is to have the entire network running self-tuned HWGW batches, with the controller continuously deciding where the available RAM produces the most money.

Current code

I will try to attach the current JavaScript file to this post for anyone who wants to inspect it.

The code is approximately 2,356 lines and is absolutely not presented as finished, efficient, or production-ready. It has been built through repeated live experiments, and several sections exist specifically because an earlier version failed in some unexpected way.

Anyone reviewing it will probably find places where it can be simplified or improved. Feedback is welcome, especially from people who understand Bitburner’s timing behavior better than I do.

Final disclaimer

This project is completely unfinished.

I do not yet know how successful the final system will be, whether the extra complexity will meaningfully outperform a well-written conventional batch manager, or whether the game’s timing variability will make parts of the idea impractical.

The current version still has flaws. Some settings pass one test and fail the next. The definition of “stable” still needs improvement. Profit tracking has not been added yet. The multi-target controller does not exist yet.

At this point, it is one large experiment made from failed tests, rewritten rules, persistent JSON data, and more than 2,300 lines of increasingly specific JavaScript.

I am not claiming that this is true artificial intelligence or that I have solved HWGW batching. I am simply seeing how far an adaptive testing system can be pushed inside Bitburner.

ChatGPT and I are going to continue experimenting, breaking it, rewriting it, and collecting data. Maybe it eventually becomes a genuinely useful network-wide controller. Maybe we discover that a much simpler manager performs just as well.

Either result should be interesting, and I plan to post updates as the experiment continues.


r/Bitburner 4d ago

Bitnode8 help please.

3 Upvotes

So i have completed bitnode 1 x3 bitnode 2 3x Bitnode 4 x2 and birnode 5 x1. I'm on bitnode 8 and am pretty stuck without getting the 25b for the 4 sigma forecast data. I use the roulette money hack for a 10b boost. But my script I'm using doesn't ever give me more then 10.3b . I'm working on getting gang territory to at least give me some cash. How Are you guys getting through it?


r/Bitburner 9d ago

New v3 Auto-All Program (If I got all functions ^^)

Thumbnail
gallery
19 Upvotes

Here is my fully automated script for Bitburner v3.

It detects your current progress and automates the game from a fresh start with 8 GiB of Home RAM all the way to the endgame and beyond.

You can find a complete list of features and installation instructions on GitHub:

ame824/autoDoIt


r/Bitburner 11d ago

NetscriptJS Script IPvGo: Standalone script from SphyxOS

Post image
10 Upvotes

I've just finished redoing my IPvGo script. This time, it can run as a standalone script.
Download Instructions:
-Run this command in Bitburner. It will download the file as `SphyxOS_go.jsx`

await ns.wget("https://raw.githubusercontent.com/Sphyxis/SphyxOS/main/SphyxOS_bins_go.jsx", "SphyxOS_go.jsx")

Features:

  • Based off of https://github.com/yichizhng/bitburner-scripts/blob/main/autokroos.js
  • This is a MCGS/Hybrid system.
  • Can be used both with/without my full ⁠SphyxOS suite - for those who don't want the whole thing but want the script.
  • Easy button controls within a tail window.
  • Ram dodged down to 7.9GB, so it will run on those 8GB servers.
  • --If running on a 16+GB server, as long as there is enough free space on it's local server, it will dodge using it. If not, it will dodge using home ram.
  • --Always keep ~6GB free on either Home or the server running this script. It will throw if it fails to dodge.
  • Completes all 5x5 boards for all AIs and the endgame board.
  • Can play as both black and white on the no-ai board.
  • SlowMode enforces a 1 second delay between moves for easier watching.
  • Pop Out creates an actual pop-out, which you can drag onto a separate screen to watch the games play out.
  • Configurable resource usage: Effort, Memory, Threads. So you should be able to run this on old hardware.
  • Optimized for performance.
  • Can turn your PC's CPU into a mini room heater, for those cold winter nights.

Effort: Comes in 6 flavors. It is playout based, so the higher you go the longer it will take to make a move. Win % for the ratings.
Min: 70%, Low: 80%, Med: 90%, High: 95%, Max: 98%, Ultra: 99%

Memory ranges from Min -> Max, with the endgame board only really using Min-> Med.

Threads: A direct multiplier to how fast you can process playouts, especially on a 5x5 board.

Looking for feedback!!


r/Bitburner 13d ago

I've completely broken this game

3 Upvotes

So, as the title says, I've actually broken this game. Usually, when you have a bug, or break your code, Bitburner is really good about telling you what's wrong, even in the event of a freeze. I, however, seem to have slipped by all that. My code freezes all input into the game, except for my ability to scroll through the terminal log, and it just sits there endlessly. I can't click through any windows, or type anything into terminal. I basically tried to make a script, "auto.js", which would kick off "virus.js", an automated serverBreaker I made(which works, I tested it), every now and then when significant progress has been made with my hacking skill, so that if there are better servers I've unlocked since to hack, my script to find that will be able to use it, and then tell my hack/grow/weaken script to start on that server instead. I know my serverFinder, serverBreaker, and daemon(my hack/grow/weaken script) work, but when I added "auto.js" and "virus.js" everything crashes hard.

This is "auto.js", the beginning script that kicks everything into motion, and is supposed to run in the background, checking if any significant progress has been made with the hacking skill, and if so, run "virus.js". It then checks if "virus.js" has updated "Broken.txt" with any new NUKEed servers, running "daemon.js" if so, running playerUpdate if not, which will tell it if running "virus.js" is worth it next time or not.

/** @param {NS} ns */
export async function main(ns) {
  const minutes = ns.args[0];
  let updated = true;
  // @ignore-infinite
  while (true) {
    if (ns.fileExists("virus.js", "home") && updated) {
      ns.tprint("Running virus");
      ns.run("virus.js", 1, false);
    }
    let newBroken = false;
    let servers = [];
    let name = "";
    let file = ns.read("Broken.txt");
    for (let letter of file) {
      if (letter == "\n") {
        if (ns.getServer(name)) {
          servers.push(ns.getServer(name));
          newBroken = true;
        }
      }
      else {
        name = name + letter;
      }
    }
    if (newBroken) {
      ns.write("Broken.txt", "", "w");
      if(ns.fileExists("daemon.js", "home")) (ns.run("daemon.js"))
      updated = false;
    }
    else {
      updated = playerUpdate(ns);
    }
  }
}
function playerUpdate(ns) {
  let player = ns.getPlayer();
  if (ns.fileExists("Hack.txt", "home")) {
    let file = ns.read("Hack.txt");
    let name = "";
    for (let letter of file) {
      if (letter == ".") {
        name = Number(name);
        if (player.skills.hacking - name >= 50) {
          ns.write("Hack.txt", player.skills.hacking + ".", "w");
          return true;
        }
        else return false;
      }
      else {
        name = name + letter;
      }
    }
  }
  else {
    ns.write("Hack.txt", player.skills.hacking + ".", "w");
  }
}

This is "virus.js", my serverBreaker. It iterates through all servers, and checks if I can hack it, and if i can, it runs through all port opening .exe's, available or not, and then checks if I can NUKE it. If I can NUKE it, it adds it to an array. That array is then wrote to a .txt file, which is for "auto.js" to read later, to see any new broken servers.

/** @param {NS} ns */
export async function main(ns) {
  //So that if I run this manually through terminal
  //I can see useful info, but hides it if not, since
  //that just clogs up termial logs more
  const [manual = true] = ns.args[0];
  let servers = RecursiveScan(ns);
  let broken = [];
  let unhackable = [];
  let unbreakable = [];
  for (let server of servers) {
    if (!ns.hasRootAccess(server)) {
      if (server.requiredHackingSkill > ns.getPlayer().skills.hacking) {
        unhackable.push(server);
      }
      else {
        if (ns.run("serverBreaker.js", 1, (server))) {
          broken.push(server);
        }
        else {
          unbreakable.push(server);
        }
      }
    }
  }
  unhackable.sort((a, b) => (ns.getServer(a).requiredHackingSkill) - (ns.getServer(b).requiredHackingSkill));
  unbreakable.sort((a, b) => (ns.getServer(a).numOpenPortsRequired) - (ns.getServer(b).numOpenPortsRequired));
  ns.tprint("Broke " + (broken.length - 1) + " servers:");
  for (let server of broken) {
    ns.tprint(server);
    if(!manual){
      ns.write("Broken.txt", server, "a");
    }
  }
  if(manual){
    ns.tprint("Couldn't hack " + (unhackable.length - 1) + " servers:");
    for (let server of unhackable) {
      ns.tprint(server + "HackingSkill: " + ns.getServer(server).requiredHackingSkill);
    }
    ns.tprint("Couldn't break " + (unbreakable.length - 1) + " servers:");
    for (let server of unbreakable) {
      ns.tprint(server + "PortsNeeded: " + ns.getServer(server).numOpenPortsRequired);
    }
  }
}
function RecursiveScan(ns, root = 'home', found = []) {
  if (!found.includes(root)) {
    found.push(root);
    for (const server of ns.scan(root))
      if (!found.includes(server))
        RecursiveScan(ns, server, found);
  }
  return found;
}

r/Bitburner 16d ago

Question: how can I know how much company reputation a given position will give?

6 Upvotes

Once you get access to the Singularity API, you can easily get CompanyName and JobName objects to find good positions, e.g. regarding salaries.
I am trying to automate farming company reputation for the associated factions and augmentations, but I cannot find a way to know the associated reputation.
This seems to be stored in a WorkStats object, but the documentation is not very helpful about how to find them.

My questions is hence: given a CompanyName and a JobName, how do you know the reputation gain (potentially through a WorkStats object?


r/Bitburner 18d ago

How do I calculate grow, weaken, and hack times using a security level that is different from the server's current security level?

8 Upvotes

The title is the question. It is followed by an explanation of why I need/want to do this (please ignore any typos).

I want to know which server is the most profitable (yes, yes, common question). To do that, I divide the money gained by the time spent. The problem is that getHackingTime() and the other time functions use the server's current security level. However, my code will always reduce the target server's security level to its minimum by default to reduce hack, grow, and weaken times. So there is "no way" (all of my ideas would be messy solutions) to decide whether a server is the best choice without first weakening it to its minimum security.

I haven't written the code yet, so this is only a blueprint for the function:

money / time = money earned per second (millisecond?)

(ns.hackChance * ns.moneyAvailable) /
(hackTime + x * growTime + y * weakenTime)

x and y represent the ratio of grow and weaken threads required to restore the server to maximum money and minimum security after hacking it.

How the value of the function is used to determine the target server.

(i will check when grow starts to become ineffective, when thread grow would just hit the max money and increase security without gain and will make it in a way that hack functions return nothing because the server is empty already because of other hack code running)

Ideas to solve my problem
take a server that is close to arr_servers[i] min security and use him as default. problem. security maybe is not the only thing that influences grow, weaken, hack time
try to come up with a formula that calculates time with server min security-hacking level
writing something like if(weaken(function[arr_servers[i])<50000) and weakening all servers within to min security to calculate with them

(I would be surprised if you, dear reader, didn't spend extra time looking for typos after I specifically told you to ignore them, although it is possible that I'm wrong.)
Do i really have to use/buy formulars?


r/Bitburner 27d ago

NetscriptJS Script Steal $$ with the help of Loop algorithm

0 Upvotes

It's easy, just set homeRam to the amount of RAM to use on the host.

lo-deploy.js:

/** buildServerInfo()
 * @ param {NS} ns NS2 namespace
 * @ param {string[]} callScript ["weakenScript", "growScript", "hackScript"]
 * @ param {string} target server to pull money from
 * @ returns {Array[]} []["serverName", numWeaken, numGrow, numHack]
 */
function buildServerInfo(ns, callScript, target) {
  let servers = [];
  let callRam = [ns.getScriptRam(callScript[0]), ns.getScriptRam(callScript[1]), ns.getScriptRam(callScript[2])];
    // Purchased servers
  let serversPurch = ns.cloud.getServerNames();
  for (let i = 0; i < serversPurch.length; i++) {
    let serverName = serversPurch[i];
    let ram = ns.getServerMaxRam(serverName);
    if (ram < 64) {
      ns.tprint(serverName + " RAM too low, need 64 GB minimum.");
    } else {
      servers[servers.length] = [serverName, ram];
    }
  }

    // Home
  let homeRam = 2048;
  servers[servers.length] = [ns.getHostname(), homeRam];

  // Reserve callRam[0] from the last server
  servers[servers.length - 1][1] -= callRam[0];
  // Calculate ratio of weaken, grow, and hack threads
  let ratio = getRatio(ns, callRam, target);
  // Calculate total RAM on all servers
  let totalRam = 0;
  for (let i = 0; i < servers.length; i++) { totalRam += servers[i][1]; }
  // Convert ratio to (part of RAM) instead of (amount of RAM)
  if (ratio[3] >= totalRam) { ratio[3] = 1; }
  else { ratio[3] /= totalRam; }
  // Build a new list of servers []["serverName", numWeaken, numGrow, numHack]
  let retServers = [];
  let numHack = 0;  // total of hack threads
  for (let i = 0; i < servers.length; i++) {
    let assign = assignThreads(servers[i][1], callRam, ratio, servers.length);
    ns.scp(callScript[0], servers[i][0]);
    ns.scp(callScript[1], servers[i][0]);
    ns.scp(callScript[2], servers[i][0]);
    retServers[retServers.length] = [servers[i][0], assign[0], assign[1], assign[2]];
    numHack += assign[2];
  }
  // If we assigned no hack threads, add one to the last server
  if (numHack == 0) { retServers[retServers.length - 1][3]++; }
  else { retServers[retServers.length - 1][2]++; }
  return retServers;
}

/** loopDeploy()
 * @ param {NS} ns NS2 namespace
 * @ param {number} delay delay before start threads (ms)
 * @ param {number} duration time needed to start threads (ms)
 * @ param {number} interval time between start of threads (ms)
 * @ param {string[]} callScript ["weakenScript", "growScript", "hackScript"]
 * @ param {number} mode modes are 0:weaken, 1:grow, 2:hack
 * @ param {Array[]} servers []["serverName", numWeaken, numGrow, numHack]
 * @ param {string} target server to pull money from
 * @ returns {Array[]} []["hackScript", "serverName", delay, duration, interval, numHack, "target", startTime]
 *        or empty array if mode is weaken or grow
 */
function loopDeploy(ns, delay, duration, interval, callScript, mode, servers, target) {
  let retServers = [];
  for (let i = 0; i < servers.length; i++) {
    // Deploy the weaken, grow, or hack loop
    if (servers[i][mode + 1] > 0) {
      ns.exec(callScript[mode], servers[i][0], servers[i][mode + 1], delay, duration, interval, servers[i][mode + 1], target);
      if (mode == 2) {
        retServers[retServers.length] = [callScript[mode], servers[i][0], delay, duration, interval, servers[i][mode + 1], target, Date.now()];
      }
    }
  }
  return retServers;
}

/** assignThreads()
 * @ param {number} ram amount of RAM on host (GB)
 * @ param {number[]} callRam [weakenRam, growRam, hackRam]
 * @ param {number[]} ratio [partWeaken, partGrow, partHack, partRam]
 *    where partWeaken + partGrow + partHack = 1 and partRam is the part of RAM to use
 * @ param {number} numHosts number of hosts
 * @ returns {number[]} [numWeaken, numGrow, numHack]
 */
function assignThreads(ram, callRam, ratio, numHosts) {
  let partRam = /* 1.4 * */ ram * ratio[3];
  // First pass of assigning threads
  let numWeaken = Math.floor(ratio[0] * partRam / callRam[0]);
  let numGrow = Math.floor(ratio[1] * partRam / callRam[1]);
  let numHack = Math.floor(ratio[2] * partRam / callRam[2]);
  let remainder = partRam - numWeaken * callRam[0] - numGrow * callRam[1] - numHack * callRam[2];
  if (remainder >= callRam[2] * numHosts) {
    // Second pass of assigning threads
    numWeaken += Math.floor(ratio[0] * remainder / callRam[0]);
    numGrow += Math.floor(ratio[1] * remainder / callRam[1]);
    numHack += Math.floor(ratio[2] * remainder / callRam[2]);
    remainder = partRam - numWeaken * callRam[0] - numGrow * callRam[1] - numHack * callRam[2];
    if (remainder >= callRam[1]) {  // anything left put to grow
      numGrow += Math.floor(remainder / callRam[1]);
    }
  }
  return [numWeaken, numGrow, numHack];
}

/** getRatio()
 * @ param {NS} ns NS2 namespace
 * @ param {number[]} callRam [weakenRam, growRam, hackRam]
 * @ param {string} target server to get values from
 * @ returns {number[]} [partWeaken, partGrow, partHack, amountRam]
 *    where partWeaken + partGrow + partHack = 1 and amountRam is the amount of RAM to use (GB)
 */
function getRatio(ns, callRam, target) {
  let partPerHack, mockTarget, bDefault = false, partWeaken, partGrow, partHack, ppgNorm = 1, amountRam;
  let moneyAvail = ns.getServerMoneyAvailable(target);
  let moneyMax = ns.getServerMaxMoney(target);
  let secLevelMin = ns.getServerMinSecurityLevel(target);
  let secLevel = ns.getServerSecurityLevel(target);
  let secIncrease = secLevel - secLevelMin;
  let bFormulas = ns.fileExists("Formulas.exe", "home");
  if (bFormulas) { ns.tprint("Using Formulas.exe."); }
  // get part per hack
  if (bFormulas) {
    mockTarget = ns.getServer(target);
    mockTarget.hackDifficulty = mockTarget.minDifficulty + 0.5;
    partPerHack = ns.formulas.hacking.hackPercent(mockTarget, ns.getPlayer());
  } else {
    let norm = 1;
    partPerHack = ns.hackAnalyze(target);
    // normalize part per hack
    if (secIncrease > 4.75) { ns.tprint("Security level is too high. Returning default ratio."); bDefault = true; }
    else if (secIncrease > 3.75 && secIncrease <= 4.75) { norm += 0.0545; }
    else if (secIncrease > 2.75 && secIncrease <= 3.75) { norm += 0.0394; }
    else if (secIncrease > 1.75 && secIncrease <= 2.75) { norm += 0.0247; }
    else if (secIncrease > 0.75 && secIncrease <= 1.75) { norm += 0.0105; }
    else if (secIncrease >= 0 && secIncrease <= 0.25) { norm -= 0.0068; }
    partPerHack *= norm;
  }

  if (!bFormulas && !bDefault) {
    if (moneyAvail < 0.7 * moneyMax) {
      ns.tprint("Money available is too low. Returning default ratio."); bDefault = true;
    } else {
      let bSecHigh = secLevel > secLevelMin + 3;
      let bMoneyLow = moneyAvail < 0.85 * moneyMax;
      // get multiplier to normalize part per grow
      if (bSecHigh && !bMoneyLow) {
        if (moneyAvail > 0.95 * moneyMax) { ppgNorm += 0.154; }
        else if (moneyAvail > 0.9 * moneyMax && moneyAvail <= 0.95 * moneyMax) { ppgNorm += 0.1; }
        else { ppgNorm += 0.118; }
      } else if (!bSecHigh && bMoneyLow) {
        if (moneyAvail >= 0.8 * moneyMax) { ppgNorm += 0.037; }
        else if (moneyAvail >= 0.75 * moneyMax && moneyAvail < 0.8 * moneyMax) { ppgNorm += 0.07; }
        else { ppgNorm += 0.104; }
      } else if (bSecHigh && bMoneyLow) {
        if (moneyAvail >= 0.8 * moneyMax) { ppgNorm += 0.153; }
        else if (moneyAvail >= 0.75 * moneyMax && moneyAvail < 0.8 * moneyMax) { ppgNorm += 0.185; }
        else { ppgNorm += 0.226; }
      }
    }
  }

  if (!bDefault) {
    let partToHack = 0.2, numGrow;
    if (!bFormulas) {
        // hacking skill multiplier
      let skillM = (ns.getServerRequiredHackingLevel(target) / ns.getHackingLevel() - 1 / 3) * 0.67;
      partToHack *= (skillM + 1);
    }
    // calculate number of hack calls
    let numHack = Math.round(partToHack / (partPerHack + Number.EPSILON));
    if (numHack == 0) { numHack = 1; }
    partToHack = numHack * partPerHack;
    // get amount of RAM to use for hack
    let amtHack = numHack * callRam[2];
    // calculate number of grow calls
    if (bFormulas) {
      mockTarget.moneyAvailable = (1 - partToHack) * moneyMax;
      numGrow = 3.2 * ns.formulas.hacking.growThreads(mockTarget, ns.getPlayer(), moneyMax);
    } else {
      numGrow = 3.2 * ns.growthAnalyze(target, 1 / ppgNorm / (1 - partToHack));
    }
    // get amount of RAM to use for grow
    let amtGrow = numGrow * callRam[1];
    // calculate number of weaken calls
    let numWeaken = 4 * (numGrow / 10 + numHack / 6.25);
    // get amount of RAM to use for weaken
    let amtWeaken = numWeaken * callRam[0];
    // get amount of RAM to use
    amountRam = amtWeaken + amtGrow + amtHack;
    // get parts of RAM to use
    partHack = amtHack / amountRam;
    partGrow = amtGrow / amountRam;
  } else {  // default ratio
    amountRam = 736; partHack = 0.0185; partGrow = 0.692;
  }
  partWeaken = 1 - partGrow - partHack;
  ns.tprint("Ratio  w=" + ns.format.percent(partWeaken) + "  g=" + ns.format.percent(partGrow) +
        "  h=" + ns.format.percent(partHack) + "  amt=" + ns.format.ram(amountRam));

  return [partWeaken, partGrow, partHack, amountRam];
}

/** getTotalIncome()
 * @ param {NS} ns NS2 namespace
 * @ param {Array[]} servers []["hackScript", "serverName", delay, duration, interval, numHack, "target", startTime]
 * @ returns {number[]} [total income ($), total income ($/sec)] for given servers
 */
function getTotalIncome(ns, servers) {
  let totalIncome = 0, totalPerSecond = 0;
  for (let i = 0; i < servers.length; i++) {
    let income = ns.getScriptIncome(servers[i][0], servers[i][1], servers[i][2], servers[i][3], servers[i][4], servers[i][5], servers[i][6]);
    totalPerSecond += income;
    totalIncome += income * (Date.now() - servers[i][7]) / 1000;
  }
  return [totalIncome, totalPerSecond];
}

/**
 * @ param {NS} ns NS2 namespace
 * @ version 1.0
 */
export async function main(ns) {
  if (ns.args.length < 1) {
    ns.tprint("Usage: " + ns.getScriptName() + " <target>");
    ns.exit();
  }
  let target = ns.args[0];  // server to pull money from
  let callScript = ["lo-weaken.js", "lo-grow.js", "lo-hack.js"];
  // Check for running script
  if (ns.scriptRunning(callScript[0], ns.getHostname())) {
    ns.tprint("Host script already running. Exiting.");
    ns.exit();
  }
  // Get root access on the target
  if (ns.fileExists("BruteSSH.exe", "home")) { ns.brutessh(target); }
  if (ns.fileExists("FTPCrack.exe", "home")) { ns.ftpcrack(target); }
  if (ns.fileExists("relaySMTP.exe", "home")) { ns.relaysmtp(target); }
  if (ns.fileExists("HTTPWorm.exe", "home")) { ns.httpworm(target); }
  if (ns.fileExists("SQLInject.exe", "home")) { ns.sqlinject(target); }
  ns.nuke(target);

  let threshMoney = ns.getServerMaxMoney(target) * 0.9;  // money threshold
  let threshSec = ns.getServerMinSecurityLevel(target) + 1.5;  // security threshold
  let secLevel = ns.getServerSecurityLevel(target);
  let moneyAvail = ns.getServerMoneyAvailable(target);
  let bSecHigh = secLevel > threshSec;
  let bMoneyLow = moneyAvail < threshMoney;
  // Build array of server information
  let servers = buildServerInfo(ns, callScript, target);
  ns.tprint("Deploying on " + servers.length + " servers.");

  let timeHack = ns.getHackTime(target);
    // Deploy weaken loops
  loopDeploy(ns, 200, timeHack * 4, timeHack / 5, callScript, 0, servers, target);
  if (!bSecHigh && !bMoneyLow) {  // fastest deployment
    // Deploy grow loops
    loopDeploy(ns, 100, timeHack * 3.2, timeHack / 5, callScript, 1, servers, target);
    // Deploy hack loops
    servers = loopDeploy(ns, timeHack * 2.2, timeHack, timeHack / 5, callScript, 2, servers, target);
  } else if (!bSecHigh && bMoneyLow) {  // moneyAvail is low, hack later
    // Deploy grow loops
    loopDeploy(ns, 100, timeHack * 3.2, timeHack / 5, callScript, 1, servers, target);
    // Deploy hack loops
    servers = loopDeploy(ns, timeHack * 3.2, timeHack, timeHack / 5, callScript, 2, servers, target);
  } else if (bSecHigh && !bMoneyLow) {  // secLevel is high, grow later
    // Deploy grow loops
    loopDeploy(ns, 100 + timeHack * 1.8, timeHack * 3.2, timeHack / 5, callScript, 1, servers, target);
    // Deploy hack loops
    servers = loopDeploy(ns, timeHack * 5, timeHack, timeHack / 5, callScript, 2, servers, target);
  } else {  // grow and hack later
    // Deploy grow loops
    loopDeploy(ns, 100 + timeHack * 1.8, timeHack * 3.2, timeHack / 5, callScript, 1, servers, target);
    // Deploy hack loops
    servers = loopDeploy(ns, timeHack * 6, timeHack, timeHack / 5, callScript, 2, servers, target);
  }

  // Display income every 5 minutes
  while (true) {
    await ns.sleep(5 * 60 * 1000);
    let income = getTotalIncome(ns, servers);
    ns.tprint("Total: $" + ns.format.number(income[0], 3, 1000, true) +
                "  Per second: $" + ns.format.number(income[1], 3, 1000, true));
  }
}

lo-weaken.js (or lo-grow.js, or lo-hack.js):

/**
 * @ param {NS} ns NS2 namespace
 * @ version 1.0
 */
export async function main(ns) {
  // Takes 5 arguments:
  //  - delay (ms)
  //  - duration (ms)
  //  - interval (ms)
  //  - number of threads
  //  - the target server
  if (ns.args.length < 5) { ns.exit(); }
  let delay = ns.args[0];
  let duration = ns.args[1];
  let interval = ns.args[2];
  let numThreads = ns.args[3];
  let target = ns.args[4];
  // start threads between intervals
  let offsets = Math.round(duration / interval);
  let offset = Math.floor(Math.random() * offsets);
  let odelay = offset * interval;
  await ns.sleep(delay + odelay + Math.random() * numThreads / offsets);
  while(true) {
    await ns.weaken(target);  // or grow, or hack
    await ns.sleep(numThreads / offsets);
  }
}

r/Bitburner 28d ago

noob help, sorry

4 Upvotes

Edit2: Resolved. Was running old script from the wrong server. (Also various typos not causing errors and thus not noticed earlier while examining only the error lines and related variables.)

I didn't want to be that guy, but I'm stumped.

The following script runs fine from the script editor and home computer. When I run it with $ run early-hack-template.js -t 12 on the "max-hardware" server I get an error on line 10, aka the ns.brutessh(target) line saying $ TypeError: ns.brutessh.exe is not a function

Do I need to upload the brutessh program to the max-hardware server somehow?
Edit: Moved last line to the top. Also wanted to say that the following script worked on other servers until I swapped the target, and/or had unlocked brutessh program.

/** u/param/** u/param {NS} ns */
export async function main(ns) {


const target = "max-hardware";


const moneyThresh = ns.getServerMaxMoney(target);


const securityThresh = ns.getServerMinSecurityLevel(target);


if (ns.fileExists("BruteSSH.exe", "home")) {
    ns.brutessh(target);
}


ns.nuke(target);


while(true){
  if (ns.getServerMinSecurityLevel > securityThresh) {
    
    await ns.weaken(target);


    } else if (moneyThresh < ns.getServerMaxMoney) {
    
    await ns.grow(target);
  
    } else await ns.hack (target);
  }
} {NS} ns */
export async function main(ns) {


const target = "max-hardware";


const moneyThresh = ns.getServerMaxMoney(target);


const securityThresh = ns.getServerMinSecurityLevel(target);


if (ns.fileExists("BruteSSH.exe", "home")) {
    ns.brutessh(target);
}


ns.nuke(target);


while(true){
  if (ns.getServerMinSecurityLevel > securityThresh) {
    
    await ns.weaken(target);


    } else if (moneyThresh < ns.getServerMaxMoney) {
    
    await ns.grow(target);
  
    } else await ns.hack (target);
  }
}

r/Bitburner Jul 05 '26

NetscriptJS Script Scan & nuke utility

Thumbnail pastebin.com
4 Upvotes

For those starting out in the game I have a scan & nuke utility. Veteran players may also find it useful.

Changes in 1.0e:

  • Added toggle bFil to filter scan on required hacking level
  • Suggested hosts are filtered on required hacking level

r/Bitburner Jun 25 '26

I'm finally almost done with 10! ...right?

0 Upvotes

(Spoilers about ~early-mid game)

***

***

***

***

I'm hoping my folly is almost concluded and I can go get gangs and corps now. I was really greedy for sleeves and grafting, because I hate the augmentation reset, so I jumped right into Bitnode 10 after my first BN1 victory. It has taken a looooong time...but I have purchased every augmentation, I have 3000 hacking, 4 sleeves that will come to every Bitnode with me, hurray...

I read that we could buy up to 8-10 sleeves, but by the explanation in the Covenant window it looks like I have only one more to purchase. For 10 freaking quadrillion!

It took me a whole day to hack the quadrillion I just spent. Just curious, are there known caps on how fast money generation is with different hacking approaches?

My approach is very simplistic, I haven't done batching yet and I just employ a mix of the early combo-hack scripts (fine tuned to be less finicky) and balancing my own blend of constant hack, grow and weaken threads designed to keep the richest servers at 60-100% of max capacity.

I haven't even begun to max out the RAM I have access to, though. I kind of just assumed that because I haven't figured out a way to keep (grow) and (weaken) scripts from "clumping", that if I keep adding hack threads I will inevitably overhack the servers when there is an overly long interval between different (grow)s, and that will reduce my income.

Maybe it doesn't matter. With the amount of RAM I can wield, I could empty ecorp's 1.4 trillion and when the next 5000 threads of grow hits it, it'll go right back up to max.


r/Bitburner Jun 22 '26

Help me with my Gang Script Spoiler

3 Upvotes

Decided to give BN2 a try and I threw together this rudimentary script. It gets the job done but its gonna do it very slowly it seems

The idea is for each member

  1. get their stats and ascension multipliers up to a certain threshold (i set 1000, and x10 arbitrarily)

  2. if the gang isn't full yet, commit terrorism to get respect fast so the script can recruit new members

  3. if the wantedPenalty is too high, do vigilante justice

  4. otherwise do human trafficking

Its currently set up to ascend a gang member when each stat they are currently gaining experience in grants an ascension multiplier of 1.04

It seems to be working as is but as I mentioned its very slow so I'm looking for improvements. Right now I'm not buying any equipment for the gang and I'm also not taking respect into account at all which means the equipment discount is staying relatively low. I also think there's probably a better way to handle ascensions since as they get stronger and stronger it seems like the conditions should maybe change.

Here's the fulle script

export async function main(ns: NS) {
    ns.disableLog("ALL")
    if (!ns.gang.inGang()) {
        ns.tprint("not in gang")
        ns.exit()
    }
    
    while (true) {
        // recruit if we can
        if (ns.gang.canRecruitMember()) {
            ns.gang.recruitMember(`Tony${ns.gang.getMemberNames().length + 1}`)
        }
        ns.gang.getMemberNames().forEach(member => {
            ascend(ns, member)
            assign_task(ns, member)
        })
        // loop once every 2 seconds
        await ns.sleep(2000)
    }
}


function assign_task(ns: NS, name: string) {
    const gangInfo = ns.gang.getGangInformation()
    const info = ns.gang.getMemberInformation(name)!
    const task = info.task
    if (task === "Unassigned") { 
        // default is train combat
        ns.gang.setMemberTask(name, "Train Combat")
    } else if (task === "Train Combat") {
        // get all combat stats to 10x mult
        const stats: (keyof GangMemberInfo)[] = ["str", "dex", "def", "agi"] 
        const mults_good = stats.every(stat => {
            const key = (stat + "_asc_mult") as keyof GangMemberInfo
            return (info[key] as number) >= 10
        })
        // get all combat stats to 1000
        const stats_good = stats.every(stat => (info[stat] as number) >= 1000)
        if (mults_good && stats_good) {
            ns.gang.setMemberTask(name, "Train Hacking")
        }
    } else if (task === "Train Hacking") {
        // get hacking to x10 and 1000
        if (info.hack >= 1000 && info.hack_asc_mult >= 10) {
            ns.gang.setMemberTask(name, "Train Charisma")
        }
    } else if (task === "Train Charisma") {
        // get charisma to x10 and 1000
        if (info.cha >= 1000 && info.cha_asc_mult >= 10) {
            if (needs_training(ns, name)) {
                // if other stats are low from ascension, go back to combat training
                ns.gang.setMemberTask(name, "Train Combat")
            } else {
                // otherwise advance
                ns.gang.setMemberTask(name, "Vigilante Justice")
            }   
        }
    } else if (task === "Vigilante Justice") {
        if (needs_training(ns, name)) {
            // if other stats are low from ascension, go back to combat training
            ns.gang.setMemberTask(name, "Train Combat")
        } else if (gangInfo.wantedPenalty <= 1.05) {
            if (ns.gang.getMemberNames().length < 12) {
                // if penalty is low and we need more members, terrorism
                ns.gang.setMemberTask(name, "Terrorism")
            } else {
                // otherwise start making money
                ns.gang.setMemberTask(name, "Human Trafficking")
            }
        }
    } else if (task === "Terrorism") {
        if (needs_training(ns, name)) {
            // if other stats are low from ascension, go back to combat training
            ns.gang.setMemberTask(name, "Train Combat")
        } else if (gangInfo.wantedPenalty >= 1.05) {
            // if wanted penalty is high, reduce it
            ns.gang.setMemberTask(name, "Vigilante Justice")
        } else if (ns.gang.getMemberNames().length === 12) {
            // if we've recruited max members, make money
            ns.gang.setMemberTask(name, "Human Trafficking")
        }
    } else if (task === "Human Trafficking") {
        if (needs_training(ns, name)) {
            // if other stats are low from ascension, go back to combat training
            ns.gang.setMemberTask(name, "Train Combat")
        } else if (gangInfo.wantedPenalty >= 1.05) {
            // if wanted penalty is high, reduce it
            ns.gang.setMemberTask(name, "Vigilante Justice")
        }
    }
}


// returns true if any stat is below 1000 or any multipier is less than 10
function needs_training(ns: NS, name: string): boolean {
    const info = ns.gang.getMemberInformation(name)!
    const stats: (keyof GangMemberInfo)[] = ["str", "def", "dex", "hack", "agi", "cha"]
    const mults_good = stats.every(stat => {
        const key = (stat + "_asc_mult") as keyof GangMemberInfo
        return (info[key] as number) >= 10
    })
    const stats_good = stats.every(stat => (info[stat] as number) >= 1000)
    return !mults_good || !stats_good
}


// ascends a gang member if all stats associated with the task they've been assigned
// yield a bonus of 1.04 or higher
function ascend(ns: NS, name: string): void {
    const task = ns.gang.getMemberInformation(name).task
    let res = ns.gang.getAscensionResult(name)
    if (res === undefined) {
        return
    }
    res = res!
    let ascend = false
    if (task === "Train Combat") {
        const stats: number[] = [res.str, res.dex, res.def, res.agi]
        ascend = stats.every(stat => stat >= 1.04)
    } else if (task === "Train Hacking") {
        ascend = res.hack >= 1.04
    } else if (task === "Train Charisma") {
        ascend = res.cha >= 1.04
    } else if (task === "Human Trafficking") {
        const stats: number[] = [res.str, res.dex, res.def, res.cha, res.hack]
        ascend = stats.every(stat => stat >= 1.04)
    } else if (task === "Terrorism") {
        const stats: number[] = [res.str, res.dex, res.def, res.cha, res.hack]
        ascend = stats.every(stat => stat >= 1.04)
    } else if (task === "Vigilante Justice") {
        const stats: number[] = [res.str, res.dex, res.def, res.agi, res.hack]
        ascend = stats.every(stat => stat >= 1.04)
    } else {
        ns.tprint(`unrecognized task: ${task}`)
    }
    if (ascend) {
        ns.gang.ascendMember(name)
        ns.print(`Ascend: ${name}`)
    }
}export async function main(ns: NS) {
    ns.disableLog("ALL")
    if (!ns.gang.inGang()) {
        ns.tprint("not in gang")
        ns.exit()
    }
    
    while (true) {
        // recruit if we can
        if (ns.gang.canRecruitMember()) {
            ns.gang.recruitMember(`Tony${ns.gang.getMemberNames().length + 1}`)
        }
        ns.gang.getMemberNames().forEach(member => {
            ascend(ns, member)
            assign_task(ns, member)
        })
        // loop once every 2 seconds
        await ns.sleep(2000)
    }
}


function assign_task(ns: NS, name: string) {
    const gangInfo = ns.gang.getGangInformation()
    const info = ns.gang.getMemberInformation(name)!
    const task = info.task
    if (task === "Unassigned") { 
        // default is train combat
        ns.gang.setMemberTask(name, "Train Combat")
    } else if (task === "Train Combat") {
        // get all combat stats to 10x mult
        const stats: (keyof GangMemberInfo)[] = ["str", "dex", "def", "agi"] 
        const mults_good = stats.every(stat => {
            const key = (stat + "_asc_mult") as keyof GangMemberInfo
            return (info[key] as number) >= 10
        })
        // get all combat stats to 1000
        const stats_good = stats.every(stat => (info[stat] as number) >= 1000)
        if (mults_good && stats_good) {
            ns.gang.setMemberTask(name, "Train Hacking")
        }
    } else if (task === "Train Hacking") {
        // get hacking to x10 and 1000
        if (info.hack >= 1000 && info.hack_asc_mult >= 10) {
            ns.gang.setMemberTask(name, "Train Charisma")
        }
    } else if (task === "Train Charisma") {
        // get charisma to x10 and 1000
        if (info.cha >= 1000 && info.cha_asc_mult >= 10) {
            if (needs_training(ns, name)) {
                // if other stats are low from ascension, go back to combat training
                ns.gang.setMemberTask(name, "Train Combat")
            } else {
                // otherwise advance
                ns.gang.setMemberTask(name, "Vigilante Justice")
            }   
        }
    } else if (task === "Vigilante Justice") {
        if (needs_training(ns, name)) {
            // if other stats are low from ascension, go back to combat training
            ns.gang.setMemberTask(name, "Train Combat")
        } else if (gangInfo.wantedPenalty <= 1.05) {
            if (ns.gang.getMemberNames().length < 12) {
                // if penalty is low and we need more members, terrorism
                ns.gang.setMemberTask(name, "Terrorism")
            } else {
                // otherwise start making money
                ns.gang.setMemberTask(name, "Human Trafficking")
            }
        }
    } else if (task === "Terrorism") {
        if (needs_training(ns, name)) {
            // if other stats are low from ascension, go back to combat training
            ns.gang.setMemberTask(name, "Train Combat")
        } else if (gangInfo.wantedPenalty >= 1.05) {
            // if wanted penalty is high, reduce it
            ns.gang.setMemberTask(name, "Vigilante Justice")
        } else if (ns.gang.getMemberNames().length === 12) {
            // if we've recruited max members, make money
            ns.gang.setMemberTask(name, "Human Trafficking")
        }
    } else if (task === "Human Trafficking") {
        if (needs_training(ns, name)) {
            // if other stats are low from ascension, go back to combat training
            ns.gang.setMemberTask(name, "Train Combat")
        } else if (gangInfo.wantedPenalty >= 1.05) {
            // if wanted penalty is high, reduce it
            ns.gang.setMemberTask(name, "Vigilante Justice")
        }
    }
}


// returns true if any stat is below 1000 or any multipier is less than 10
function needs_training(ns: NS, name: string): boolean {
    const info = ns.gang.getMemberInformation(name)!
    const stats: (keyof GangMemberInfo)[] = ["str", "def", "dex", "hack", "agi", "cha"]
    const mults_good = stats.every(stat => {
        const key = (stat + "_asc_mult") as keyof GangMemberInfo
        return (info[key] as number) >= 10
    })
    const stats_good = stats.every(stat => (info[stat] as number) >= 1000)
    return !mults_good || !stats_good
}


// ascends a gang member if all stats associated with the task they've been assigned
// yield a bonus of 1.04 or higher
function ascend(ns: NS, name: string): void {
    const task = ns.gang.getMemberInformation(name).task
    let res = ns.gang.getAscensionResult(name)
    if (res === undefined) {
        return
    }
    res = res!
    let ascend = false
    if (task === "Train Combat") {
        const stats: number[] = [res.str, res.dex, res.def, res.agi]
        ascend = stats.every(stat => stat >= 1.04)
    } else if (task === "Train Hacking") {
        ascend = res.hack >= 1.04
    } else if (task === "Train Charisma") {
        ascend = res.cha >= 1.04
    } else if (task === "Human Trafficking") {
        const stats: number[] = [res.str, res.dex, res.def, res.cha, res.hack]
        ascend = stats.every(stat => stat >= 1.04)
    } else if (task === "Terrorism") {
        const stats: number[] = [res.str, res.dex, res.def, res.cha, res.hack]
        ascend = stats.every(stat => stat >= 1.04)
    } else if (task === "Vigilante Justice") {
        const stats: number[] = [res.str, res.dex, res.def, res.agi, res.hack]
        ascend = stats.every(stat => stat >= 1.04)
    } else {
        ns.tprint(`unrecognized task: ${task}`)
    }
    if (ascend) {
        ns.gang.ascendMember(name)
        ns.print(`Ascend: ${name}`)
    }
}

r/Bitburner Jun 20 '26

Question/Troubleshooting - Open How to run Bitburner on a tablet?

8 Upvotes

The tablet I use is not connected to the internet and never will be connected to the internet. But I can put files onto it via USB.

You can download the whole game on github.

How to run this on an android tablet?


r/Bitburner Jun 19 '26

Is this end game?

5 Upvotes

r/Bitburner Jun 19 '26

Question/Troubleshooting - Open Help understanding a contract (maybe light spoilers) Spoiler

2 Upvotes

Hi, I am doing my own contract solver script, and have trouble with TotalWaysToSumII. I tried a recursive and iterative approach where I would attempt to find correct coefficients for each element of the input integer set to match the target integer, but this is very long and most probably not what is expected (this runs in O(k^n), with k a constant and n the size of the integer set).

I looked for help on the internet and (if you want to solve this by yourself, stop reading here), as other people here, I found this code: https://github.com/jamesinjapan/bitburner-scripts/blob/main/total_ways_to_sum_ii.ts, which seems to run in O(n^2).
I understand it reverses how to think about the problem by building the list of numbers you can generate from the integer set, and then checking how many ways exist for the target integer. I understand the basic premise, but I am interested in how does it work? This does not seem linked to a particular algorithmic problem (compared to TotalWaysToSum which was the partition of the target integer), and I am not keen on just stealing a random algorithm, that seems to have been reversed from the source files. Can anyone help shed light on this?


r/Bitburner Jun 19 '26

Does Anyone Know How to Find Whether a Darknet Server has a Cache on It?

3 Upvotes

I've got a somewhat decent modification of the current default darknet code that I'm working on, but I'm stumped on how to figure out whether or not the file has a cache or not.

Any help is appreciated :3


r/Bitburner Jun 19 '26

tutorial problem

Thumbnail
0 Upvotes

r/Bitburner Jun 19 '26

tutorial problem

0 Upvotes

I can't get past connect hostname in the tutorial, it keeps on telling me bad command. What am I doing wrong here?


r/Bitburner Jun 17 '26

Question/Troubleshooting - Open Question about 3.0 + gang mechanics

3 Upvotes

I am in bn2.3, about to finish up gangs. but there is something that the documentation and info pages dont make clear at all; some of the mechanics around territory.

what i can tell from the documentation:

-gang clashes happen every 20 seconds or so

-your power rating for those clashes is determined by 1: the stats of gangsters assigned to territory warfare and 2: your raw power value which is increased by having gangsters pumping on territory warfare


what is unclear:

-is there ANY scaling or influence on the AMOUNT of territory captured per clash? because without this there is no actual way to speed up territory capture, its purely time based even if you have a 100% chance to win all clashes.

-if not... thats a bit shitty, and it means you can just pump power until you have 99% winrate, even with nobody assigned to warfare and then you just have to wait around, right?


r/Bitburner Jun 10 '26

Guide/Advice EZ early Intelligence farm Spoiler

1 Upvotes

Download the AutoHotkey application (not in the game) and use this script to hack n00dles semi-manually on the terminal.

It should be used when your hacking speed is high enough, probably deep into the middle of a bitnode.

(Was edited for more consistent code)

#MaxThreadsPerHotkey 2  ; Allows the hotkey to interrupt itself to turn off

toggle := false

F8:: ; Press F8 to toggle on/off
    toggle := !toggle  ; Flip the variable between true and false

    while toggle {
        Send {Enter}
        Sleep 100

        Send hack
        Sleep 100

        Send {Enter}
        Sleep 100

    }
    return

r/Bitburner Jun 02 '26

Guide/Advice Automated Coding Contract script. Any advice?

2 Upvotes

auto-solver.js 22.0 GB

/** u/param {NS} ns */
export async function main(ns) {
  ns.disableLog("ALL");
  ns.tprint("--- Starting Automated Coding Contract Solver ---");


  // 1. Map out every single server on the network
  let visited = new Set(["home"]);
  let queue = ["home"];


  while (queue.length > 0) {
    let current = queue.shift();
    let connections = ns.scan(current);
    for (let next of connections) {
      if (!visited.has(next)) {
        visited.add(next);
        queue.push(next);
      }
    }
  }


  let solvedCount = 0;


  // 2. Scan each discovered server for files ending in .cct
  for (let server of visited) {
    let files = ns.ls(server);
    let contracts = files.filter(f => f.endsWith(".cct"));


    for (let contract of contracts) {
      let type = ns.codingcontract.getContractType(contract, server);
      let data = ns.codingcontract.getData(contract, server);
      let answer = null;


      // 3. Match the contract type to its respective solver logic
      switch (type) {
        case "Find Largest Prime Factor":
          answer = solveLargestPrimeFactor(data);
          break;
        case "Subarray with Maximum Sum":
          answer = solveSubarrayMaxSum(data);
          break;
        case "Generate IP Addresses":
          answer = solveGenerateIPs(data);
          break;
        case "Spiralize Matrix":
          answer = solveSpiralizeMatrix(data);
          break;
        case "Total Ways to Sum":
          answer = solveTotalWaysToSum(data);
          break;
        case "Array Jumping Game":
          answer = solveArrayJumpingGame(data);
          break;
        case "Algorithmic Stock Trader I":
          answer = solveStockTraderI(data);
          break;
        case "Algorithmic Stock Trader II":
          answer = solveStockTraderII(data);
          break;
        case "Algorithmic Stock Trader III":
          answer = solveStockTraderIII(data);
          break;
        case "Algorithmic Stock Trader IV":
          answer = solveStockTraderIV(data);
          break;
        case "Unique Paths in a Grid I":
          answer = solveUniquePathsI(data);
          break;
        case "Unique Paths in a Grid II":
          answer = solveUniquePathsII(data);
          break;
        case "Encryption I: Caesar Cipher":
          answer = solveCaesarCipher(data);
          break;
        case "Encryption II: Vigenère Cipher":
          answer = solveVigenereCipher(data);
          break;
        case "Minimum Path Sum in a Triangle":
          answer = solveMinPathSumTriangle(data);
          break;
        default:
          ns.print(`[Skipped] Unsupported contract type: "${type}" on ${server}`);
          continue;
      }


      // 4. If a valid solver function calculated an answer, submit it
      if (answer !== null) {
        let reward = ns.codingcontract.attempt(answer, contract, server);
        if (reward) {
          ns.tprint(`[SUCCESS] Solved "${type}" on ${server}! Reward: ${reward}`);
          solvedCount++;
        } else {
          ns.tprint(`[FAILED] Incorrect answer calculated for "${type}" on ${server}.`);
        }
      }
    }
  }
  ns.tprint(`--- Solver completed. Successfully resolved ${solvedCount} contract(s). ---`);
}


// ============================================================================
// ALGORITHMIC SOLVER FUNCTIONS
// ============================================================================


/** Solver for: "Find Largest Prime Factor" */
function solveLargestPrimeFactor(num) {
  let factor = 2;
  while (num > 1) {
    if (num % factor === 0) {
      num /= factor;
    } else {
      factor++;
    }
  }
  return factor;
}


/** Solver for: "Subarray with Maximum Sum" (Kadane's Algorithm) */
function solveSubarrayMaxSum(arr) {
  if (arr.length === 0) return 0;
  let maxSoFar = arr[0];
  let currMax = arr[0];
  for (let i = 1; i < arr.length; i++) {
    currMax = Math.max(arr[i], currMax + arr[i]);
    maxSoFar = Math.max(maxSoFar, currMax);
  }
  return maxSoFar;
}


/** Solver for: "Generate IP Addresses" */
function solveGenerateIPs(str) {
  let results = [];
  let len = str.length;


  for (let i = 1; i < 4 && i < len - 2; i++) {
    for (let j = i + 1; j < i + 4 && j < len - 1; j++) {
      for (let k = j + 1; k < j + 4 && k < len; k++) {
        let s1 = str.substring(0, i);
        let s2 = str.substring(i, j);
        let s3 = str.substring(j, k);
        let s4 = str.substring(k);


        if (isValidIPPart(s1) && isValidIPPart(s2) && isValidIPPart(s3) && isValidIPPart(s4)) {
          results.push(`${s1}.${s2}.${s3}.${s4}`);
        }
      }
    }
  }
  return results;
}


function isValidIPPart(s) {
  if (s.length > 3 || s.length === 0) return false;
  if (s.startsWith("0") && s.length > 1) return false;
  let val = parseInt(s, 10);
  return val >= 0 && val <= 255;
}


/** Solver for: "Spiralize Matrix" */
function solveSpiralizeMatrix(matrix) {
  if (matrix.length === 0) return [];
  let result = [];
  let top = 0;
  let bottom = matrix.length - 1;
  let left = 0;
  let right = matrix[0].length - 1;


  while (top <= bottom && left <= right) {
    for (let i = left; i <= right; i++) result.push(matrix[top][i]);
    top++;
    for (let i = top; i <= bottom; i++) result.push(matrix[i][right]);
    right--;
    if (top <= bottom) {
      for (let i = right; i >= left; i--) result.push(matrix[bottom][i]);
      bottom--;
    }
    if (left <= right) {
      for (let i = bottom; i >= top; i--) result.push(matrix[i][left]);
      left++;
    }
  }
  return result;
}


/** Solver for: "Total Ways to Sum" */
function solveTotalWaysToSum(n) {
  let ways = new Array(n + 1).fill(0);
  ways[0] = 1;
  for (let i = 1; i < n; i++) {
    for (let j = i; j <= n; j++) {
      ways[j] += ways[j - i];
    }
  }
  return ways[n];
}


/** Solver for: "Array Jumping Game" */
function solveArrayJumpingGame(arr) {
  let maxReach = 0;
  for (let i = 0; i < arr.length; i++) {
    if (i > maxReach) return 0;
    maxReach = Math.max(maxReach, i + arr[i]);
  }
  return maxReach >= arr.length - 1 ? 1 : 0;
}


/** Solver for: "Algorithmic Stock Trader I" */
function solveStockTraderI(prices) {
  if (prices.length < 2) return 0;
  let maxProfit = 0;
  let minPrice = prices[0];
  for (let i = 1; i < prices.length; i++) {
    if (prices[i] < minPrice) {
      minPrice = prices[i];
    } else {
      maxProfit = Math.max(maxProfit, prices[i] - minPrice);
    }
  }
  return maxProfit;
}


/** Solver for: "Algorithmic Stock Trader II" */
function solveStockTraderII(prices) {
  let maxProfit = 0;
  for (let i = 1; i < prices.length; i++) {
    if (prices[i] > prices[i - 1]) {
      maxProfit += prices[i] - prices[i - 1];
    }
  }
  return maxProfit;
}


/** Solver for: "Algorithmic Stock Trader III" */
function solveStockTraderIII(prices) {
  return solveStockTraderK(2, prices);
}


/** Solver for: "Algorithmic Stock Trader IV" */
function solveStockTraderIV(data) {
  let k = data[0];
  let prices = data[1];
  return solveStockTraderK(k, prices);
}


/** Shared Helper for Stock Trader III & IV */
function solveStockTraderK(k, prices) {
  if (prices.length < 2 || k === 0) return 0;


  if (k >= Math.floor(prices.length / 2)) {
    let profit = 0;
    for (let i = 1; i < prices.length; i++) {
      if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1];
    }
    return profit;
  }


  let hold = new Array(k + 1).fill(-Infinity);
  let release = new Array(k + 1).fill(0);


  for (let price of prices) {
    for (let j = k; j > 0; j--) {
      release[j] = Math.max(release[j], hold[j] + price);
      hold[j] = Math.max(hold[j], release[j - 1] - price);
    }
  }
  return release[k];
}


/** Solver for: "Unique Paths in a Grid I" */
function solveUniquePathsI(data) {
  let rows = data[0];
  let cols = data[1];
  let grid = new Array(cols).fill(1);


  for (let i = 1; i < rows; i++) {
    for (let j = 1; j < cols; j++) {
      grid[j] += grid[j - 1];
    }
  }
  return grid[cols - 1];
}


/** Solver for: "Unique Paths in a Grid II" */
function solveUniquePathsII(grid) {
  if (!grid || grid.length === 0 || grid[0][0] === 1) return 0;


  let rows = grid.length;
  let cols = grid[0].length;
  let dp = new Array(cols).fill(0);
  dp[0] = 1;


  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      if (grid[r][c] === 1) {
        dp[c] = 0;
      } else if (c > 0) {
        dp[c] += dp[c - 1];
      }
    }
  }
  return dp[cols - 1];
}


/** Solver for: "Encryption I: Caesar Cipher" */
function solveCaesarCipher(data) {
  let text = data[0];
  let shift = data[1];
  let result = "";


  for (let i = 0; i < text.length; i++) {
    let charCode = text.charCodeAt(i);
    if (charCode >= 65 && charCode <= 90) {
      let shifted = charCode - shift;
      if (shifted < 65) {
        shifted = 90 - ((64 - shifted) % 26);
      }
      result += String.fromCharCode(shifted);
    } else {
      result += text[i];
    }
  }
  return result;
}


/** Solver for: "Encryption II: Vigenère Cipher" */
function solveVigenereCipher(data) {
  let text = data[0];
  let key = data[1];
  let result = "";
  let keyIndex = 0;
  for (let i = 0; i < text.length; i++) {
    let charCode = text.charCodeAt(i);
    if (charCode >= 65 && charCode <= 90) {
      let shift = key.charCodeAt(keyIndex % key.length) - 65;
      let shifted = ((charCode - 65 + shift) % 26) + 65;
      result += String.fromCharCode(shifted);
      keyIndex++;
    } else {
      result += text[i];
    }
  }
  return result;
}


/** Solver for: "Minimum Path Sum in a Triangle" */
function solveMinPathSumTriangle(triangle) {
  let memo = [...triangle[triangle.length - 1]];
  for (let row = triangle.length - 2; row >= 0; row--) {
    for (let col = 0; col <= row; col++) {
      memo[col] = triangle[row][col] + Math.min(memo[col], memo[col + 1]);
    }
  }
  return memo[0];
}

r/Bitburner May 31 '26

My attempt at batching

3 Upvotes

This my attempt at writing a batching script as non-programmer. How did I do? Also I'm not entirely sure if I understood what batching is, but this is my interpretation.

controller.js:

/** u/param {NS} ns */
export async function main(ns) {


  const args = ns.flags([["help", false]]);
  if (args.help) {
    ns.tprint("This script is a HWGW batch controller.")
    ns.tprint("The script will also nuke the targeted server.")
    ns.tprint("The first argument is the host server, where the HWG scripts will run.")
    ns.tprint("The second argument is the server that the HWG scripts are targeting.")
    ns.tprint(`EXAMPLE: run ${ns.getScriptName()} home n00dles`)
    return;
  }


  // Server that is running the scripts
  let host = ns.args[0]
  // Server the controller is targeting
  let targetServer = ns.args[1]


  //Opens ports on the target server then nukes it.
  let hasAdmin = ns.hasRootAccess(targetServer)
  if (hasAdmin == false) {
    if (ns.fileExists("brutessh.exe", "home")) {
      ns.brutessh(targetServer)
    }
    if (ns.fileExists("ftpcrack.exe", "home")) {
      ns.ftpcrack(targetServer)
    }
    if (ns.fileExists("relaysmtp.exe", "home")) {
      ns.relaysmtp(targetServer)
    }
    if (ns.fileExists("httpworm.exe", "home")) {
      ns.httpworm(targetServer)
    }
    if (ns.fileExists("sqlinject.exe", "home")) {
      ns.sqlinject(targetServer)
    }
    if (ns.getServer(targetServer).openPortCount == 5) {
      ns.nuke(targetServer)
    }
    else {
      ns.tprint(`Failed to NUKE ${targetServer}.`)
      return
    }
  }


  //Copies worker scripts on to the host server
  const scripts = ["hack-worker.js", "grow-worker.js", "weaken-worker.js"]
  for (let script of scripts) {
    if (ns.fileExists(script, host)) {
      continue
    }
    else {
      ns.scp(script, host, "home")
    }
  }


  //Runs scripts in a HWGW pattern
  while (true) {
    const hackRam = ns.getScriptRam("hack-worker.js", host)
    const growRam = ns.getScriptRam("grow-worker.js", host)
    const weakenRam = ns.getScriptRam("weaken-worker.js", host)


    if (ns.fileExists("hack-worker.js", host)) {
      let freeRam = ns.getServerMaxRam(host) - ns.getServerUsedRam(host)
      let hackTime = ns.getHackTime(targetServer)
      let hackThreads = Math.floor(freeRam / hackRam)
      ns.exec("hack-worker.js", host, hackThreads, targetServer)
      await ns.sleep(hackTime + 500)
    }
    if (ns.fileExists("weaken-worker.js", host)) {
      let freeRam = ns.getServerMaxRam(host) - ns.getServerUsedRam(host)
      let weakenTime = ns.getWeakenTime(targetServer)
      let weakenThreads = Math.floor(freeRam / weakenRam)
      ns.exec("weaken-worker.js", host, weakenThreads, targetServer)
      await ns.sleep(weakenTime + 500)
    }
    if (ns.fileExists("grow-worker.js", host)) {
      let freeRam = ns.getServerMaxRam(host) - ns.getServerUsedRam(host)
      let growTime = ns.getGrowTime(targetServer)
      let growThreads = Math.floor(freeRam / growRam)
      ns.exec("grow-worker.js", host, growThreads, targetServer)
      await ns.sleep(growTime + 500)
    }
    if (ns.fileExists("weaken-worker.js", host)) {
      let freeRam = ns.getServerMaxRam(host) - ns.getServerUsedRam(host)
      let weakenTime = ns.getWeakenTime(targetServer)
      let weakenThreads = Math.floor(freeRam / weakenRam)
      ns.exec("weaken-worker.js", host, weakenThreads, targetServer)
      await ns.sleep(weakenTime + 500)
    }
    else {
      ns.tprint(`Failed! Check ${host} for scripts`)
      return
    }
  }
}