mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-20 16:22:56 +02:00
DOC: Move all docs into en/ subdirectory (#1505)
* DOC: Move all docs into en/ subdirectory PR #1502 is working on adding a Chinese translation to the docs. In general, I encouraged this (in #1452) as a path towards getting useful translated content in the game without requiring a massive refactor/rearchitecting of everything. To support this, this takes the first step of moving our docs into an en/ subdirectory, so that other languages can live alongside. No effort is made at this time to support or select between alternate languages; this is a pure-rename refactor.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# Game Frozen or Stuck?
|
||||
|
||||
## Infinite Loop in Scripts
|
||||
|
||||
If your game is frozen or stuck in any way, then the most likely culprit is an infinitely running loop in your script.
|
||||
To get past the freezing, run the game with `?noScripts` in the URL:
|
||||
|
||||
[Link to no freeze](https://bitburner-official.github.io?noScripts)
|
||||
|
||||
Then, to fix your script, make sure you have a `sleep()` or any other timed function like `hack()` or `grow()` in any infinite loops:
|
||||
|
||||
while(true) {
|
||||
// This is an infinite loop that does something
|
||||
...
|
||||
await ns.sleep(1000); // Add a 1s sleep to prevent freezing
|
||||
}
|
||||
|
||||
Also make sure that each while loop gets to the `await`ed function or `break`, for example the next snippet has a `sleep()` function, but it nor any possible conditional breaks are never reached and therefore will crash the game:
|
||||
|
||||
while(true) {
|
||||
let currentMoney = ns.getServerMoneyAvailable("n00dles");
|
||||
let maxMoney = ns.getServerMaxMoney("n00dles");
|
||||
if (currentMoney < maxMoney/2){
|
||||
await ns.grow("n00dles");
|
||||
}
|
||||
if (currentMoney === maxMoney){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
If `n00dles` current money is, for example, 75% of the maximum money, the script will reach neither `grow()` nor `break` and the game will crash.
|
||||
Adding a sleep like in the first example, or changing the code so that the `awaited` function or `break` is always reached, would prevent the crash.
|
||||
|
||||
Common infinite loop when translating the server purchasing script in starting guide to scripts is to have a while loop, where the condition's change is conditional:
|
||||
|
||||
var ram = 8;
|
||||
var i = 0;
|
||||
|
||||
while (i < ns.getPurchasedServerLimit()) {
|
||||
if (ns.getServerMoneyAvailable("home") > ns.getPurchasedServerCost(ram)) {
|
||||
var hostname = ns.purchaseServer("pserv-" + i, ram);
|
||||
ns.scp("early-hack-template.js", hostname);
|
||||
ns.exec("early-hack-template.js", hostname, 3);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
If the player does not currently have enough money to purchase a server, the `if`'s condition will be false and `++i` will not be reached.
|
||||
Since the script doesn't have `sleep()` and value `i` will not change without the `if` being true, this will crash the game.
|
||||
Adding a `sleep()` that is always reached would prevent the crash.
|
||||
|
||||
## Black screen
|
||||
|
||||
If the game window becomes a black screen without the game itself crashing, this is caused by the game running too many concurrent scripts (the game runs on a browser and each tab can only use so much ram until it crashes).
|
||||
Depending on which scripts are running and your hardware, this number can vary between 50000 to 100000 instances (in version 2.0.2. In prior versions this number was about 1/5th of that).
|
||||
To prevent this from happening make sure to multithread the scripts as much as possible.
|
||||
|
||||
## Bug
|
||||
|
||||
Otherwise, the game is probably frozen/stuck due to a bug.
|
||||
To report a bug, follow the guidelines [here](https://github.com/bitburner-official/bitburner-src/blob/stable/CONTRIBUTING.md#reporting-bugs).
|
||||
@@ -0,0 +1,322 @@
|
||||
## Automating IPvGO
|
||||
|
||||
IPvGO is a strategic territory control minigame accessible from DefComm in New Tokyo, or the CIA in Sector-12. Form networks of routers on a grid to control open space and gain stat multipliers and favor, but make sure the opposing faction does not surround and destroy your network!
|
||||
|
||||
For basic instructions, go to DefComm or CIA to access the current subnet, and look through the "How to Play" section. This document is specifically focused on building scripts to automate subnet takeover, which will be more applicable you have played a few subnets.
|
||||
|
||||
For a full list of all IpvGO methods and their descriptions and documentation, you can use the game's [API documentation page](https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.go.md).
|
||||
|
||||
|
||||
|
||||
#### Overview
|
||||
|
||||
The rules of Go, at their heart, are very simple. Because of this, they can be used to make a very simple script to automatically collect node power from IPvGO subnets.
|
||||
|
||||
This script can be iteratively improved upon, gradually giving it more tools and types of move to look for, and becoming more consistent at staking out territory on the current subnet.
|
||||
|
||||
This document starts out with a lot of detail and example code to get you started, but will transition to more high-level algorithm design and pseudocode as it progresses. If you get stuck implementing some of these ideas, feel free to discuss in the [official Discord server](https://discord.gg/TFc3hKD)
|
||||
|
||||
|
||||
|
||||
### Script outline: Starting with the basics
|
||||
|
||||
|
||||
|
||||
#### Testing Validity
|
||||
|
||||
The `ns.go` API provides a number of useful tools to understand the current subnet state.
|
||||
|
||||
`ns.go.analysis.getValidMoves()` returns a 2D array of true/false, indicating if you can place a router on the current square.
|
||||
|
||||
You can test if a given move `x,y` is valid with a test like this:
|
||||
|
||||
```js
|
||||
const validMoves = ns.go.analysis.getValidMoves();
|
||||
|
||||
if (validMoves[x][y] === true) {
|
||||
// Do something
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### Choosing a random move
|
||||
|
||||
The simplest move type, and the fallback if no other move can be found, is to pick a random valid move.
|
||||
|
||||
The `validMoves` grid can be retrieved using `getValidMoves()` as mentioned above. `board` comes from `ns.go.getBoardState()`, more on that later.
|
||||
|
||||
It would be a problem to fill in every single node on the board, however. If networks ever lose contact with any empty nodes, they will be destroyed! So, leave some "airspace" by excluding certain moves from the random ones we select.
|
||||
|
||||
One way to do this is to exclude nodes with both even X coordinate and even y coordinate:
|
||||
|
||||
```js
|
||||
/**
|
||||
* Choose one of the empty points on the board at random to play
|
||||
*/
|
||||
const getRandomMove = (board, validMoves) => {
|
||||
const moveOptions = [];
|
||||
const size = board[0].length;
|
||||
|
||||
// Look through all the points on the board
|
||||
for (let x = 0; x < size; x++) {
|
||||
for (let y = 0; y < size; y++) {
|
||||
// Make sure the point is a valid move
|
||||
const isValidMove = validMoves[x][y] === true;
|
||||
// Leave some spaces to make it harder to capture our pieces.
|
||||
// We don't want to run out of empty node connections!
|
||||
const isNotReservedSpace = x % 2 === 1 || y % 2 === 1;
|
||||
|
||||
if (isValidMove && isNotReservedSpace) {
|
||||
moveOptions.push([x, y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Choose one of the found moves at random
|
||||
const randomIndex = Math.floor(Math.random() * moveOptions.length);
|
||||
return moveOptions[randomIndex] ?? [];
|
||||
};
|
||||
```
|
||||
|
||||
This idea can also be improved to focus on a specific area or corner first, rather than spread across the whole board right away.
|
||||
|
||||
|
||||
|
||||
#### Playing moves
|
||||
|
||||
Now that a simple move type is available, it can be used to play on the current subnet!
|
||||
|
||||
`await ns.go.makeMove(x, y)` can be used to play a chosen move `x,y`. Note that it returns a `Promise`, meaning it needs to be used with `await`.
|
||||
|
||||
`await ns.go.passTurn()` can be used if no moves are found. This will end the game if the AI also passes (or just passed previously).
|
||||
|
||||
Both `makeMove()` and `passTurn()`, when awaited, return an object that tells you what the AI's response is, and if the game is over.
|
||||
|
||||
```js
|
||||
{
|
||||
// If the opponent moved or passed, or if the game is now over.
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null; // Opponent move's x coord (if applicable)
|
||||
y: number | null; // Opponent move's y coord (if applicable)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
When used together with the `getRandomMove()` implemented above, the framework of the script is ready. An example `main()` that implements this is below. Search for a new subnet using the UI, then launch the script you have been working on, and watch it play!
|
||||
|
||||
```js
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns) {
|
||||
let result, x, y;
|
||||
|
||||
do {
|
||||
const board = ns.go.getBoardState();
|
||||
const validMoves = ns.go.analysis.getValidMoves();
|
||||
|
||||
const [randX, randY] = getRandomMove(board, validMoves);
|
||||
// TODO: more move options
|
||||
|
||||
// Choose a move from our options (currently just "random move")
|
||||
x = randX;
|
||||
y = randY;
|
||||
|
||||
if (x === undefined) {
|
||||
// Pass turn if no moves are found
|
||||
result = await ns.go.passTurn();
|
||||
} else {
|
||||
// Play the selected move
|
||||
result = await ns.go.makeMove(x, y);
|
||||
}
|
||||
|
||||
// Log opponent's next move, once it happens
|
||||
await ns.go.opponentNextTurn();
|
||||
|
||||
await ns.sleep(200);
|
||||
|
||||
// Keep looping as long as the opponent is playing moves
|
||||
} while (result?.type !== "gameOver");
|
||||
|
||||
// TODO: add a loop to keep playing
|
||||
// TODO: reset board, e.g. `ns.go.resetBoardState("Netburners", 7)`
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Adding network expansion moves
|
||||
|
||||
Just playing random moves is not very effective, though. The next step is to use the board state to try and take over territory.
|
||||
|
||||
`ns.go.getBoardState()` returns a simple grid representing what the current board looks like. The player's routers are marked with `X`, and the opponents with `O`.
|
||||
|
||||
Example 5x5 board state, with a number of networks for each player:
|
||||
|
||||
```angularjs
|
||||
[ "XX.O.",
|
||||
"X..OO",
|
||||
".XO..",
|
||||
"XXO..",
|
||||
".XOO.", ]
|
||||
```
|
||||
|
||||
The board state can be used to look at all the nodes touching a given point, by looking at an adjacent pair of coordinates.
|
||||
|
||||
For example, the point to the 'north' of the current point `x, y` can be retrieved with `board[x + 1]?.[y]`. If it is a friendly router it will have value `"X"`. (It will be undefined if `x,y` is on the north edge of the subnet)
|
||||
|
||||
That info can be used to make decisions about where to place routers.
|
||||
|
||||
In order to expand the area that is controlled by the player's networks, connecting to friendly routers (when possible) is a strong move. This can be done with a very similar implementation to `getRandomMove()`, with the additional check of looking for a neighboring friendly router. For each point on the board:
|
||||
|
||||
```text
|
||||
Detect expansion moves:
|
||||
For each point on the board:
|
||||
* If the empty point is a valid move, and
|
||||
* If the point is not an open space reserved to protect the network [see getRandomMove()], and
|
||||
* If a point to the north, south, east, or west is a friendly router
|
||||
|
||||
Then, the move will expand an existing network
|
||||
```
|
||||
|
||||
When possible, an expansion move like this should be used over a random move. When neither can be found, pass turn.
|
||||
|
||||
This idea can be improved: reserved spaces can be skipped if the nodes are in different networks. Se `ns.go.analysis.getChains()`
|
||||
|
||||
After implementing this, the script will consistently get points on the subnet against most opponents (at least on the larger boards), and will sometimes even get lucky and win against the easiest factions.
|
||||
|
||||
|
||||
|
||||
#### Next Steps
|
||||
|
||||
There is a lot we can still do to improve the script. For one, it currently only plays one game, and must be restarted each time! Also, it does not re-set the subnet upon game completion yet.
|
||||
|
||||
In addition, the script only knows about a few types of moves, and does not yet know how to capture or defend networks.
|
||||
|
||||
|
||||
|
||||
#### Killing duplicate scripts
|
||||
|
||||
Because there is only one subnet active at any time, you do not want multiple copies of your scripts running and competing with each other. It may be helpful to kill any other scripts with the same name as your IPvGO script on startup. This can be done using `ns.getRunningScript()` to get the script details and `ns.kill()` to remove old copies of the script.
|
||||
|
||||
|
||||
|
||||
### Move option: Capturing the opponent's networks
|
||||
|
||||
If the opposing faction's network is down to its last open port, placing a router in that empty node will capture and destroy that entire network.
|
||||
|
||||
To find out what networks are in danger of capture, `ns.go.analysis.getLiberties()` shows how many empty nodes / open ports each network has. As with `getBoardState()` and `getValidMoves()` , the number of liberties (open ports) for a given point's network can be retrieved via its coordinates `[x][y]` on the grid returned by `getLiberties()`
|
||||
|
||||
```text
|
||||
Detect moves to capture the opponent's routers:
|
||||
For each point on the board:
|
||||
* If the empty point is a valid move, and
|
||||
* If a point to the north, south, east, or west is a router with exactly 1 liberty [via its coordinates in getLiberties()], and
|
||||
* That point is controlled by the opponent [it is a "O" via getBoardState()]
|
||||
|
||||
Then, playing that move will capture the opponent's network.
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Move option: Defending your networks from capture
|
||||
|
||||
`getLiberties()` can also be used to detect your own networks that are in danger of being captured, and look for moves to try and save it.
|
||||
|
||||
```text
|
||||
Detect moves to defend a threatened network:
|
||||
For each point on the board:
|
||||
* If the empty point is a valid move, and
|
||||
* If a point to the north, south, east, or west is a router with exactly 1 liberty [via its coordinates in getLiberties()], and
|
||||
* That point is controlled by the player [it is a "X" via getBoardState()]
|
||||
|
||||
Then, that network is in danger of being captured.
|
||||
|
||||
|
||||
To detect if that network can be saved:
|
||||
|
||||
* Ensure the new move will not immediately allow the opponent to capture:
|
||||
* That empty point ALSO has two or more empty points adjacent to it [a "." via getBoardState()], OR
|
||||
* That empty point has a friendly network adjacent to it, and that network has 3 or more liberties [via getLiberties()]
|
||||
|
||||
Then, playing that move will prevent your network from being captured (at least for a turn or two)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Move option: Smothering the opponent's networks
|
||||
|
||||
In some cases, an opponent's network cannot YET be captured, but by placing routers all around it, the network can be captured on a future move. (Or at least you force the opponent to spend moves defending their network.)
|
||||
|
||||
There are many ways to approach this, but the simplest is to look for any opposing network with the fewest liberties remaining (ideally 2), and find a safe point to place a router that touches it.
|
||||
|
||||
To make sure the move will not immediately get re-captured, make sure the point you play on has two adjacent empty nodes, or is touching a friendly network with three+ liberties. (This is the same as the check in the move to defend a friendly chain.)
|
||||
|
||||
|
||||
|
||||
### Move option: Expanding your networks' connections to empty nodes
|
||||
|
||||
The more empty nodes a network touches, the stronger it is, and the more territory it influences. Thus, placing routers that touch a friendly network and also to as many open nodes as possible is often a strong move.
|
||||
|
||||
This is similar to the logic for defending your networks from immediate capture. Look for a friendly network with the fewest open ports, and find an empty node adjacent to it that touches multiple other empty nodes.
|
||||
|
||||
|
||||
|
||||
### Move option: Encircling space to control empty nodes
|
||||
|
||||
A key part of the strategy of Go is fully encircling groups of empty nodes. The examples at the start of this doc simply leave out specific nodes and hope they stay empty, but this can be done in much better ways.
|
||||
|
||||
As a simple approach, look for possible moves that are:
|
||||
|
||||
- adjacent to two separate empty nodes (open areas it will divide up)
|
||||
- adjacent a friendly piece and the edge of a board (or a second friendly piece from a different chain than the first)
|
||||
|
||||
This will find moves which are connecting your chains together, or connecting to the edge of the board, and dividing up empty space in the process. This allows you to control space, making it harder to capture your chains in the process.
|
||||
|
||||
|
||||
|
||||
### Choosing a good move option
|
||||
|
||||
Having multiple plausible moves to select from is helpful, but choosing the right option is important to making a strong Go script. In some cases, if a move type is available, it is almost always worth playing (such as defending your network from immediate capture, or capturing a vulnerable enemy network)
|
||||
|
||||
Each of the IPvGO factions has a few moves they will almost always choose (The Black hand will always capture if possible, for example). Coming up with a simple prioritized list is a good start to compete with these scripts. Experiment to see what works best!
|
||||
|
||||
This idea can be improved, however, by including information such as the size of the network that is being threatened or that is vulnerable to capture. It is probably worth giving up one router in exchange for capturing a large enemy network, for example. Adding two new open ports to a large network is helpful, but limiting an opponent's network to one open port might be better.
|
||||
|
||||
|
||||
|
||||
### Other types of move options
|
||||
|
||||
**Preparing to invade the opponent**
|
||||
|
||||
Empty areas that are completely surrounded and controlled by a single player can be seen via `ns.go.analysis.getControlledEmptyNodes()`. However, just because the area is currently controlled by the opponent does not mean it cannot be attacked! Start by surrounding an opponent's network from the outside, then it can be captured by attacking the space it surrounds and controls. (Note that this only works on networks that have a single interior empty space: if they have multiple inner empty points, the suicide rule prevents you from filling any of them)
|
||||
|
||||
**Wrapping empty space**
|
||||
|
||||
The starting script uses some very simple logic to leave open empty nodes inside its networks (simply excluding points with `x % 2 === 0 && y % 2 === 0`). However, it is very strong to look for ways to actively surround empty space.
|
||||
|
||||
Look for moves that connect a network to the edge of a board that touch an empty node, or look for moves that connect two networks and touch an empty node. Or, look for a move that touches a friendly network and splits apart a chain of empty nodes.
|
||||
|
||||
**Jumps and Knights' moves**
|
||||
|
||||
The factions currently only look at moves directly connected to friendly or enemy networks in most cases. however, especially on the larger board, playing a router a few spaces away from an existing line/network allows the player to influence more territory, compared to slower moves that connect one adjacent node at a time. Consider skipping a node or two, or playing diagonally, or combining them to make L shaped jumps (like a knight's move in chess)
|
||||
|
||||
**Pattern Matching**
|
||||
|
||||
There are a lot of strong shapes in Go, that are worth attempting to re-create. The factions look for ways to slip diagonally between the players' networks and cut them apart. They also look for ways to wrap around isolated opposing routers. Consider making a small library of strong shapes, then looking to match them on the board (or their rotations or mirrors). The exact shapes will require some research into Go, but there is a lot of good documentation online about this idea.
|
||||
|
||||
**Creating "Eyes"**
|
||||
|
||||
If a single network fully encloses two different disconnected empty nodes, it can never be taken. (If it only had one inner airspace, the opponent could eventually surround and then fill it to capture the network. If there is two, however, the suicide rule prevents them from filling either inner empty space.) Detecting moves that make figure-8 type shapes, or split an encircled empty node chain into two smaller ones, are very strong.
|
||||
|
||||
In addition, if the opponent has only a single such move, playing there first to block it is often extremely disruptive, and can even lead to their network being captured.
|
||||
|
||||
|
||||
|
||||
A deeper dive into this idea will involve making your own code to identify chains of pieces (and continuous empty nodes).
|
||||
|
||||
- Find all moves that divide up empty space and connect two chains or a chain and the edge as in the 'encircling empty space' idea above
|
||||
- Apply the move on a sample board in memory, one at a time
|
||||
- Identify all chains and continuous groups of empty nodes in the resulting board, and which color pieces surround the new empty node groups
|
||||
- Prioritize the move that makes the most empty node groups fully surrounded by your player color.
|
||||
- Alternatively, count how many empty node groups each friendly chain is touching, and prioritize moves that create a second of these "eyes" for friendly chains
|
||||
@@ -0,0 +1,131 @@
|
||||
# Hacking algorithms
|
||||
|
||||
There are three primary families of hacking algorithms.
|
||||
This guide will go over each of them and advise on how they can be implemented.
|
||||
|
||||
## Self-contained algorithms
|
||||
|
||||
**Difficulty**: Easy
|
||||
|
||||
Pros:
|
||||
|
||||
- Easy to implement
|
||||
- Does not require other scripts to work
|
||||
- Works at any stage of the game
|
||||
|
||||
Cons:
|
||||
|
||||
- Limits income generation
|
||||
- Extremely [RAM](../basic/ram.md) inefficient
|
||||
- Utilizes script online time poorly
|
||||
- Risk of over hacking
|
||||
|
||||
Self-contained algorithms are the simplest family of hacking algorithms to implement.
|
||||
Each script is tasked with choosing which function to execute based on the status of the target server.
|
||||
Because of this, they guarantee a consistent, but relatively small, flow of money.
|
||||
|
||||
The general logic goes like this:
|
||||
|
||||
loop forever {
|
||||
if security is not minimum {
|
||||
await ns.weaken(target)
|
||||
} else if money is not maximum {
|
||||
await ns.grow(target)
|
||||
} else {
|
||||
await ns.hack(target)
|
||||
}
|
||||
}
|
||||
|
||||
This algorithm is perfectly capable of paving the way through the majority of the game, but it has a few significant issues.
|
||||
|
||||
- It tends to make all your scripts on every server do the same thing.
|
||||
(e.g. If the target is 0.01 security above the minimum, all scripts will decide to weaken, when only a handful of threads should be devoted to the task)
|
||||
- At higher thread counts, these scripts have the potential to hack the server to \$0, or maximum security, requiring a long setup time while the scripts return the server to the best stats.
|
||||
- Requires function calls such as `getServerSecurityLevel` and `getServerMoneyAvailable`, as well as calling all three hacking functions, increasing RAM cost which is multiplied by the number of allocated threads
|
||||
|
||||
## Loop algorithms
|
||||
|
||||
**Difficulty**: Easy to Medium
|
||||
|
||||
Pros:
|
||||
|
||||
- Simple to understand
|
||||
- Works at any stage of the game
|
||||
- Maximize RAM usage
|
||||
|
||||
Cons:
|
||||
|
||||
- Requires a script that handles deployment
|
||||
|
||||
By splitting our hack, weaken, and grow functions into three separate scripts, we can both remove our reliance on functions such as `getServerSecurityLevel` as well as removing functions that cannot work concurrently, reducing RAM requirements, and thus increasing our thread limits.
|
||||
Loop scripts are formatted like this:
|
||||
|
||||
loop forever {
|
||||
await ns.hack(target) // or grow, or weaken
|
||||
}
|
||||
|
||||
Now we can take the total amount of threads available and split it and allocate, for example:
|
||||
|
||||
- 1 part to the hack scripts
|
||||
- 10 parts to the grow scripts
|
||||
- 2 parts to the weaken scripts
|
||||
|
||||
Meaning if we have space for 100 threads across the entire network 7 threads will go to the hack scripts, 76 threads will go to the grow scripts and 15 threads will go to the weaken scripts.
|
||||
The ratios described here are arbitrary and can be greatly improved through the use of the analyze functions, and later, through the use of Formulas.exe.
|
||||
|
||||
When utilizing this strategy, monitor the amount of money and security on the target server, if the money is not hovering around maximum and the security around the minimum, the ratios should be tweaked until that is the case.
|
||||
|
||||
Utilizing `sleep` or `asleep` to ensure that your scripts do not all start at the same time can decrease the chance of issues associated with overhacking occurring.
|
||||
Both functions have a ram cost of zero.
|
||||
|
||||
## Batch algorithms (HGW, HWGW, or Cycles)
|
||||
|
||||
**Difficulty**: Hard
|
||||
|
||||
Pros:
|
||||
|
||||
- Maximum potential income
|
||||
|
||||
Cons:
|
||||
|
||||
- Very difficult to implement without prior programming knowledge
|
||||
- Very difficult to make work on servers with less than 1TB of RAM
|
||||
|
||||
Batch algorithms utilize a master script that uses `exec` many scripts which utilize a relevant hacking function in batches.
|
||||
|
||||
The scripts used to execute the hacking functions are even simpler than the previous algorithms but a complex controller is required to calculate the effect, time taken, and the necessary delay.
|
||||
|
||||
await ns.sleep(a bit)
|
||||
await ns.hack(target) // or grow, or weaken
|
||||
|
||||
A few things need to be known before this algorithm can be implemented:
|
||||
|
||||
- The effects of hack and grow depend on the server security level, a higher security level results in a reduced effect.
|
||||
You only want these effects to occur when the security level is minimized.
|
||||
- The time taken to execute hack, grow, or weaken is determined when the function is called and is based on the security level of the target server and your hacking level.
|
||||
You only want these effects to start when the security level is minimized.
|
||||
- The effects of hack, grow, and weaken, are determined when the time is completed, rather than at the beginning.
|
||||
Hack should finish when security is minimum and money is maximum.
|
||||
Grow should finish when security is minimum, shortly after a hack occurred.
|
||||
Weaken should occur when security is not at a minimum due to a hack or grow increasing it.
|
||||
|
||||
A single batch consists of four actions:
|
||||
|
||||
1. A hack script removes a predefined, precalculated amount of money from the target server.
|
||||
2. A weaken script counters the security increase of the hack script.
|
||||
3. A grow script counters the money decrease caused by the hack script.
|
||||
4. A weaken script counters the security increase caused by the grow script.
|
||||
|
||||
It is also important that these 4 scripts finish in the order specified above, and all of their effects be precalculated to optimize the ratios between them.
|
||||
This is the reason for the delay in the scripts.
|
||||
|
||||
|= hack ====================|
|
||||
|=weaken 1======================================|
|
||||
|= grow ==========================|
|
||||
|=weaken 2======================================|
|
||||
|
||||
Batches only function predictably when the target server is at minimum security and maximum money, so your script must also handle preparing a server for your batches.
|
||||
You can utilize batches to prepare a server by using no hack threads during preparation.
|
||||
|
||||
Depending on your computer's performance as well as a few other factors, the necessary delay between script execution times may range between 20ms and 200ms, you want to fine-tune this value to be as low as possible while also avoiding your scripts finishing out of order.
|
||||
Anything lower than 20ms will not work due to JavaScript limitations.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Learn to Program in JavaScript
|
||||
|
||||
## For Beginner Programmers
|
||||
|
||||
If you have little to no programming experience, that's okay!
|
||||
You don't need to be a great programmer in order to enjoy or play this game.
|
||||
In fact, this game could help you learn some basic programming concepts.
|
||||
|
||||
Here are some good tutorials for learning programming/JavaScript as a beginner:
|
||||
|
||||
- [Learn-JS](https://www.learn-js.org/en/Welcome)
|
||||
- [programiz](https://www.programiz.com/javascript/get-started)
|
||||
- [Speaking JavaScript](https://exploringjs.com/es5/)
|
||||
This is a bit on the longer side.
|
||||
You can skip all of the historical background stuff.
|
||||
Recommended chapters: 1, 7-18
|
||||
|
||||
## For Experienced Programmers
|
||||
|
||||
The following section lists several good tutorials/resources for those who have experience programming but who have not worked extensively with JavaScript before.
|
||||
|
||||
Before that, however, it's important to clarify some terminology about the different versions of JavaScript.
|
||||
These are summarized in this article:
|
||||
|
||||
[WTF is ES6, ES8, ES2017, ECMAScript...](https://codeburst.io/javascript-wtf-is-es6-es8-es-2017-ecmascript-dca859e4821c)
|
||||
|
||||
An important takeaway from this article is that ES6, also known as ES2015, introduced many major features that are commonly seen in modern JavaScript programming.
|
||||
However, this means that ES5 engines and interpreters will fail if they encounters these ES6 features.
|
||||
You'll see why this is important further down.
|
||||
|
||||
- [MDN Introduction to JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)
|
||||
- [Eloquent JavaScript (ES6+)](https://eloquentjavascript.net/)
|
||||
Recommended Chapters: Introduction, 1-6
|
||||
- [Modern JavaScript Tutorial (ES6+)](https://javascript.info/)
|
||||
Recommended Chapters: 2, 4-6
|
||||
@@ -0,0 +1,29 @@
|
||||
# How to use React in game
|
||||
|
||||
Since v2.7.0, Bitburner supports React and TypeScript out of the box. You can use the jsx syntax inside `.jsx` and `.tsx` files.
|
||||
|
||||
## Example
|
||||
|
||||
Use `ns.printRaw` and `ns.tprintRaw` to render React elements in the logs and terminal.
|
||||
|
||||
```tsx
|
||||
// timer.tsx
|
||||
function Timer() {
|
||||
const [seconds, setSeconds] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setSeconds((seconds) => seconds + 1);
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return <div>Seconds: {seconds}</div>;
|
||||
}
|
||||
|
||||
export async function main(ns: NS) {
|
||||
ns.tail();
|
||||
ns.printRaw(<Timer />);
|
||||
await ns.asleep(10000);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,174 @@
|
||||
# Remote API
|
||||
|
||||
All versions of Bitburner can use websockets to connect to a server.
|
||||
That server can then perform a number of actions.
|
||||
Most commonly this is used in conjunction with an external text editor or API
|
||||
in order to make writing scripts easier, or even use typescript.
|
||||
|
||||
To make use of this Remote API through the official server, look [here](https://github.com/bitburner-official/typescript-template).
|
||||
If you want to make your own server, see below for API details.
|
||||
|
||||
This API uses the JSON RPC 2.0 protocol. Inputs are in the following form:
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": string,
|
||||
"params": any
|
||||
}
|
||||
|
||||
Outputs:
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": any,
|
||||
"error": any
|
||||
}
|
||||
|
||||
## Methods
|
||||
|
||||
## `pushFile`
|
||||
|
||||
Create or update a file.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "pushFile",
|
||||
"params": {
|
||||
filename: string;
|
||||
content: string;
|
||||
server: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": "OK"
|
||||
}
|
||||
|
||||
## `getFile`
|
||||
|
||||
Read a file and its content.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "getFile",
|
||||
"params": {
|
||||
filename: string;
|
||||
server: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": string
|
||||
}
|
||||
|
||||
## `deleteFile`
|
||||
|
||||
Delete a file.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "deleteFile",
|
||||
"params": {
|
||||
filename: string;
|
||||
server: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": "OK"
|
||||
}
|
||||
|
||||
## `getFileNames`
|
||||
|
||||
List all file names on a server.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "getFileNames",
|
||||
"params": {
|
||||
server: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": string[]
|
||||
}
|
||||
|
||||
## `getAllFiles`
|
||||
|
||||
Get the content of all files on a server.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "getAllFiles",
|
||||
"params": {
|
||||
server: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": {
|
||||
filename: string,
|
||||
content: string
|
||||
}[]
|
||||
}
|
||||
|
||||
## `calculateRam`
|
||||
|
||||
Calculate the in-game ram cost of a script.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "calculateRam",
|
||||
"params": {
|
||||
filename: string;
|
||||
server: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": number
|
||||
}
|
||||
|
||||
## `getDefinitionFile`
|
||||
|
||||
Get the definition file of the API.
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"method": "getDefinitionFile"
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": number,
|
||||
"result": string
|
||||
}
|
||||
Reference in New Issue
Block a user