mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-22 06:02:26 +01:00
IPVGO: Remove current game history from savefile, re-implement superko (#1175)
This commit is contained in:
parent
fc8958af83
commit
1e5f7184a2
@ -12,7 +12,6 @@ import { isInteger, isNumber } from "../types";
|
||||
|
||||
type PreviousGameSaveData = { ai: GoOpponent; board: SimpleBoard; previousPlayer: GoColor | null } | null;
|
||||
type CurrentGameSaveData = PreviousGameSaveData & {
|
||||
previousBoard: SimpleBoard | null;
|
||||
cheatCount: number;
|
||||
passCount: number;
|
||||
};
|
||||
@ -35,7 +34,6 @@ export function getGoSave(): SaveFormat {
|
||||
currentGame: {
|
||||
ai: Go.currentGame.ai,
|
||||
board: simpleBoardFromBoard(Go.currentGame.board),
|
||||
previousBoard: Go.currentGame.previousBoard,
|
||||
previousPlayer: Go.currentGame.previousPlayer,
|
||||
cheatCount: Go.currentGame.cheatCount,
|
||||
passCount: Go.currentGame.passCount,
|
||||
@ -94,8 +92,6 @@ function loadCurrentGame(currentGame: unknown): BoardState | string {
|
||||
const requiredSize = currentGame.board.length;
|
||||
const board = loadSimpleBoard(currentGame.board, requiredSize);
|
||||
if (typeof board === "string") return board;
|
||||
const previousBoard = currentGame.previousBoard ? loadSimpleBoard(currentGame.previousBoard, requiredSize) : null;
|
||||
if (typeof previousBoard === "string") return previousBoard;
|
||||
const previousPlayer = getEnumHelper("GoColor").getMember(currentGame.previousPlayer) ?? null;
|
||||
if (!isInteger(currentGame.cheatCount) || currentGame.cheatCount < 0)
|
||||
return "invalid number for currentGame.cheatCount";
|
||||
@ -105,7 +101,7 @@ function loadCurrentGame(currentGame: unknown): BoardState | string {
|
||||
boardState.previousPlayer = previousPlayer;
|
||||
boardState.cheatCount = currentGame.cheatCount;
|
||||
boardState.passCount = currentGame.passCount;
|
||||
boardState.previousBoard = previousBoard;
|
||||
boardState.previousBoards = [];
|
||||
return boardState;
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,8 @@ export type EyeMove = {
|
||||
export type BoardState = {
|
||||
board: Board;
|
||||
previousPlayer: GoColor | null;
|
||||
/** The previous board position as a SimpleBoard */
|
||||
previousBoard: SimpleBoard | null;
|
||||
/** The previous board positions as a SimpleBoard */
|
||||
previousBoards: SimpleBoard[];
|
||||
ai: GoOpponent;
|
||||
passCount: number;
|
||||
cheatCount: number;
|
||||
|
@ -44,7 +44,7 @@ export function evaluateIfMoveIsValid(boardState: BoardState, x: number, y: numb
|
||||
}
|
||||
|
||||
// Detect if the move might be an immediate repeat (only one board of history is saved to check)
|
||||
const possibleRepeat = boardState.previousBoard && getColorOnSimpleBoard(boardState.previousBoard, x, y) === player;
|
||||
const possibleRepeat = boardState.previousBoards.find((board) => getColorOnSimpleBoard(board, x, y) === player);
|
||||
|
||||
if (shortcut) {
|
||||
// If the current point has some adjacent open spaces, it is not suicide. If the move is not repeated, it is legal
|
||||
@ -85,9 +85,11 @@ export function evaluateIfMoveIsValid(boardState: BoardState, x: number, y: numb
|
||||
if (evaluationBoard[x]?.[y]?.color !== player) {
|
||||
return GoValidity.noSuicide;
|
||||
}
|
||||
if (possibleRepeat && boardState.previousBoard) {
|
||||
if (possibleRepeat && boardState.previousBoards.length) {
|
||||
const simpleEvalBoard = simpleBoardFromBoard(evaluationBoard);
|
||||
if (areSimpleBoardsIdentical(simpleEvalBoard, boardState.previousBoard)) return GoValidity.boardRepeated;
|
||||
if (boardState.previousBoards.find((board) => areSimpleBoardsIdentical(simpleEvalBoard, board))) {
|
||||
return GoValidity.boardRepeated;
|
||||
}
|
||||
}
|
||||
|
||||
return GoValidity.valid;
|
||||
|
@ -27,7 +27,7 @@ export function getNewBoardState(
|
||||
}
|
||||
|
||||
const newBoardState: BoardState = {
|
||||
previousBoard: null,
|
||||
previousBoards: [],
|
||||
previousPlayer: GoColor.white,
|
||||
ai: ai,
|
||||
passCount: 0,
|
||||
@ -82,7 +82,12 @@ export function makeMove(boardState: BoardState, x: number, y: number, player: G
|
||||
return false;
|
||||
}
|
||||
|
||||
boardState.previousBoard = simpleBoardFromBoard(boardState.board);
|
||||
// Only maintain last 7 moves
|
||||
boardState.previousBoards.unshift(simpleBoardFromBoard(boardState.board));
|
||||
if (boardState.previousBoards.length > 7) {
|
||||
boardState.previousBoards.pop();
|
||||
}
|
||||
|
||||
const point = boardState.board[x][y];
|
||||
if (!point) return false;
|
||||
|
||||
@ -266,7 +271,7 @@ export function getEmptySpaces(board: Board): PointState[] {
|
||||
export function getStateCopy(initialState: BoardState) {
|
||||
const boardState = structuredClone(initialState);
|
||||
|
||||
boardState.previousBoard = initialState.previousBoard ? [...initialState.previousBoard] : null;
|
||||
boardState.previousBoards = initialState.previousBoards ?? [];
|
||||
boardState.previousPlayer = initialState.previousPlayer;
|
||||
boardState.ai = initialState.ai;
|
||||
boardState.passCount = initialState.passCount;
|
||||
|
@ -98,7 +98,7 @@ function calculateMults(): Multipliers {
|
||||
}
|
||||
|
||||
export function playerHasDiscoveredGo() {
|
||||
const playedGame = Go.currentGame.previousBoard;
|
||||
const playedGame = Go.currentGame.previousBoards.length;
|
||||
const hasRecords = getRecordValues(Go.stats).some((stats) => stats.wins + stats.losses);
|
||||
const isInBn14 = Player.bitNodeN === 14;
|
||||
|
||||
|
@ -300,11 +300,11 @@ export function getCurrentPlayer(): "None" | "White" | "Black" {
|
||||
* Find a move made by the previous player, if present.
|
||||
*/
|
||||
export function getPreviousMove(): [number, number] | null {
|
||||
if (Go.currentGame.passCount) {
|
||||
const priorBoard = Go.currentGame?.previousBoards[0];
|
||||
if (Go.currentGame.passCount || !priorBoard) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const priorBoard = Go.currentGame?.previousBoard;
|
||||
for (const rowIndexString in Go.currentGame.board) {
|
||||
const row = Go.currentGame.board[+rowIndexString] ?? [];
|
||||
for (const pointIndexString in row) {
|
||||
@ -348,7 +348,7 @@ export function resetBoardState(error: (s: string) => void, opponent: GoOpponent
|
||||
}
|
||||
|
||||
const oldBoardState = Go.currentGame;
|
||||
if (oldBoardState.previousPlayer !== null && oldBoardState.previousBoard) {
|
||||
if (oldBoardState.previousPlayer !== null && oldBoardState.previousBoards.length) {
|
||||
resetWinstreak(oldBoardState.ai, false);
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
||||
setScoreOpen(false);
|
||||
setSearchOpen(false);
|
||||
setOpponent(newOpponent);
|
||||
if (boardState.previousPlayer !== null && boardState.previousBoard) {
|
||||
if (boardState.previousPlayer !== null && boardState.previousBoards.length) {
|
||||
resetWinstreak(boardState.ai, false);
|
||||
}
|
||||
|
||||
@ -152,16 +152,16 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
||||
}
|
||||
|
||||
function getPriorMove() {
|
||||
if (!boardState.previousBoard) return boardState;
|
||||
if (!boardState.previousBoards.length) return boardState;
|
||||
const priorState = getStateCopy(boardState);
|
||||
priorState.previousPlayer = boardState.previousPlayer === GoColor.black ? GoColor.white : GoColor.black;
|
||||
priorState.board = boardFromSimpleBoard(boardState.previousBoard);
|
||||
priorState.board = boardFromSimpleBoard(boardState.previousBoards[0]);
|
||||
updateCaptures(priorState.board, priorState.previousPlayer);
|
||||
return priorState;
|
||||
}
|
||||
|
||||
function showPreviousMove(newValue: boolean) {
|
||||
if (boardState.previousBoard) {
|
||||
if (boardState.previousBoards.length) {
|
||||
setShowPriorMove(newValue);
|
||||
}
|
||||
}
|
||||
@ -175,7 +175,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
||||
boardState.previousPlayer === GoColor.white && !getAllValidMoves(boardState, GoColor.black).length;
|
||||
const disablePassButton = opponent !== GoOpponent.none && boardState.previousPlayer === GoColor.black && waitingOnAI;
|
||||
|
||||
const scoreBoxText = boardState.previousBoard
|
||||
const scoreBoxText = boardState.previousBoards.length
|
||||
? `Score: Black: ${score[GoColor.black].sum} White: ${score[GoColor.white].sum}`
|
||||
: "Place a router to begin!";
|
||||
|
||||
@ -256,7 +256,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={showPriorMove}
|
||||
disabled={!boardState.previousBoard}
|
||||
disabled={!boardState.previousBoards.length}
|
||||
onChange={(newValue) => showPreviousMove(newValue)}
|
||||
text="Show previous move"
|
||||
tooltip={<>Show the board as it was before the last move</>}
|
||||
|
@ -41,7 +41,8 @@ export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwne
|
||||
|
||||
const sizeClass = getSizeClass(state.board[0].length, classes);
|
||||
|
||||
const isNewStone = state.previousBoard && getColorOnSimpleBoard(state.previousBoard, x, y) === GoColor.empty;
|
||||
const isNewStone =
|
||||
state.previousBoards.length && getColorOnSimpleBoard(state.previousBoards[0], x, y) === GoColor.empty;
|
||||
const isPriorMove = player === state.previousPlayer && isNewStone;
|
||||
|
||||
const emptyPointColorClass =
|
||||
|
@ -38,7 +38,7 @@ export function GoTutorialChallenge({
|
||||
const [showReset, setShowReset] = useState(false);
|
||||
|
||||
const handleClick = (x: number, y: number) => {
|
||||
if (stateRef.current.previousBoard) {
|
||||
if (stateRef.current.previousBoards.length) {
|
||||
SnackbarEvents.emit(`Hit 'Reset' to try again`, ToastVariant.WARNING, 2000);
|
||||
return;
|
||||
}
|
||||
@ -67,6 +67,7 @@ export function GoTutorialChallenge({
|
||||
|
||||
const reset = () => {
|
||||
stateRef.current = getStateCopy(state);
|
||||
stateRef.current.previousBoards = [];
|
||||
setDisplayText(description);
|
||||
setShowReset(false);
|
||||
};
|
||||
|
@ -81,7 +81,7 @@ describe("Netscript Go API unit tests", () => {
|
||||
it("should correctly retrieve the current game state", async () => {
|
||||
const board = ["OXX..", ".....", ".....", "...XX", "...X."];
|
||||
const boardState = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.black);
|
||||
boardState.previousBoard = ["OX..", ".....", ".....", "...XX", "...X."];
|
||||
boardState.previousBoards = [["OX..", ".....", ".....", "...XX", "...X."]];
|
||||
Go.currentGame = boardState;
|
||||
|
||||
const result = getGameState();
|
||||
@ -232,7 +232,7 @@ describe("Netscript Go API unit tests", () => {
|
||||
|
||||
await cheatPlayTwoMoves(mockLogger, 4, 3, 3, 4, 1, 0);
|
||||
expect(mockLogger).toHaveBeenCalledWith("Cheat failed! You have been ejected from the subnet.");
|
||||
expect(Go.currentGame.previousBoard).toEqual(null);
|
||||
expect(Go.currentGame.previousBoards).toEqual([]);
|
||||
});
|
||||
});
|
||||
describe("cheatRemoveRouter() tests", () => {
|
||||
@ -270,7 +270,7 @@ describe("Netscript Go API unit tests", () => {
|
||||
|
||||
await cheatRemoveRouter(mockLogger, 0, 0, 1, 0);
|
||||
expect(mockLogger).toHaveBeenCalledWith("Cheat failed! You have been ejected from the subnet.");
|
||||
expect(Go.currentGame.previousBoard).toEqual(null);
|
||||
expect(Go.currentGame.previousBoards).toEqual([]);
|
||||
});
|
||||
});
|
||||
describe("cheatRepairOfflineNode() tests", () => {
|
||||
|
@ -53,4 +53,16 @@ describe("Go board analysis tests", () => {
|
||||
|
||||
expect(validity).toEqual(GoValidity.noSuicide);
|
||||
});
|
||||
|
||||
it("identifies invalid moves from repeat", async () => {
|
||||
const board = [".X...", ".....", ".....", ".....", "....."];
|
||||
const boardState = boardStateFromSimpleBoard(board);
|
||||
boardState.previousBoards.push([".X...", ".....", ".....", ".....", "....."]);
|
||||
boardState.previousBoards.push([".X...", ".....", ".....", ".....", "....."]);
|
||||
boardState.previousBoards.push([".X...", ".....", ".....", ".....", "....."]);
|
||||
boardState.previousBoards.push(["OX...", ".....", ".....", ".....", "....."]);
|
||||
const validity = evaluateIfMoveIsValid(boardState, 0, 0, GoColor.white, false);
|
||||
|
||||
expect(validity).toEqual(GoValidity.boardRepeated);
|
||||
});
|
||||
});
|
||||
|
@ -41,7 +41,6 @@ exports[`Check Save File Continuity GoSave continuity 1`] = `
|
||||
],
|
||||
"cheatCount": 0,
|
||||
"passCount": 0,
|
||||
"previousBoard": null,
|
||||
"previousPlayer": "White",
|
||||
},
|
||||
"previousGame": null,
|
||||
|
Loading…
Reference in New Issue
Block a user