diff --git a/src/Go/SaveLoad.ts b/src/Go/SaveLoad.ts index 8b76b50d7..19877452f 100644 --- a/src/Go/SaveLoad.ts +++ b/src/Go/SaveLoad.ts @@ -85,7 +85,7 @@ export function loadGo(data: unknown): boolean { // If it's the AI's turn, initiate their turn, which will populate nextTurn if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) { makeAIMove(currentGame).catch((error) => { - showError(error); + showError(new Error(`Error while making first IPvGO AI move: ${error}`, { cause: error })); }); } // If it's not the AI's turn and we're not in gameover status, initialize nextTurn promise based on the previous move/pass @@ -152,9 +152,10 @@ function loadStats(stats: unknown): PartialRecord | s const entries = Object.entries(stats); for (const [opponent, opponentStats] of entries) { if (!getEnumHelper("GoOpponent").isMember(opponent)) return `Invalid opponent in Go.stats: ${opponent}`; - if (!opponentStats || typeof opponentStats !== "object") "Non-object encountered for an opponent's stats"; - assertLoadingType(opponentStats); - const { favor, highestWinStreak, losses, nodes, wins, oldWinStreak, winStreak, nodePower } = opponentStats; + if (!opponentStats || typeof opponentStats !== "object") return "Non-object encountered for an opponent's stats"; + assertLoadingType(opponentStats as object); + const { favor, highestWinStreak, losses, nodes, wins, oldWinStreak, winStreak, nodePower } = + opponentStats as OpponentStats; // Integers >= 0. Todo: make a better helper for this. if (!isInteger(favor) || favor < 0) return "A favor entry in Go.stats was invalid"; if (!isInteger(highestWinStreak) || highestWinStreak < 0) return "A highestWinStreak entry in Go.stats was invalid"; @@ -183,7 +184,7 @@ function loadSimpleBoard(simpleBoard: unknown, requiredSize?: number): SimpleBoa if (!simpleBoard.every((column) => typeof column === "string" && column.length === requiredSize)) { return "Incorrect types or column size while loading a SimpleBoard."; } - return simpleBoard; + return simpleBoard as SimpleBoard; } function loadStoredCycles(storedCycles: unknown): number { diff --git a/src/Go/Types.ts b/src/Go/Types.ts index c765c6aec..7a83d7244 100644 --- a/src/Go/Types.ts +++ b/src/Go/Types.ts @@ -11,7 +11,7 @@ export type Move = { createsLife?: boolean; }; -type MoveType = +export type MoveType = | "capture" | "defendCapture" | "eyeMove" @@ -25,8 +25,20 @@ type MoveType = | "corner" | "random"; -type MoveFunction = () => Promise; -export type MoveOptions = Record; +export type MoveOptions = { + readonly eyeMove: () => Move | null; + readonly random: () => Move | null; + readonly defendCapture: () => Promise; + readonly corner: () => Move | null; + readonly defend: () => Move | null; + readonly pattern: () => Promise; + readonly capture: () => Promise; + readonly growth: () => Move | null; + readonly eyeBlock: () => Move | null; + readonly surround: () => Move | null; + readonly expansion: () => Move | null; + readonly jump: () => Move | null; +}; export type EyeMove = { point: PointState; diff --git a/src/Go/boardAnalysis/boardAnalysis.ts b/src/Go/boardAnalysis/boardAnalysis.ts index ca0b01c51..40b28256d 100644 --- a/src/Go/boardAnalysis/boardAnalysis.ts +++ b/src/Go/boardAnalysis/boardAnalysis.ts @@ -696,16 +696,14 @@ export function getPreviousMove(): [number, number] | null { return null; } - for (const rowIndexString in Go.currentGame.board) { - const row = Go.currentGame.board[+rowIndexString] ?? []; - for (const pointIndexString in row) { - const point = row[+pointIndexString]; - const priorColor = point && priorBoard && getColorOnBoardString(priorBoard, point.x, point.y); + for (const [rowIndex, row] of Go.currentGame.board.entries()) { + for (const [pointIndex, point] of row.entries()) { + const priorColor = point && getColorOnBoardString(priorBoard, point.x, point.y); const currentColor = point?.color; const isPreviousPlayer = currentColor === Go.currentGame.previousPlayer; const isChanged = priorColor !== currentColor; if (priorColor && currentColor && isPreviousPlayer && isChanged) { - return [+rowIndexString, +pointIndexString]; + return [rowIndex, pointIndex]; } } } diff --git a/src/Go/boardAnalysis/goAI.ts b/src/Go/boardAnalysis/goAI.ts index 38567b17d..a2d1298f0 100644 --- a/src/Go/boardAnalysis/goAI.ts +++ b/src/Go/boardAnalysis/goAI.ts @@ -1,4 +1,4 @@ -import type { Board, BoardState, EyeMove, Move, MoveOptions, Play, PointState } from "../Types"; +import type { Board, BoardState, EyeMove, Move, MoveOptions, MoveType, Play, PointState } from "../Types"; import { Player } from "@player"; import { AugmentationName, GoColor, GoOpponent, GoPlayType } from "@enums"; @@ -153,13 +153,13 @@ export async function getMove( // If no priority move is chosen, pick one of the reasonable moves const moveOptions = [ - (await moves.growth())?.point, - (await moves.surround())?.point, - (await moves.defend())?.point, - (await moves.expansion())?.point, + moves.growth()?.point, + moves.surround()?.point, + moves.defend()?.point, + moves.expansion()?.point, (await moves.pattern())?.point, - (await moves.eyeMove())?.point, - (await moves.eyeBlock())?.point, + moves.eyeMove()?.point, + moves.eyeBlock()?.point, ] .filter(isNotNullish) .filter((point) => evaluateIfMoveIsValid(boardState, point.x, point.y, player, false)); @@ -221,12 +221,12 @@ function isSmart(faction: GoOpponent, rng: number) { async function getNetburnersPriorityMove(moves: MoveOptions, rng: number): Promise { if (rng < 0.2) { return getIlluminatiPriorityMove(moves, rng); - } else if (rng < 0.4 && (await moves.expansion())) { - return (await moves.expansion())?.point ?? null; - } else if (rng < 0.6 && (await moves.growth())) { - return (await moves.growth())?.point ?? null; + } else if (rng < 0.4 && moves.expansion()) { + return moves.expansion()?.point ?? null; + } else if (rng < 0.6 && moves.growth()) { + return moves.growth()?.point ?? null; } else if (rng < 0.75) { - return (await moves.random())?.point ?? null; + return moves.random()?.point ?? null; } return null; @@ -242,10 +242,10 @@ async function getSlumSnakesPriorityMove(moves: MoveOptions, rng: number): Promi if (rng < 0.2) { return getIlluminatiPriorityMove(moves, rng); - } else if (rng < 0.6 && (await moves.growth())) { - return (await moves.growth())?.point ?? null; + } else if (rng < 0.6 && moves.growth()) { + return moves.growth()?.point ?? null; } else if (rng < 0.65) { - return (await moves.random())?.point ?? null; + return moves.random()?.point ?? null; } return null; @@ -260,7 +260,7 @@ async function getBlackHandPriorityMove(moves: MoveOptions, rng: number): Promis return (await moves.capture())?.point ?? null; } - const surround = await moves.surround(); + const surround = moves.surround(); if (surround && surround.point && (surround.newLibertyCount ?? 999) <= 1) { //console.debug("surround move chosen"); @@ -282,7 +282,7 @@ async function getBlackHandPriorityMove(moves: MoveOptions, rng: number): Promis } else if (rng < 0.75 && surround) { return surround.point; } else if (rng < 0.8) { - return (await moves.random())?.point ?? null; + return moves.random()?.point ?? null; } return null; @@ -307,7 +307,7 @@ async function getTetradPriorityMove(moves: MoveOptions, rng: number): Promise

m, - ).length; + const hasMoves = [moves.eyeMove(), moves.eyeBlock(), moves.growth(), moves.defend, surround].filter((m) => m).length; const usePattern = rng > 0.25 || !hasMoves; if ((await moves.pattern()) && usePattern) { @@ -381,9 +379,9 @@ async function getIlluminatiPriorityMove(moves: MoveOptions, rng: number): Promi return (await moves.pattern())?.point ?? null; } - if (rng > 0.4 && (await moves.jump())) { + if (rng > 0.4 && moves.jump()) { //console.debug("Jump move chosen"); - return (await moves.jump())?.point ?? null; + return moves.jump()?.point ?? null; } if (rng < 0.6 && surround && surround.point && (surround?.newLibertyCount ?? 9) <= 2) { @@ -508,7 +506,7 @@ function getDisputedTerritoryMoves(board: Board, availableSpaces: PointState[], /** * Finds all moves that increases the liberties of the player's pieces, making them harder to capture and occupy more space on the board. */ -async function getLibertyGrowthMoves(board: Board, player: GoColor, availableSpaces: PointState[]) { +function getLibertyGrowthMoves(board: Board, player: GoColor, availableSpaces: PointState[]) { const friendlyChains = getAllChains(board).filter((chain) => chain[0].color === player); if (!friendlyChains.length) { @@ -551,8 +549,8 @@ async function getLibertyGrowthMoves(board: Board, player: GoColor, availableSpa /** * Find a move that increases the player's liberties by the maximum amount */ -async function getGrowthMove(board: Board, player: GoColor, availableSpaces: PointState[], rng: number) { - const growthMoves = await getLibertyGrowthMoves(board, player, availableSpaces); +function getGrowthMove(board: Board, player: GoColor, availableSpaces: PointState[], rng: number) { + const growthMoves = getLibertyGrowthMoves(board, player, availableSpaces); const maxLibertyCount = Math.max(...growthMoves.map((l) => l.newLibertyCount - l.oldLibertyCount)); @@ -563,8 +561,8 @@ async function getGrowthMove(board: Board, player: GoColor, availableSpaces: Poi /** * Find a move that specifically increases a chain's liberties from 1 to more than 1, preventing capture */ -async function getDefendMove(board: Board, player: GoColor, availableSpaces: PointState[]) { - const growthMoves = await getLibertyGrowthMoves(board, player, availableSpaces); +function getDefendMove(board: Board, player: GoColor, availableSpaces: PointState[]) { + const growthMoves = getLibertyGrowthMoves(board, player, availableSpaces); const libertyIncreases = growthMoves?.filter((move) => move.oldLibertyCount <= 1 && move.newLibertyCount > move.oldLibertyCount) ?? []; @@ -582,7 +580,7 @@ async function getDefendMove(board: Board, player: GoColor, availableSpaces: Poi * Find a move that reduces the opponent's liberties as much as possible, * capturing (or making it easier to capture) their pieces */ -async function getSurroundMove(board: Board, player: GoColor, availableSpaces: PointState[], smart = true) { +function getSurroundMove(board: Board, player: GoColor, availableSpaces: PointState[], smart = true) { const opposingPlayer = player === GoColor.black ? GoColor.white : GoColor.black; const enemyChains = getAllChains(board).filter((chain) => chain[0].color === opposingPlayer); @@ -741,12 +739,7 @@ function getEyeBlockingMove(board: Board, player: GoColor, availablePoints: Poin /** * Gets a group of reasonable moves based on the current board state, to be passed to the factions' AI to decide on */ -function getMoveOptions( - boardState: BoardState, - player: GoColor, - rng: number, - smart = true, -): { [s in keyof MoveOptions]: () => Promise } { +function getMoveOptions(boardState: BoardState, player: GoColor, rng: number, smart = true) { const board = boardState.board; const availableSpaces = findDisputedTerritory(boardState, player, smart); const contestedPoints = getDisputedTerritoryMoves(board, availableSpaces); @@ -756,7 +749,7 @@ function getMoveOptions( // needlessly extend the game, unless they actually can change the score const endGameAvailable = !contestedPoints.length && boardState.passCount; - const moveOptions: { [s in keyof MoveOptions]: Move | null | undefined } = { + const moveOptions: { [s in MoveType]: Move | null | undefined } = { capture: undefined, defendCapture: undefined, eyeMove: undefined, @@ -771,7 +764,7 @@ function getMoveOptions( random: undefined, }; - const moveOptionGetters: { [s in keyof MoveOptions]: () => Promise } = { + const moveOptionGetters: MoveOptions = { capture: async () => { const surroundMove = await retrieveMoveOption("surround"); return surroundMove && surroundMove?.newLibertyCount === 0 ? surroundMove : null; @@ -785,30 +778,30 @@ function getMoveOptions( ? defendMove : null; }, - eyeMove: async () => (endGameAvailable ? null : getEyeCreationMove(board, player, availableSpaces) ?? null), - eyeBlock: async () => (endGameAvailable ? null : getEyeBlockingMove(board, player, availableSpaces) ?? null), + eyeMove: () => (endGameAvailable ? null : getEyeCreationMove(board, player, availableSpaces) ?? null), + eyeBlock: () => (endGameAvailable ? null : getEyeBlockingMove(board, player, availableSpaces) ?? null), pattern: async () => { const point = endGameAvailable ? null : await findAnyMatchedPatterns(board, player, availableSpaces, smart, rng); return point ? { point } : null; }, - growth: async () => (endGameAvailable ? null : (await getGrowthMove(board, player, availableSpaces, rng)) ?? null), - expansion: async () => (await getExpansionMove(board, availableSpaces, rng, expansionMoves)) ?? null, - jump: async () => (await getJumpMove(board, player, availableSpaces, rng, expansionMoves)) ?? null, - defend: async () => (await getDefendMove(board, player, availableSpaces)) ?? null, - surround: async () => (await getSurroundMove(board, player, availableSpaces, smart)) ?? null, - corner: async () => { + growth: () => (endGameAvailable ? null : getGrowthMove(board, player, availableSpaces, rng) ?? null), + expansion: () => getExpansionMove(board, availableSpaces, rng, expansionMoves) ?? null, + jump: () => getJumpMove(board, player, availableSpaces, rng, expansionMoves) ?? null, + defend: () => getDefendMove(board, player, availableSpaces) ?? null, + surround: () => getSurroundMove(board, player, availableSpaces, smart) ?? null, + corner: () => { const point = getCornerMove(board); return point ? { point } : null; }, - random: async () => { + random: () => { // Only offer a random move if there are some contested spaces on the board. // (Random move should not be picked if the AI would otherwise pass turn.) const point = contestedPoints.length ? availableSpaces[Math.floor(rng * availableSpaces.length)] : null; return point ? { point } : null; }, - }; + } as const; - async function retrieveMoveOption(id: keyof typeof moveOptions): Promise { + async function retrieveMoveOption(id: MoveType): Promise { await waitCycle(); if (moveOptions[id] !== undefined) { return moveOptions[id] ?? null; diff --git a/src/Go/boardAnalysis/patternMatching.ts b/src/Go/boardAnalysis/patternMatching.ts index 6ed25f2bc..fb49af534 100644 --- a/src/Go/boardAnalysis/patternMatching.ts +++ b/src/Go/boardAnalysis/patternMatching.ts @@ -2,8 +2,8 @@ import type { Board, PointState } from "../Types"; import { GoColor } from "@enums"; -import { sleep } from "./goAI"; import { findEffectiveLibertiesOfNewMove } from "./boardAnalysis"; +import { sleep } from "./goAI"; export const threeByThreePatterns = [ // 3x3 piece patterns; X,O are color pieces; x,o are any state except the opposite color piece; diff --git a/src/Go/boardState/goStyles.ts b/src/Go/boardState/goStyles.ts index b2385b2e0..84bd4f9f9 100644 --- a/src/Go/boardState/goStyles.ts +++ b/src/Go/boardState/goStyles.ts @@ -24,7 +24,7 @@ const fadeLoop = keyframes` } `; -export const pointStyle = makeStyles({ uniqId: "pointStyle" })( +export const pointStyle = makeStyles({ uniqId: "pointStyle" })( (theme: Theme, _, classes) => ({ hover: {}, valid: {}, @@ -396,7 +396,7 @@ export const pointStyle = makeStyles }), ); -export const boardStyles = makeStyles({ uniqId: "boardStyles" })( +export const boardStyles = makeStyles({ uniqId: "boardStyles" })( (theme: Theme, _, classes) => ({ tab: { paddingTop: 0, diff --git a/src/Go/effects/netscriptGoImplementation.ts b/src/Go/effects/netscriptGoImplementation.ts index 0bbca27e6..006c8c4af 100644 --- a/src/Go/effects/netscriptGoImplementation.ts +++ b/src/Go/effects/netscriptGoImplementation.ts @@ -472,7 +472,11 @@ export function cheatRemoveRouter( successRngOverride?: number, ejectRngOverride?: number, ): Promise { - const point = Go.currentGame.board[x][y]!; + const point = Go.currentGame.board[x][y]; + if (!point) { + logger(`Cheat failed. The point ${x},${y} is already offline.`); + return Go.nextTurn; + } return determineCheatSuccess( logger, () => { @@ -498,8 +502,13 @@ export function cheatPlayTwoMoves( successRngOverride?: number, ejectRngOverride?: number, ): Promise { - const point1 = Go.currentGame.board[x1][y1]!; - const point2 = Go.currentGame.board[x2][y2]!; + const point1 = Go.currentGame.board[x1][y1]; + const point2 = Go.currentGame.board[x2][y2]; + + if (!point1 || !point2) { + logger(`Cheat failed. One of the points ${x1},${y1} or ${x2},${y2} is already offline.`); + return Go.nextTurn; + } return determineCheatSuccess( logger, diff --git a/src/Go/ui/GoGameboard.tsx b/src/Go/ui/GoGameboard.tsx index 7f369f82d..f20e4afa2 100644 --- a/src/Go/ui/GoGameboard.tsx +++ b/src/Go/ui/GoGameboard.tsx @@ -11,7 +11,7 @@ import { getAllValidMoves, getControlledSpace } from "../boardAnalysis/boardAnal interface GoGameboardProps { boardState: BoardState; traditional: boolean; - clickHandler: (x: number, y: number) => any; + clickHandler: (x: number, y: number) => unknown; hover: boolean; } @@ -27,7 +27,7 @@ export function GoGameboard({ boardState, traditional, clickHandler, hover }: Go } const boardSize = boardState.board[0].length; - const { classes } = boardStyles(); + const { classes } = boardStyles({}); return ( diff --git a/src/Go/ui/GoGameboardWrapper.tsx b/src/Go/ui/GoGameboardWrapper.tsx index 1cce3e597..c39be745f 100644 --- a/src/Go/ui/GoGameboardWrapper.tsx +++ b/src/Go/ui/GoGameboardWrapper.tsx @@ -49,7 +49,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps const [scoreExplanationOpen, setScoreExplanationOpen] = useState(false); const [searchOpen, setSearchOpen] = useState(false); - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const boardSize = boardState.board[0].length; const currentPlayer = boardState.previousPlayer === GoColor.white ? GoColor.black : GoColor.white; const waitingOnAI = boardState.previousPlayer === GoColor.black && boardState.ai !== GoOpponent.none; diff --git a/src/Go/ui/GoHistoryPage.tsx b/src/Go/ui/GoHistoryPage.tsx index 13d416bdc..597312be8 100644 --- a/src/Go/ui/GoHistoryPage.tsx +++ b/src/Go/ui/GoHistoryPage.tsx @@ -16,7 +16,7 @@ import { getRecordKeys } from "../../Types/Record"; export const GoHistoryPage = (): React.ReactElement => { useRerender(400); - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const priorBoard = Go.previousGame ?? getNewBoardState(7); const score = getScore(priorBoard); const opponent = priorBoard.ai; diff --git a/src/Go/ui/GoInstructionsPage.tsx b/src/Go/ui/GoInstructionsPage.tsx index 2a20f7acc..1c02c665d 100644 --- a/src/Go/ui/GoInstructionsPage.tsx +++ b/src/Go/ui/GoInstructionsPage.tsx @@ -70,7 +70,7 @@ const makeTwoEyesChallenge = ( ); export const GoInstructionsPage = (): React.ReactElement => { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); return (

<> diff --git a/src/Go/ui/GoPoint.tsx b/src/Go/ui/GoPoint.tsx index 6c5e944f3..48a9a71de 100644 --- a/src/Go/ui/GoPoint.tsx +++ b/src/Go/ui/GoPoint.tsx @@ -19,7 +19,7 @@ interface GoPointProps { } export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwner }: GoPointProps): React.ReactElement { - const { classes } = pointStyle(); + const { classes } = pointStyle({}); const currentPoint = state.board[x]?.[y]; const player = currentPoint?.color; diff --git a/src/Go/ui/GoRoot.tsx b/src/Go/ui/GoRoot.tsx index a4d64e3af..1cdfc495b 100644 --- a/src/Go/ui/GoRoot.tsx +++ b/src/Go/ui/GoRoot.tsx @@ -9,7 +9,7 @@ import { GoGameboardWrapper } from "./GoGameboardWrapper"; import { boardStyles } from "../boardState/goStyles"; export function GoRoot(): React.ReactElement { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const [value, setValue] = React.useState(0); function handleChange(event: React.SyntheticEvent, tab: number): void { diff --git a/src/Go/ui/GoScoreExplanation.tsx b/src/Go/ui/GoScoreExplanation.tsx index 107a5e3d4..d48789cbf 100644 --- a/src/Go/ui/GoScoreExplanation.tsx +++ b/src/Go/ui/GoScoreExplanation.tsx @@ -10,7 +10,7 @@ interface Props { } export const GoScoreExplanation = ({ open, onClose }: Props): React.ReactElement => { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); return ( diff --git a/src/Go/ui/GoScoreModal.tsx b/src/Go/ui/GoScoreModal.tsx index 7d25f8467..9bdd89026 100644 --- a/src/Go/ui/GoScoreModal.tsx +++ b/src/Go/ui/GoScoreModal.tsx @@ -25,7 +25,7 @@ export const GoScoreModal = ({ showScoreExplanation, opponent, }: Props): React.ReactElement => { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const blackScore = finalScore[GoColor.black]; const whiteScore = finalScore[GoColor.white]; diff --git a/src/Go/ui/GoScorePowerSummary.tsx b/src/Go/ui/GoScorePowerSummary.tsx index e102e3815..4b420f887 100644 --- a/src/Go/ui/GoScorePowerSummary.tsx +++ b/src/Go/ui/GoScorePowerSummary.tsx @@ -18,7 +18,7 @@ interface Props { } export const GoScorePowerSummary = ({ finalScore, opponent }: Props) => { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const status = getOpponentStats(opponent); const winStreak = status.winStreak; const oldWinStreak = status.winStreak; diff --git a/src/Go/ui/GoScoreSummaryTable.tsx b/src/Go/ui/GoScoreSummaryTable.tsx index f89429704..299800c4a 100644 --- a/src/Go/ui/GoScoreSummaryTable.tsx +++ b/src/Go/ui/GoScoreSummaryTable.tsx @@ -12,7 +12,7 @@ interface GoScoreSummaryTableProps { } export const GoScoreSummaryTable = ({ score, opponent }: GoScoreSummaryTableProps) => { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const blackScore = score[GoColor.black]; const whiteScore = score[GoColor.white]; const blackPlayerName = opponent === GoOpponent.none ? GoColor.black : "You"; diff --git a/src/Go/ui/GoStatusPage.tsx b/src/Go/ui/GoStatusPage.tsx index 0ea2c48f8..bbca67e4d 100644 --- a/src/Go/ui/GoStatusPage.tsx +++ b/src/Go/ui/GoStatusPage.tsx @@ -13,7 +13,7 @@ import { GoOpponent } from "@enums"; export const GoStatusPage = (): React.ReactElement => { useRerender(400); - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const score = getScore(Go.currentGame); const opponent = Go.currentGame.ai; const playedOpponentList = getRecordKeys(Go.stats).filter((o) => o !== GoOpponent.none); diff --git a/src/Go/ui/GoSubnetSearch.tsx b/src/Go/ui/GoSubnetSearch.tsx index ac73081b1..ca3ec67b5 100644 --- a/src/Go/ui/GoSubnetSearch.tsx +++ b/src/Go/ui/GoSubnetSearch.tsx @@ -21,7 +21,7 @@ interface IProps { const boardSizeOptions = boardSizes.filter((size) => size !== 19); export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProps): React.ReactElement => { - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const [opponent, setOpponent] = useState(Go.currentGame?.ai ?? GoOpponent.SlumSnakes); const preselectedBoardSize = opponent === GoOpponent.w0r1d_d43m0n ? 19 : Math.min(Go.currentGame?.board?.[0]?.length ?? 7, 13); diff --git a/src/Go/ui/GoTutorialChallenge.tsx b/src/Go/ui/GoTutorialChallenge.tsx index 53186223e..0776d6928 100644 --- a/src/Go/ui/GoTutorialChallenge.tsx +++ b/src/Go/ui/GoTutorialChallenge.tsx @@ -33,7 +33,7 @@ export function GoTutorialChallenge({ incorrectText2, }: IProps): React.ReactElement { const stateRef = useRef(getStateCopy(state)); - const { classes } = boardStyles(); + const { classes } = boardStyles({}); const [displayText, setDisplayText] = useState(description); const [showReset, setShowReset] = useState(false); diff --git a/test/jest/Go/NetscriptGo.test.ts b/test/jest/Go/NetscriptGo.test.ts index 2b3af3d5c..469382666 100644 --- a/test/jest/Go/NetscriptGo.test.ts +++ b/test/jest/Go/NetscriptGo.test.ts @@ -53,7 +53,7 @@ describe("Netscript Go API unit tests", () => { throw new Error("Invalid"); }); - await makePlayerMove(mockLogger, mockError, 0, 0).catch((_) => _); + await makePlayerMove(mockLogger, mockError, 0, 0).catch(() => {}); expect(mockError).toHaveBeenCalledWith("Invalid move: 0 0. That node is already occupied by a piece."); }); @@ -95,7 +95,7 @@ describe("Netscript Go API unit tests", () => { }); describe("getGameState() tests", () => { - it("should correctly retrieve the current game state", async () => { + it("should correctly retrieve the current game state", () => { const board = ["OXX..", ".....", "..#..", "...XX", "...X."]; const boardState = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.black); boardState.previousBoards = ["OX.........#.....XX...X."]; @@ -240,7 +240,7 @@ describe("Netscript Go API unit tests", () => { }); }); describe("cheatPlayTwoMoves() tests", () => { - it("should handle invalid moves", async () => { + it("should handle invalid moves", () => { const board = ["XOO..", ".....", ".....", ".....", "....."]; Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white); const mockError = jest.fn(); @@ -289,7 +289,7 @@ describe("Netscript Go API unit tests", () => { }); }); describe("cheatRemoveRouter() tests", () => { - it("should handle invalid moves", async () => { + it("should handle invalid moves", () => { const board = ["XOO..", ".....", ".....", ".....", "....."]; Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white); const mockError = jest.fn(); @@ -327,7 +327,7 @@ describe("Netscript Go API unit tests", () => { }); }); describe("cheatRepairOfflineNode() tests", () => { - it("should handle invalid moves", async () => { + it("should handle invalid moves", () => { const board = ["XOO..", ".....", ".....", ".....", "....#"]; Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white); const mockError = jest.fn(); diff --git a/test/jest/Go/boardAnalysis.test.ts b/test/jest/Go/boardAnalysis.test.ts index 97df6b678..928ba190e 100644 --- a/test/jest/Go/boardAnalysis.test.ts +++ b/test/jest/Go/boardAnalysis.test.ts @@ -6,13 +6,15 @@ import { getAllValidMoves, boardStateFromSimpleBoard, evaluateIfMoveIsValid, + getPreviousMove, } from "../../../src/Go/boardAnalysis/boardAnalysis"; import { findAnyMatchedPatterns } from "../../../src/Go/boardAnalysis/patternMatching"; +import { Go } from "../../../src/Go/Go"; setPlayer(new PlayerObject()); describe("Go board analysis tests", () => { - it("identifies chains and liberties", async () => { + it("identifies chains and liberties", () => { const board = ["XOO..", ".....", ".....", ".....", "....."]; const boardState = boardStateFromSimpleBoard(board); @@ -20,7 +22,7 @@ describe("Go board analysis tests", () => { expect(boardState.board[0]?.[1]?.liberties?.length).toEqual(3); }); - it("identifies all points that are part of 'eyes' on the board", async () => { + it("identifies all points that are part of 'eyes' on the board", () => { const board = ["..O..", "OOOOO", "..XXX", "..XX.", "..X.X"]; const boardState = boardStateFromSimpleBoard(board); @@ -46,7 +48,7 @@ describe("Go board analysis tests", () => { expect(point?.y).toEqual(2); }); - it("identifies invalid moves from self-capture", async () => { + it("identifies invalid moves from self-capture", () => { const board = [".X...", "X....", ".....", ".....", "....."]; const boardState = boardStateFromSimpleBoard(board); const validity = evaluateIfMoveIsValid(boardState, 0, 0, GoColor.white, false); @@ -54,7 +56,7 @@ describe("Go board analysis tests", () => { expect(validity).toEqual(GoValidity.noSuicide); }); - it("identifies invalid moves from repeat", async () => { + it("identifies invalid moves from repeat", () => { const board = [".X...", ".....", ".....", ".....", "....."]; const boardState = boardStateFromSimpleBoard(board); boardState.previousBoards.push(".X......................."); @@ -65,4 +67,12 @@ describe("Go board analysis tests", () => { expect(validity).toEqual(GoValidity.boardRepeated); }); + + it("identifies the previous move made, based on the board history", () => { + const board = [".XXO.", ".....", ".....", ".....", "....."]; + Go.currentGame = boardStateFromSimpleBoard(board); + Go.currentGame.previousBoards.push("..XO....................."); + + expect(getPreviousMove()).toEqual([0, 1]); + }); }); diff --git a/test/jest/Go/boardState.test.ts b/test/jest/Go/boardState.test.ts index c1f2d77d4..40e100014 100644 --- a/test/jest/Go/boardState.test.ts +++ b/test/jest/Go/boardState.test.ts @@ -13,13 +13,14 @@ describe("Board analysis utility tests", () => { .filter((p) => p === "O").length; expect(whitePieceCount).toEqual(1); expect(result).toEqual({ - board: expect.any(Object), + board: result.board, // This board state is different every run, due to random offline nodes and handicap placement previousPlayer: GoColor.white, previousBoards: [], ai: GoOpponent.Illuminati, passCount: 0, cheatCount: 0, }); + expect(result.board?.length).toEqual(5); }); it("Correctly applies the board size and handicap for the special opponent", () => {