Skip to content

Commit

Permalink
IPVGO: Support scripts playing against each other as each color on "N…
Browse files Browse the repository at this point in the history
…o AI" boards (#1917)

This is a big change with a *lot* of moving parts.

The largest part of it is enabling scripts to `playAsWhite` as a parameter to many Go functions. In the implementation, this involved a significant rewrite of `opponentNextTurn` promise handling.

A number of other changes and bugfixes are included:
* Fixes the issue where handicap stones are added on game load.
* Better typing for error callbacks.
* Throw errors instead of deadlocking on bad cheat usage.
* Return always-resolved gameOver promise after game end
* Added a new `resetStats` api function.

---------

Co-authored-by: David Walker <[email protected]>
  • Loading branch information
ficocelliguy and d0sboots authored Feb 3, 2025
1 parent de6b202 commit c8d2c9f
Show file tree
Hide file tree
Showing 30 changed files with 503 additions and 264 deletions.
4 changes: 4 additions & 0 deletions markdown/bitburner.go.makemove.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

Make a move on the IPvGO subnet game board, and await the opponent's response. x:0 y:0 represents the bottom-left corner of the board in the UI.

playAsWhite is optional, and attempts to make a move as the white player. Only can be used when playing against "No AI".

**Signature:**

```typescript
makeMove(
x: number,
y: number,
playAsWhite = false,
): Promise<{
type: "move" | "pass" | "gameOver";
x: number | null;
Expand All @@ -25,6 +28,7 @@ makeMove(
| --- | --- | --- |
| x | number | |
| y | number | |
| playAsWhite | (not declared) | _(Optional)_ |

**Returns:**

Expand Down
6 changes: 3 additions & 3 deletions markdown/bitburner.go.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export interface Go
| [getGameState()](./bitburner.go.getgamestate.md) | Gets the status of the current game. Shows the current player, current score, and the previous move coordinates. Previous move coordinates will be \[-1, -1\] for a pass, or if there are no prior moves. |
| [getMoveHistory()](./bitburner.go.getmovehistory.md) | <p>Returns all the prior moves in the current game, as an array of simple board states.</p><p>For example, a single 5x5 prior move board might look like this:</p><p>\[<br/> "XX.O.",<br/> "X..OO",<br/> ".XO..",<br/> "XXO.\#",<br/> ".XO.\#",<br/> \]</p> |
| [getOpponent()](./bitburner.go.getopponent.md) | Returns the name of the opponent faction in the current subnet. |
| [makeMove(x, y)](./bitburner.go.makemove.md) | Make a move on the IPvGO subnet game board, and await the opponent's response. x:0 y:0 represents the bottom-left corner of the board in the UI. |
| [opponentNextTurn(logOpponentMove)](./bitburner.go.opponentnextturn.md) | Returns a promise that resolves with the success or failure state of your last move, and the AI's response, if applicable. x:0 y:0 represents the bottom-left corner of the board in the UI. |
| [passTurn()](./bitburner.go.passturn.md) | <p>Pass the player's turn rather than making a move, and await the opponent's response. This ends the game if the opponent passed on the previous turn, or if the opponent passes on their following turn.</p><p>This can also be used if you pick up the game in a state where the opponent needs to play next. For example: if BitBurner was closed while waiting for the opponent to make a move, you may need to call passTurn() to get them to play their move on game start.</p> |
| [makeMove(x, y, playAsWhite)](./bitburner.go.makemove.md) | <p>Make a move on the IPvGO subnet game board, and await the opponent's response. x:0 y:0 represents the bottom-left corner of the board in the UI.</p><p>playAsWhite is optional, and attempts to make a move as the white player. Only can be used when playing against "No AI".</p> |
| [opponentNextTurn(logOpponentMove, playAsWhite)](./bitburner.go.opponentnextturn.md) | Returns a promise that resolves with the success or failure state of your last move, and the AI's response, if applicable. x:0 y:0 represents the bottom-left corner of the board in the UI. |
| [passTurn(passAsWhite)](./bitburner.go.passturn.md) | <p>Pass the player's turn rather than making a move, and await the opponent's response. This ends the game if the opponent passed on the previous turn, or if the opponent passes on their following turn.</p><p>This can also be used if you pick up the game in a state where the opponent needs to play next. For example: if BitBurner was closed while waiting for the opponent to make a move, you may need to call passTurn() to get them to play their move on game start.</p><p>passAsWhite is optional, and attempts to pass while playing as the white player. Only can be used when playing against "No AI".</p> |
| [resetBoardState(opponent, boardSize)](./bitburner.go.resetboardstate.md) | <p>Gets new IPvGO subnet with the specified size owned by the listed faction, ready for the player to make a move. This will reset your win streak if the current game is not complete and you have already made moves.</p><p>Note that some factions will have a few routers already on the subnet after a reset.</p><p>opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????" or "No AI",</p> |

6 changes: 5 additions & 1 deletion markdown/bitburner.go.opponentnextturn.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ Returns a promise that resolves with the success or failure state of your last m
**Signature:**

```typescript
opponentNextTurn(logOpponentMove?: boolean): Promise<{
opponentNextTurn(
logOpponentMove?: boolean,
playAsWhite = false,
): Promise<{
type: "move" | "pass" | "gameOver";
x: number | null;
y: number | null;
Expand All @@ -21,6 +24,7 @@ opponentNextTurn(logOpponentMove?: boolean): Promise<{
| Parameter | Type | Description |
| --- | --- | --- |
| logOpponentMove | boolean | _(Optional)_ optional, defaults to true. if false prevents logging opponent move |
| playAsWhite | (not declared) | _(Optional)_ optional. If true, waits to get the next move the black player makes. Intended to be used when playing as white when the opponent is set to "No AI" |

**Returns:**

Expand Down
11 changes: 10 additions & 1 deletion markdown/bitburner.go.passturn.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,24 @@ Pass the player's turn rather than making a move, and await the opponent's respo

This can also be used if you pick up the game in a state where the opponent needs to play next. For example: if BitBurner was closed while waiting for the opponent to make a move, you may need to call passTurn() to get them to play their move on game start.

passAsWhite is optional, and attempts to pass while playing as the white player. Only can be used when playing against "No AI".

**Signature:**

```typescript
passTurn(): Promise<{
passTurn(passAsWhite = false): Promise<{
type: "move" | "pass" | "gameOver";
x: number | null;
y: number | null;
}>;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| passAsWhite | (not declared) | _(Optional)_ |

**Returns:**

Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;
Expand Down
5 changes: 4 additions & 1 deletion markdown/bitburner.goanalysis.getvalidmoves.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ Note that the \[0\]\[0\] point is shown on the bottom-left on the visual board (

Also note that, when given a custom board state, only one prior move can be analyzed. This means that the superko rules (no duplicate board states in the full game history) is not supported; you will have to implement your own analysis for that.

playAsWhite is optional, and gets the current valid moves for the white player. Intended to be used when playing as white when the opponent is set to "No AI"

**Signature:**

```typescript
getValidMoves(boardState?: string[], priorBoardState?: string[]): boolean[][];
getValidMoves(boardState?: string[], priorBoardState?: string[], playAsWhite = false): boolean[][];
```

## Parameters
Expand All @@ -26,6 +28,7 @@ getValidMoves(boardState?: string[], priorBoardState?: string[]): boolean[][];
| --- | --- | --- |
| boardState | string\[\] | _(Optional)_ |
| priorBoardState | string\[\] | _(Optional)_ |
| playAsWhite | (not declared) | _(Optional)_ |

**Returns:**

Expand Down
3 changes: 2 additions & 1 deletion markdown/bitburner.goanalysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export interface GoAnalysis
| [getControlledEmptyNodes(boardState)](./bitburner.goanalysis.getcontrolledemptynodes.md) | <p>Returns 'X' for black, 'O' for white, or '?' for each empty point to indicate which player controls that empty point. If no single player fully encircles the empty space, it is shown as contested with '?'. "\#" are dead nodes that are not part of the subnet.</p><p>Takes an optional boardState argument; by default uses the current board state.</p><p>Filled points of any color are indicated with '.'</p><p>In this example, white encircles some space in the top-left, black encircles some in the top-right, and between their routers is contested space in the center: <pre lang="javascript"> \[ "OO..?", "OO.?.", "O.?.X", ".?.XX", "?..X\#", \] </pre></p> |
| [getLiberties(boardState)](./bitburner.goanalysis.getliberties.md) | <p>Returns a number for each point, representing how many open nodes its network/chain is connected to. Empty nodes and dead nodes are shown as -1 liberties.</p><p>Takes an optional boardState argument; by default uses the current board state.</p><p>For example, a 5x5 board might look like this. The chain in the top-left touches 5 total empty nodes, and the one in the center touches four. The group in the bottom-right only has one liberty; it is in danger of being captured! <pre lang="javascript"> \[ \[-1, 5,-1,-1, 2\], \[ 5, 5,-1,-1,-1\], \[-1,-1, 4,-1,-1\], \[ 3,-1,-1, 3, 1\], \[ 3,-1,-1, 3, 1\], \] </pre></p> |
| [getStats()](./bitburner.goanalysis.getstats.md) | <p>Displays the game history, captured nodes, and gained bonuses for each opponent you have played against.</p><p>The details are keyed by opponent name, in this structure:</p><p><pre lang="javascript"> { <OpponentName>: { wins: number, losses: number, winStreak: number, highestWinStreak: number, favor: number, bonusPercent: number, bonusDescription: string, } } </pre></p> |
| [getValidMoves(boardState, priorBoardState)](./bitburner.goanalysis.getvalidmoves.md) | <p>Shows if each point on the board is a valid move for the player. By default, analyzes the current board state. Takes an optional boardState (and an optional prior-move boardState, if desired) to analyze a custom board.</p><p>The true/false validity of each move can be retrieved via the X and Y coordinates of the move. <code>const validMoves = ns.go.analysis.getValidMoves();</code></p><p><code>const moveIsValid = validMoves[x][y];</code></p><p>Note that the \[0\]\[0\] point is shown on the bottom-left on the visual board (as is traditional), and each string represents a vertical column on the board. In other words, the printed example above can be understood to be rotated 90 degrees clockwise compared to the board UI as shown in the IPvGO subnet tab.</p><p>Also note that, when given a custom board state, only one prior move can be analyzed. This means that the superko rules (no duplicate board states in the full game history) is not supported; you will have to implement your own analysis for that.</p> |
| [getValidMoves(boardState, priorBoardState, playAsWhite)](./bitburner.goanalysis.getvalidmoves.md) | <p>Shows if each point on the board is a valid move for the player. By default, analyzes the current board state. Takes an optional boardState (and an optional prior-move boardState, if desired) to analyze a custom board.</p><p>The true/false validity of each move can be retrieved via the X and Y coordinates of the move. <code>const validMoves = ns.go.analysis.getValidMoves();</code></p><p><code>const moveIsValid = validMoves[x][y];</code></p><p>Note that the \[0\]\[0\] point is shown on the bottom-left on the visual board (as is traditional), and each string represents a vertical column on the board. In other words, the printed example above can be understood to be rotated 90 degrees clockwise compared to the board UI as shown in the IPvGO subnet tab.</p><p>Also note that, when given a custom board state, only one prior move can be analyzed. This means that the superko rules (no duplicate board states in the full game history) is not supported; you will have to implement your own analysis for that.</p><p>playAsWhite is optional, and gets the current valid moves for the white player. Intended to be used when playing as white when the opponent is set to "No AI"</p> |
| [resetStats(resetAll)](./bitburner.goanalysis.resetstats.md) | Reset all win/loss and winstreak records for the No AI opponent. |

24 changes: 24 additions & 0 deletions markdown/bitburner.goanalysis.resetstats.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [GoAnalysis](./bitburner.goanalysis.md) &gt; [resetStats](./bitburner.goanalysis.resetstats.md)

## GoAnalysis.resetStats() method

Reset all win/loss and winstreak records for the No AI opponent.

**Signature:**

```typescript
resetStats(resetAll = false): void;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| resetAll | (not declared) | _(Optional)_ if true, reset win/loss records for all opponents. Leaves node power and bonuses unchanged. |

**Returns:**

void

6 changes: 4 additions & 2 deletions markdown/bitburner.gocheat.destroynode.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Warning: if you fail to play a cheat move, your turn will be skipped. After your
destroyNode(
x: number,
y: number,
playAsWhite = false,
): Promise<{
type: "move" | "pass" | "gameOver";
x: number | null;
Expand All @@ -27,8 +28,9 @@ destroyNode(

| Parameter | Type | Description |
| --- | --- | --- |
| x | number | |
| y | number | |
| x | number | x coordinate of empty node to destroy |
| y | number | y coordinate of empty node to destroy |
| playAsWhite | (not declared) | _(Optional)_ Optional override for playing as white. Can only be used when playing on a 'No AI' board. |

**Returns:**

Expand Down
9 changes: 8 additions & 1 deletion markdown/bitburner.gocheat.getcheatcount.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ Returns the number of times you've attempted to cheat in the current game.
**Signature:**

```typescript
getCheatCount(): number;
getCheatCount(playAsWhite = false): number;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| playAsWhite | (not declared) | _(Optional)_ Optional override for playing as white. Can only be used when playing on a 'No AI' board. |

**Returns:**

number
Expand Down
3 changes: 2 additions & 1 deletion markdown/bitburner.gocheat.getcheatsuccesschance.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ Warning: if you fail to play a cheat move, your turn will be skipped. After your
**Signature:**

```typescript
getCheatSuccessChance(cheatCount?: number): number;
getCheatSuccessChance(cheatCount?: number, playAsWhite = false): number;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| cheatCount | number | _(Optional)_ Optional override for the number of cheats already attempted. Defaults to the number of cheats attempted in the current game. |
| playAsWhite | (not declared) | _(Optional)_ Optional override for playing as white. Can only be used when playing on a 'No AI' board. |

**Returns:**

Expand Down
12 changes: 6 additions & 6 deletions markdown/bitburner.gocheat.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export interface GoCheat

| Method | Description |
| --- | --- |
| [destroyNode(x, y)](./bitburner.gocheat.destroynode.md) | <p>Attempts to destroy an empty node, leaving an offline dead space that does not count as territory or provide open node access to adjacent routers.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [getCheatCount()](./bitburner.gocheat.getcheatcount.md) | Returns the number of times you've attempted to cheat in the current game. |
| [getCheatSuccessChance(cheatCount)](./bitburner.gocheat.getcheatsuccesschance.md) | <p>Returns your chance of successfully playing one of the special moves in the ns.go.cheat API. Scales up with your crime success rate stat. Scales down with the number of times you've attempted to cheat in the current game.</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [playTwoMoves(x1, y1, x2, y2)](./bitburner.gocheat.playtwomoves.md) | <p>Attempts to place two routers at once on empty nodes. Note that this ignores other move restrictions, so you can suicide your own routers if they have no access to empty ports and do not capture any enemy routers.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [removeRouter(x, y)](./bitburner.gocheat.removerouter.md) | <p>Attempts to remove an existing router, leaving an empty node behind.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [repairOfflineNode(x, y)](./bitburner.gocheat.repairofflinenode.md) | <p>Attempts to repair an offline node, leaving an empty playable node behind.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [destroyNode(x, y, playAsWhite)](./bitburner.gocheat.destroynode.md) | <p>Attempts to destroy an empty node, leaving an offline dead space that does not count as territory or provide open node access to adjacent routers.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [getCheatCount(playAsWhite)](./bitburner.gocheat.getcheatcount.md) | Returns the number of times you've attempted to cheat in the current game. |
| [getCheatSuccessChance(cheatCount, playAsWhite)](./bitburner.gocheat.getcheatsuccesschance.md) | <p>Returns your chance of successfully playing one of the special moves in the ns.go.cheat API. Scales up with your crime success rate stat. Scales down with the number of times you've attempted to cheat in the current game.</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [playTwoMoves(x1, y1, x2, y2, playAsWhite)](./bitburner.gocheat.playtwomoves.md) | <p>Attempts to place two routers at once on empty nodes. Note that this ignores other move restrictions, so you can suicide your own routers if they have no access to empty ports and do not capture any enemy routers.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [removeRouter(x, y, playAsWhite)](./bitburner.gocheat.removerouter.md) | <p>Attempts to remove an existing router, leaving an empty node behind.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |
| [repairOfflineNode(x, y, playAsWhite)](./bitburner.gocheat.repairofflinenode.md) | <p>Attempts to repair an offline node, leaving an empty playable node behind.</p><p>Success chance can be seen via ns.go.getCheatSuccessChance()</p><p>Warning: if you fail to play a cheat move, your turn will be skipped. After your first cheat attempt, if you fail, there is a small (\~10%) chance you will instantly be ejected from the subnet.</p> |

10 changes: 6 additions & 4 deletions markdown/bitburner.gocheat.playtwomoves.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ playTwoMoves(
y1: number,
x2: number,
y2: number,
playAsWhite = false,
): Promise<{
type: "move" | "pass" | "gameOver";
x: number | null;
Expand All @@ -29,10 +30,11 @@ playTwoMoves(

| Parameter | Type | Description |
| --- | --- | --- |
| x1 | number | |
| y1 | number | |
| x2 | number | |
| y2 | number | |
| x1 | number | x coordinate of first move to make |
| y1 | number | y coordinate of first move to make |
| x2 | number | x coordinate of second move to make |
| y2 | number | y coordinate of second move to make |
| playAsWhite | (not declared) | _(Optional)_ Optional override for playing as white. Can only be used when playing on a 'No AI' board. |

**Returns:**

Expand Down
Loading

0 comments on commit c8d2c9f

Please sign in to comment.