IPVGO: Add new analysis method to set a custom testing board state (#2029)

This commit is contained in:
Michael Ficocelli
2025-03-23 02:52:26 -04:00
committed by GitHub
parent de8c8691c2
commit 17ffabdfa5
11 changed files with 76 additions and 4 deletions

View File

@@ -22,4 +22,5 @@ export interface GoAnalysis
| [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, 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. |
| [setTestingBoardState(boardState)](./bitburner.goanalysis.settestingboardstate.md) | Starts a new game against the "No AI" opponent, and sets the initial board size, pieces, and offline nodes to the given board state. "X" represent black pieces, "O" white, and "." empty points. "\#" are dead nodes that are not part of the subnet. |

View File

@@ -0,0 +1,28 @@
<!-- 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; [setTestingBoardState](./bitburner.goanalysis.settestingboardstate.md)
## GoAnalysis.setTestingBoardState() method
Starts a new game against the "No AI" opponent, and sets the initial board size, pieces, and offline nodes to the given board state. "X" represent black pieces, "O" white, and "." empty points. "\#" are dead nodes that are not part of the subnet.
**Signature:**
```typescript
setTestingBoardState(boardState: string[]): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| boardState | string\[\] | The initial board state to use for the new game, in the format used by getBoardState(). |
**Returns:**
void
## Remarks
RAM cost: 4 GB

View File

@@ -54,6 +54,7 @@ export type BoardState = {
passCount: number;
cheatCount: number;
cheatCountForWhite: number;
komiOverride: number | null;
};
export type PointState = {

View File

@@ -860,8 +860,11 @@ function getMoveOptions(boardState: BoardState, player: GoColor, rng: number, sm
/**
* Gets the starting score for white.
*/
export function getKomi(opponent: GoOpponent) {
return opponentDetails[opponent].komi;
export function getKomi(state: BoardState): number {
if (state.komiOverride !== null) {
return state.komiOverride;
}
return opponentDetails[state.ai].komi;
}
/**

View File

@@ -17,7 +17,7 @@ import { Go, GoEvents } from "../Go";
* fully surrounded by their pieces
*/
export function getScore(boardState: BoardState) {
const komi = getKomi(boardState.ai) ?? 6.5;
const komi = getKomi(boardState) ?? 6.5;
const whitePieces = getColoredPieceCount(boardState, GoColor.white);
const blackPieces = getColoredPieceCount(boardState, GoColor.black);
const territoryScores = getTerritoryScores(boardState.board);

View File

@@ -35,6 +35,7 @@ export function getNewBoardState(
passCount: 0,
cheatCount: 0,
cheatCountForWhite: 0,
komiOverride: null,
board: Array.from({ length: boardSize }, (_, x) =>
Array.from({ length: boardSize }, (_, y) =>
!boardToCopy || boardToCopy?.[x]?.[y]

View File

@@ -2,7 +2,7 @@ import { Board, BoardState, OpponentStats, Play, SimpleBoard, SimpleOpponentStat
import { Player } from "@player";
import { AugmentationName, GoColor, GoOpponent, GoPlayType, GoValidity } from "@enums";
import { Go } from "../Go";
import { Go, GoEvents } from "../Go";
import {
getNewBoardState,
getNewBoardStateFromSimpleBoard,
@@ -263,6 +263,20 @@ export function getControlledEmptyNodes(_board?: Board) {
);
}
export function setTestingBoardState(board: Board, komi?: number) {
resetBoardState(
() => {},
() => {},
GoOpponent.none,
board.length,
);
Go.currentGame.board = board;
if (komi != undefined) {
Go.currentGame.komiOverride = komi;
}
GoEvents.emit();
}
/**
* Returns all previous board states as SimpleBoards
*/

View File

@@ -271,6 +271,7 @@ const go = {
getControlledEmptyNodes: 16,
getStats: 0,
resetStats: 0,
setTestingBoardState: 4,
},
cheat: {
getCheatSuccessChance: 1,

View File

@@ -25,6 +25,7 @@ import {
makePlayerMove,
resetBoardState,
resetStats,
setTestingBoardState,
validateBoardState,
validateMove,
} from "../Go/effects/netscriptGoImplementation";
@@ -104,6 +105,15 @@ export function NetscriptGo(): InternalAPI<NSGo> {
(resetAll = false) => {
resetStats(!!resetAll);
},
setTestingBoardState: (ctx) => (_boardState, _komi) => {
const State = validateBoardState(error(ctx), _boardState);
if (!State) {
error(ctx)("Invalid board state passed to setTestingBoardState()");
return;
}
const komi: number | undefined = _komi !== undefined ? helpers.number(ctx, "komi", _komi) : undefined;
return setTestingBoardState(State.board, komi);
},
},
cheat: {
getCheatSuccessChance: (ctx: NetscriptContext) => (_cheatCount, playAsWhite) => {

View File

@@ -4495,6 +4495,18 @@ export interface GoAnalysis {
* @param resetAll if true, reset win/loss records for all opponents. Leaves node power and bonuses unchanged.
*/
resetStats(resetAll = false): void;
/**
* Starts a new game against the "No AI" opponent, and sets the initial board size, pieces, and offline nodes to the given board state.
* "X" represent black pieces, "O" white, and "." empty points. "#" are dead nodes that are not part of the subnet.
*
* @remarks
* RAM cost: 4 GB
*
* @param boardState - The initial board state to use for the new game, in the format used by getBoardState().
* @param komi - Optional komi value to set for the game. Defaults to 5.5.
*/
setTestingBoardState(boardState: string[], komi?: number): void;
}
/**

View File

@@ -20,6 +20,7 @@ describe("Board analysis utility tests", () => {
passCount: 0,
cheatCount: 0,
cheatCountForWhite: 0,
komiOverride: null,
});
expect(result.board?.length).toEqual(5);
});