Bitburner

Bitburner

213 ratings
Early game progression guide (Spoiler free)
By Kaladin Stormblessed
Finished the tutorial and wondering how to progress? Want to improve your hacking script or maybe you want to start automating other parts of the game?

This is a spoiler free guide that points you in the right direction.
7
2
9
2
2
   
Award
Favorite
Favorited
Unfavorite
Hacking scripts: How to improve
1: Follow the tutorial

In addition the the ingame tutorial, there's a good guide on how to get a decent hacking script started in the online documentation[bitburner.readthedocs.io].

At this point you have a script that will continuously weaken, grow and hack a given server. You might think it's slow (and you'd be right) and that's because there are several improvements you can make.

The first step is to start using netscript2[bitburner.readthedocs.io]. Netscript 1, or .script files which are used in the tutorial, are slow and low on features. Netscript2 is very close to modern javascript, and their speed is only limited by your computer. Note that speed here refers to the execution time, not how fast your weaken() grow() and hack() functions run - that is determined by hacking skill.

When writing scripts you might notice that their ram requirements increase as you use more ns functions. Since only weaken() grow() and hack() benefit from using additional threads, separating them from everything else will let you run more threads and thus gain a higher payoff.

At this point it's worth having the API documentation[bitburner.readthedocs.io] handy.

Separating our hacking scripts to optimize ram usage
To let us run our important functions with as many threads as possible, figure out how to make the simplest possible script that will run only weaken() on a target given by argument and nothing more. Then take your hacking script from the tutorial, and instead of running weaken() directly, find a way to run the weaken script and passing the target to it as an argument, using as many threads as free ram allows. Then, find a way to wait until it finishes. Repeat this process for grow() and hack().

Because we now only use a small fraction of our RAM on control with 1 thread, we can spend the entire rest of our RAM on running the functions that actually care about threads, with as many threads as possible. This can more than double your income.

Centralizing control
If you deploy this hacking script to each server you have root on, you should be progressing quite well. But if we analyze ram usage on our rooted servers, we see that the actual weaken.ns, grow.ns and hack.ns scripts don't use all the available ram because the control script takes up quite a chunk. But if we are just attacking the most profitable target with all of these servers of ours anyway, why not stick to a single control script on one server and let it control the others?

The goal here is to write a control script that we run on our home computer, which can spawn weaken.ns grow.ns and hack.ns on other servers - leaving us with all their memory free for doing important jobs. If the control script takes approximately twice the ram of a single thread of weaken.ns, grow.ns or hack.ns, we get 2 additional threads on each of our rooted servers for free by using this technique. In the early game when most of your ram comes from random servers you've rooted, this can count for quite an increase.

Simultaneous execution
In general for optimizations, it's important to measure what parts of our algorithms has the longest impact on performance. Let's assume we've gotten to the point where a server is prepped (min security and max money reached) so that our loop is approximately weaken -> grow -> weaken -> hack.

Start by printing to log the time spent on each step. In my case, the obvious time sink was the weaken directly after grow, because the grow increased security significantly. What if we could start weakening while the grow is in progress, thus doing the tasks in parallel? We actually can:

The time taken by weaken(), grow() and hack() is calculated by the server and player status at the start of the operation. The effect happens at the end. Thus, if we start our weaken before the grow finishes, it will be way faster. Figure out a way to see how long weaken() and grow() will take (hint: the documentation[github.com] has functions for this). Then instead of using all available memory for starting grow, use half. Then start weaken with the other half. If weaken would run faster, you need to sleep your script a bit before you run weaken.

Note that the optimal split is likely far from 50/50. You can either experiment with the ratio, or use the analyze functions from the documentation[github.com] to calculate the optimal split so that the weaken has just enough power to counteract the effect of the grow.

Further optimization
grow() grows the server by a certain % of it's current money. Ideally, we want to make sure that a single grow() will bring it back up to full. Find a way to calculate how many threads of hack and grow you need to keep the cycle short.

The last and hardest optimization to consider is to batch an entire cycle and launch them simultaneously, so that hack() grow() and weaken() all hit within a short time of eachoter. If you manage this, you can take great advantage of larger servers in the later game, because a single full-threaded hack will remove all money, leaving you a long time to rebuild it's stash. If you instead queue several batches of scripts that all hit after one another and steal a smaller % of the money, you gain a lot more money over time per GB ram than otherwise.

This last optimization however is a non trivial problem, and requires extensive planning, analysis and refactoring to get right.
Finding and cracking servers: Get more ram for attacking or a juicier target
If you'we followed the tutorial, you have a script that will check a few hardcoded servers and crack them. Instead of relying on hardcoded lists, make a script that runs scan()[github.com] and does a breadth-first-search[en.wikipedia.org] to generate a list of every server in the game. Then you can loop continuously (say once every ten seconds) with sleep()[github.com], and loop through the list, running any available port openers and NUKE.exe if you can - there are functions to check if you have enough ports[github.com] and hacking skill[github.com].

Once a server is cracked, you can decide what to do with it:
  • An easy first step is to ns.tprint()[github.com] the name of the server so you are aware.
  • Another is to copy[github.com] and run[github.com] hacking scripts to it so it can immediately start gaining you money.
  • A third is to make a ranking of all possible hacking targets, sort them by some metric and either print the result, or redirect all your servers to hack the new target if it is better than your old one. A simple heuristic is max money[github.com] divided by min security[github.com]. A more precise one is max money divided by time it would take to hack, grow back and weaken down, functions for which can be found in the documentation[github.com].
Other mechanics
Coming soon: Guides to hacknet, stock market and more.
26 Comments
lie sayer 24 Jan, 2024 @ 4:46am 
thank you steam for the 1000 character limit on posts. I hope this helps you out in the mathematical-scientific way of Figuring Stuff Out!
lie sayer 24 Jan, 2024 @ 4:46am 
here's some non-spoiler-y ways of figuring it out: 1. You want the distance in hacking level to matter close to like, 1 or 2, but it takes more of a gap the further away from 0 you are. 2. You want to put 'positive' values that make hacking better on the top of a division, and 'negative' values that make hacking worse on the bottom. 3. You might want to weed out servers that have hacking level requirements about your own, because those are ALWAYS too hard to hack. this should be part of your function, but it's what it's.
lie sayer 24 Jan, 2024 @ 4:46am 
problems are:
since it doesn't take current values into account, if you 'prep' a server (get it to maxMoney and minSecurity), it won't care, and might choose a server that hasn't been prepped. This makes the function non-universal, as early-game doesn't have the time (nor ram) to properly prep most/all servers with money. You can just hard-code it so that, if your hacking level is below, like, 750, then it returns n00dles (a decent server), as beyond 750 you either have a solution or the resources. part 2
lie sayer 24 Jan, 2024 @ 4:46am 
@tclord
i found a formula that agrees with conventional logic, but it's up to you if you want to use it.
maxMoney / (minSecurity * Math.log2(Math.abs(hackingReq + 2 - playerHacking)))
explanation is:
you start off the difference in hacking levels - hackingReq + 2 - playerHacking. You take the absolute value, and then take the log2 of that - this makes the level difference worth less the further away it is. The log2 is why you add 2. Then, you multiply that value (at most like 17 or smthn) by the server's minSecurity. This number is the divisor for maxMoney, because high level difference || high minSecurity is bad, but high maxMoney is good. Then, finally, that is the server's value. Add it to an array, Math.max(...arr), and you have the best server to hack. part 1
lie sayer 24 Jan, 2024 @ 4:36am 
@elidoran
alternatively, use math and function calls. It straight up tells you how long a grow will last, but the problem is planning. Therefore, spend five billion on Formulas.exe, and use ns.formulas.hacking.growTime(), and simulate the changes done to the server by using a variable instead of ns.getServer().
Jer 17:5-10 11 Nov, 2023 @ 10:52am 
This is a good introductory guide. I am tired of guides by people who don't know what they are talking about.
elidoran 27 Jul, 2023 @ 9:31pm 
@Kryptkiller There's more you can do than "await ns.sleep(someTime)". It's possible to use ns.isRunning() to see if a script you started is running still to know when it's done. A more advanced method is to have your controller/launcher script listen on a data port for the other script to send a message when it's done. You can use "const portHandle = ns.getPortHandle(portNum);" to hold onto a controller for a port. Then use "await portHandle.nextWrite();" to wait until a message arrives on that port. The other script would use "ns.writePort(portNum, 'Script #4138h reporting completion, sir.');" to tell the controller who it is and that it's done. (Presumably, it'd send its PID instead, and the controller would remember the PIDs of the scripts it started.)

Definitely go to the games documentation and look through its API. There's a lot there.
elidoran 27 Jul, 2023 @ 9:31pm 
@1prohappyboom @Saki The two deepscan upgrades change the scan-analyze command. Initially, you can do scan-analyze with no argument, or with 1, 2, or 3. The 3 is the max distance to scan. When you buy DeepscanV1.exe you can also use 4 and 5. When you buy DeepscanV2.exe you can then use 6, 7, 8, 9, or 10. And, that's the most. They just increase the distance, the depth, the scan-analyze command goes.

The other things, like the port openers such as BrutesSSH.exe, and the ServerProfiler.exe can be used with the "run" command the same as running a script or coding contract. For example: "run ServerProfiler.exe n00dles". The same as using the Nuke.exe. To make it easier you can start typing their names in all lowercase and press Tab for it to auto-complete the names.
[TASK] Dr Duck 14 Oct, 2022 @ 8:06am 
@Kryptkiller if you're using NS2 you need to await ns.sleep(x) as it's using a javascript feature called a 'promise' that you need to wait for them to fulfil
Kryptkiller 8 Oct, 2022 @ 12:42am 
I'm having trouble at the:

"Then, find a way to wait until it finishes. Repeat this process for grow() and hack()."

I can't find a way to 'wait'. I thought the await or ns.sleep commands were it, but they just crash the game for me.

Any advice?