CODEBASE: Fix lint errors 4 (#1773)

Co-authored-by: Michael Ficocelli <ficocemt@gmail.com>
This commit is contained in:
catloversg 2024-11-14 22:47:35 +07:00 committed by GitHub
parent 4f84a894eb
commit 97ca8c5f5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 123 additions and 99 deletions

@ -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 it's the AI's turn, initiate their turn, which will populate nextTurn
if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) { if (currentGame.previousPlayer === GoColor.black && currentGame.ai !== GoOpponent.none) {
makeAIMove(currentGame).catch((error) => { 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 // 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<GoOpponent, OpponentStats> | s
const entries = Object.entries(stats); const entries = Object.entries(stats);
for (const [opponent, opponentStats] of entries) { for (const [opponent, opponentStats] of entries) {
if (!getEnumHelper("GoOpponent").isMember(opponent)) return `Invalid opponent in Go.stats: ${opponent}`; 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"; if (!opponentStats || typeof opponentStats !== "object") return "Non-object encountered for an opponent's stats";
assertLoadingType<OpponentStats>(opponentStats); assertLoadingType<OpponentStats>(opponentStats as object);
const { favor, highestWinStreak, losses, nodes, wins, oldWinStreak, winStreak, nodePower } = opponentStats; const { favor, highestWinStreak, losses, nodes, wins, oldWinStreak, winStreak, nodePower } =
opponentStats as OpponentStats;
// Integers >= 0. Todo: make a better helper for this. // 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(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"; 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)) { if (!simpleBoard.every((column) => typeof column === "string" && column.length === requiredSize)) {
return "Incorrect types or column size while loading a SimpleBoard."; return "Incorrect types or column size while loading a SimpleBoard.";
} }
return simpleBoard; return simpleBoard as SimpleBoard;
} }
function loadStoredCycles(storedCycles: unknown): number { function loadStoredCycles(storedCycles: unknown): number {

@ -11,7 +11,7 @@ export type Move = {
createsLife?: boolean; createsLife?: boolean;
}; };
type MoveType = export type MoveType =
| "capture" | "capture"
| "defendCapture" | "defendCapture"
| "eyeMove" | "eyeMove"
@ -25,8 +25,20 @@ type MoveType =
| "corner" | "corner"
| "random"; | "random";
type MoveFunction = () => Promise<Move | null>; export type MoveOptions = {
export type MoveOptions = Record<MoveType, MoveFunction>; readonly eyeMove: () => Move | null;
readonly random: () => Move | null;
readonly defendCapture: () => Promise<Move | null>;
readonly corner: () => Move | null;
readonly defend: () => Move | null;
readonly pattern: () => Promise<Move | null>;
readonly capture: () => Promise<Move | null>;
readonly growth: () => Move | null;
readonly eyeBlock: () => Move | null;
readonly surround: () => Move | null;
readonly expansion: () => Move | null;
readonly jump: () => Move | null;
};
export type EyeMove = { export type EyeMove = {
point: PointState; point: PointState;

@ -696,16 +696,14 @@ export function getPreviousMove(): [number, number] | null {
return null; return null;
} }
for (const rowIndexString in Go.currentGame.board) { for (const [rowIndex, row] of Go.currentGame.board.entries()) {
const row = Go.currentGame.board[+rowIndexString] ?? []; for (const [pointIndex, point] of row.entries()) {
for (const pointIndexString in row) { const priorColor = point && getColorOnBoardString(priorBoard, point.x, point.y);
const point = row[+pointIndexString];
const priorColor = point && priorBoard && getColorOnBoardString(priorBoard, point.x, point.y);
const currentColor = point?.color; const currentColor = point?.color;
const isPreviousPlayer = currentColor === Go.currentGame.previousPlayer; const isPreviousPlayer = currentColor === Go.currentGame.previousPlayer;
const isChanged = priorColor !== currentColor; const isChanged = priorColor !== currentColor;
if (priorColor && currentColor && isPreviousPlayer && isChanged) { if (priorColor && currentColor && isPreviousPlayer && isChanged) {
return [+rowIndexString, +pointIndexString]; return [rowIndex, pointIndex];
} }
} }
} }

@ -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 { Player } from "@player";
import { AugmentationName, GoColor, GoOpponent, GoPlayType } from "@enums"; 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 // If no priority move is chosen, pick one of the reasonable moves
const moveOptions = [ const moveOptions = [
(await moves.growth())?.point, moves.growth()?.point,
(await moves.surround())?.point, moves.surround()?.point,
(await moves.defend())?.point, moves.defend()?.point,
(await moves.expansion())?.point, moves.expansion()?.point,
(await moves.pattern())?.point, (await moves.pattern())?.point,
(await moves.eyeMove())?.point, moves.eyeMove()?.point,
(await moves.eyeBlock())?.point, moves.eyeBlock()?.point,
] ]
.filter(isNotNullish) .filter(isNotNullish)
.filter((point) => evaluateIfMoveIsValid(boardState, point.x, point.y, player, false)); .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<PointState | null> { async function getNetburnersPriorityMove(moves: MoveOptions, rng: number): Promise<PointState | null> {
if (rng < 0.2) { if (rng < 0.2) {
return getIlluminatiPriorityMove(moves, rng); return getIlluminatiPriorityMove(moves, rng);
} else if (rng < 0.4 && (await moves.expansion())) { } else if (rng < 0.4 && moves.expansion()) {
return (await moves.expansion())?.point ?? null; return moves.expansion()?.point ?? null;
} else if (rng < 0.6 && (await moves.growth())) { } else if (rng < 0.6 && moves.growth()) {
return (await moves.growth())?.point ?? null; return moves.growth()?.point ?? null;
} else if (rng < 0.75) { } else if (rng < 0.75) {
return (await moves.random())?.point ?? null; return moves.random()?.point ?? null;
} }
return null; return null;
@ -242,10 +242,10 @@ async function getSlumSnakesPriorityMove(moves: MoveOptions, rng: number): Promi
if (rng < 0.2) { if (rng < 0.2) {
return getIlluminatiPriorityMove(moves, rng); return getIlluminatiPriorityMove(moves, rng);
} else if (rng < 0.6 && (await moves.growth())) { } else if (rng < 0.6 && moves.growth()) {
return (await moves.growth())?.point ?? null; return moves.growth()?.point ?? null;
} else if (rng < 0.65) { } else if (rng < 0.65) {
return (await moves.random())?.point ?? null; return moves.random()?.point ?? null;
} }
return null; return null;
@ -260,7 +260,7 @@ async function getBlackHandPriorityMove(moves: MoveOptions, rng: number): Promis
return (await moves.capture())?.point ?? null; return (await moves.capture())?.point ?? null;
} }
const surround = await moves.surround(); const surround = moves.surround();
if (surround && surround.point && (surround.newLibertyCount ?? 999) <= 1) { if (surround && surround.point && (surround.newLibertyCount ?? 999) <= 1) {
//console.debug("surround move chosen"); //console.debug("surround move chosen");
@ -282,7 +282,7 @@ async function getBlackHandPriorityMove(moves: MoveOptions, rng: number): Promis
} else if (rng < 0.75 && surround) { } else if (rng < 0.75 && surround) {
return surround.point; return surround.point;
} else if (rng < 0.8) { } else if (rng < 0.8) {
return (await moves.random())?.point ?? null; return moves.random()?.point ?? null;
} }
return null; return null;
@ -307,7 +307,7 @@ async function getTetradPriorityMove(moves: MoveOptions, rng: number): Promise<P
return (await moves.pattern())?.point ?? null; return (await moves.pattern())?.point ?? null;
} }
const surround = await moves.surround(); const surround = moves.surround();
if (surround && surround.point && (surround?.newLibertyCount ?? 9) <= 1) { if (surround && surround.point && (surround?.newLibertyCount ?? 9) <= 1) {
//console.debug("surround move chosen"); //console.debug("surround move chosen");
return surround.point; return surround.point;
@ -350,30 +350,28 @@ async function getIlluminatiPriorityMove(moves: MoveOptions, rng: number): Promi
return (await moves.defendCapture())?.point ?? null; return (await moves.defendCapture())?.point ?? null;
} }
if (await moves.eyeMove()) { if (moves.eyeMove()) {
//console.debug("Create eye move chosen"); //console.debug("Create eye move chosen");
return (await moves.eyeMove())?.point ?? null; return moves.eyeMove()?.point ?? null;
} }
const surround = await moves.surround(); const surround = moves.surround();
if (surround && surround.point && (surround?.newLibertyCount ?? 9) <= 1) { if (surround && surround.point && (surround?.newLibertyCount ?? 9) <= 1) {
//console.debug("surround move chosen"); //console.debug("surround move chosen");
return surround.point; return surround.point;
} }
if (await moves.eyeBlock()) { if (moves.eyeBlock()) {
//console.debug("Block eye move chosen"); //console.debug("Block eye move chosen");
return (await moves.eyeBlock())?.point ?? null; return moves.eyeBlock()?.point ?? null;
} }
if (await moves.corner()) { if (moves.corner()) {
//console.debug("Corner move chosen"); //console.debug("Corner move chosen");
return (await moves.corner())?.point ?? null; return moves.corner()?.point ?? null;
} }
const hasMoves = [await moves.eyeMove(), await moves.eyeBlock(), await moves.growth(), moves.defend, surround].filter( const hasMoves = [moves.eyeMove(), moves.eyeBlock(), moves.growth(), moves.defend, surround].filter((m) => m).length;
(m) => m,
).length;
const usePattern = rng > 0.25 || !hasMoves; const usePattern = rng > 0.25 || !hasMoves;
if ((await moves.pattern()) && usePattern) { if ((await moves.pattern()) && usePattern) {
@ -381,9 +379,9 @@ async function getIlluminatiPriorityMove(moves: MoveOptions, rng: number): Promi
return (await moves.pattern())?.point ?? null; return (await moves.pattern())?.point ?? null;
} }
if (rng > 0.4 && (await moves.jump())) { if (rng > 0.4 && moves.jump()) {
//console.debug("Jump move chosen"); //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) { 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. * 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); const friendlyChains = getAllChains(board).filter((chain) => chain[0].color === player);
if (!friendlyChains.length) { 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 * Find a move that increases the player's liberties by the maximum amount
*/ */
async function getGrowthMove(board: Board, player: GoColor, availableSpaces: PointState[], rng: number) { function getGrowthMove(board: Board, player: GoColor, availableSpaces: PointState[], rng: number) {
const growthMoves = await getLibertyGrowthMoves(board, player, availableSpaces); const growthMoves = getLibertyGrowthMoves(board, player, availableSpaces);
const maxLibertyCount = Math.max(...growthMoves.map((l) => l.newLibertyCount - l.oldLibertyCount)); 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 * 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[]) { function getDefendMove(board: Board, player: GoColor, availableSpaces: PointState[]) {
const growthMoves = await getLibertyGrowthMoves(board, player, availableSpaces); const growthMoves = getLibertyGrowthMoves(board, player, availableSpaces);
const libertyIncreases = const libertyIncreases =
growthMoves?.filter((move) => move.oldLibertyCount <= 1 && move.newLibertyCount > move.oldLibertyCount) ?? []; 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, * Find a move that reduces the opponent's liberties as much as possible,
* capturing (or making it easier to capture) their pieces * 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 opposingPlayer = player === GoColor.black ? GoColor.white : GoColor.black;
const enemyChains = getAllChains(board).filter((chain) => chain[0].color === opposingPlayer); 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 * Gets a group of reasonable moves based on the current board state, to be passed to the factions' AI to decide on
*/ */
function getMoveOptions( function getMoveOptions(boardState: BoardState, player: GoColor, rng: number, smart = true) {
boardState: BoardState,
player: GoColor,
rng: number,
smart = true,
): { [s in keyof MoveOptions]: () => Promise<Move | null> } {
const board = boardState.board; const board = boardState.board;
const availableSpaces = findDisputedTerritory(boardState, player, smart); const availableSpaces = findDisputedTerritory(boardState, player, smart);
const contestedPoints = getDisputedTerritoryMoves(board, availableSpaces); const contestedPoints = getDisputedTerritoryMoves(board, availableSpaces);
@ -756,7 +749,7 @@ function getMoveOptions(
// needlessly extend the game, unless they actually can change the score // needlessly extend the game, unless they actually can change the score
const endGameAvailable = !contestedPoints.length && boardState.passCount; 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, capture: undefined,
defendCapture: undefined, defendCapture: undefined,
eyeMove: undefined, eyeMove: undefined,
@ -771,7 +764,7 @@ function getMoveOptions(
random: undefined, random: undefined,
}; };
const moveOptionGetters: { [s in keyof MoveOptions]: () => Promise<Move | null> } = { const moveOptionGetters: MoveOptions = {
capture: async () => { capture: async () => {
const surroundMove = await retrieveMoveOption("surround"); const surroundMove = await retrieveMoveOption("surround");
return surroundMove && surroundMove?.newLibertyCount === 0 ? surroundMove : null; return surroundMove && surroundMove?.newLibertyCount === 0 ? surroundMove : null;
@ -785,30 +778,30 @@ function getMoveOptions(
? defendMove ? defendMove
: null; : null;
}, },
eyeMove: async () => (endGameAvailable ? null : getEyeCreationMove(board, player, availableSpaces) ?? null), eyeMove: () => (endGameAvailable ? null : getEyeCreationMove(board, player, availableSpaces) ?? null),
eyeBlock: async () => (endGameAvailable ? null : getEyeBlockingMove(board, player, availableSpaces) ?? null), eyeBlock: () => (endGameAvailable ? null : getEyeBlockingMove(board, player, availableSpaces) ?? null),
pattern: async () => { pattern: async () => {
const point = endGameAvailable ? null : await findAnyMatchedPatterns(board, player, availableSpaces, smart, rng); const point = endGameAvailable ? null : await findAnyMatchedPatterns(board, player, availableSpaces, smart, rng);
return point ? { point } : null; return point ? { point } : null;
}, },
growth: async () => (endGameAvailable ? null : (await getGrowthMove(board, player, availableSpaces, rng)) ?? null), growth: () => (endGameAvailable ? null : getGrowthMove(board, player, availableSpaces, rng) ?? null),
expansion: async () => (await getExpansionMove(board, availableSpaces, rng, expansionMoves)) ?? null, expansion: () => getExpansionMove(board, availableSpaces, rng, expansionMoves) ?? null,
jump: async () => (await getJumpMove(board, player, availableSpaces, rng, expansionMoves)) ?? null, jump: () => getJumpMove(board, player, availableSpaces, rng, expansionMoves) ?? null,
defend: async () => (await getDefendMove(board, player, availableSpaces)) ?? null, defend: () => getDefendMove(board, player, availableSpaces) ?? null,
surround: async () => (await getSurroundMove(board, player, availableSpaces, smart)) ?? null, surround: () => getSurroundMove(board, player, availableSpaces, smart) ?? null,
corner: async () => { corner: () => {
const point = getCornerMove(board); const point = getCornerMove(board);
return point ? { point } : null; return point ? { point } : null;
}, },
random: async () => { random: () => {
// Only offer a random move if there are some contested spaces on the board. // 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.) // (Random move should not be picked if the AI would otherwise pass turn.)
const point = contestedPoints.length ? availableSpaces[Math.floor(rng * availableSpaces.length)] : null; const point = contestedPoints.length ? availableSpaces[Math.floor(rng * availableSpaces.length)] : null;
return point ? { point } : null; return point ? { point } : null;
}, },
}; } as const;
async function retrieveMoveOption(id: keyof typeof moveOptions): Promise<Move | null> { async function retrieveMoveOption(id: MoveType): Promise<Move | null> {
await waitCycle(); await waitCycle();
if (moveOptions[id] !== undefined) { if (moveOptions[id] !== undefined) {
return moveOptions[id] ?? null; return moveOptions[id] ?? null;

@ -2,8 +2,8 @@
import type { Board, PointState } from "../Types"; import type { Board, PointState } from "../Types";
import { GoColor } from "@enums"; import { GoColor } from "@enums";
import { sleep } from "./goAI";
import { findEffectiveLibertiesOfNewMove } from "./boardAnalysis"; import { findEffectiveLibertiesOfNewMove } from "./boardAnalysis";
import { sleep } from "./goAI";
export const threeByThreePatterns = [ export const threeByThreePatterns = [
// 3x3 piece patterns; X,O are color pieces; x,o are any state except the opposite color piece; // 3x3 piece patterns; X,O are color pieces; x,o are any state except the opposite color piece;

@ -24,7 +24,7 @@ const fadeLoop = keyframes`
} }
`; `;
export const pointStyle = makeStyles<void, Size | Point | Structure | Highlight>({ uniqId: "pointStyle" })( export const pointStyle = makeStyles<unknown, Size | Point | Structure | Highlight>({ uniqId: "pointStyle" })(
(theme: Theme, _, classes) => ({ (theme: Theme, _, classes) => ({
hover: {}, hover: {},
valid: {}, valid: {},
@ -396,7 +396,7 @@ export const pointStyle = makeStyles<void, Size | Point | Structure | Highlight>
}), }),
); );
export const boardStyles = makeStyles<void, Size | "background">({ uniqId: "boardStyles" })( export const boardStyles = makeStyles<unknown, Size | "background">({ uniqId: "boardStyles" })(
(theme: Theme, _, classes) => ({ (theme: Theme, _, classes) => ({
tab: { tab: {
paddingTop: 0, paddingTop: 0,

@ -472,7 +472,11 @@ export function cheatRemoveRouter(
successRngOverride?: number, successRngOverride?: number,
ejectRngOverride?: number, ejectRngOverride?: number,
): Promise<Play> { ): Promise<Play> {
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( return determineCheatSuccess(
logger, logger,
() => { () => {
@ -498,8 +502,13 @@ export function cheatPlayTwoMoves(
successRngOverride?: number, successRngOverride?: number,
ejectRngOverride?: number, ejectRngOverride?: number,
): Promise<Play> { ): Promise<Play> {
const point1 = Go.currentGame.board[x1][y1]!; const point1 = Go.currentGame.board[x1][y1];
const point2 = Go.currentGame.board[x2][y2]!; 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( return determineCheatSuccess(
logger, logger,

@ -11,7 +11,7 @@ import { getAllValidMoves, getControlledSpace } from "../boardAnalysis/boardAnal
interface GoGameboardProps { interface GoGameboardProps {
boardState: BoardState; boardState: BoardState;
traditional: boolean; traditional: boolean;
clickHandler: (x: number, y: number) => any; clickHandler: (x: number, y: number) => unknown;
hover: boolean; hover: boolean;
} }
@ -27,7 +27,7 @@ export function GoGameboard({ boardState, traditional, clickHandler, hover }: Go
} }
const boardSize = boardState.board[0].length; const boardSize = boardState.board[0].length;
const { classes } = boardStyles(); const { classes } = boardStyles({});
return ( return (
<Grid container id="goGameboard" className={`${classes.board} ${traditional ? classes.traditional : ""}`}> <Grid container id="goGameboard" className={`${classes.board} ${traditional ? classes.traditional : ""}`}>

@ -49,7 +49,7 @@ export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps
const [scoreExplanationOpen, setScoreExplanationOpen] = useState(false); const [scoreExplanationOpen, setScoreExplanationOpen] = useState(false);
const [searchOpen, setSearchOpen] = useState(false); const [searchOpen, setSearchOpen] = useState(false);
const { classes } = boardStyles(); const { classes } = boardStyles({});
const boardSize = boardState.board[0].length; const boardSize = boardState.board[0].length;
const currentPlayer = boardState.previousPlayer === GoColor.white ? GoColor.black : GoColor.white; const currentPlayer = boardState.previousPlayer === GoColor.white ? GoColor.black : GoColor.white;
const waitingOnAI = boardState.previousPlayer === GoColor.black && boardState.ai !== GoOpponent.none; const waitingOnAI = boardState.previousPlayer === GoColor.black && boardState.ai !== GoOpponent.none;

@ -16,7 +16,7 @@ import { getRecordKeys } from "../../Types/Record";
export const GoHistoryPage = (): React.ReactElement => { export const GoHistoryPage = (): React.ReactElement => {
useRerender(400); useRerender(400);
const { classes } = boardStyles(); const { classes } = boardStyles({});
const priorBoard = Go.previousGame ?? getNewBoardState(7); const priorBoard = Go.previousGame ?? getNewBoardState(7);
const score = getScore(priorBoard); const score = getScore(priorBoard);
const opponent = priorBoard.ai; const opponent = priorBoard.ai;

@ -70,7 +70,7 @@ const makeTwoEyesChallenge = (
); );
export const GoInstructionsPage = (): React.ReactElement => { export const GoInstructionsPage = (): React.ReactElement => {
const { classes } = boardStyles(); const { classes } = boardStyles({});
return ( return (
<div className={classes.instructionScroller}> <div className={classes.instructionScroller}>
<> <>

@ -19,7 +19,7 @@ interface GoPointProps {
} }
export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwner }: GoPointProps): React.ReactElement { 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 currentPoint = state.board[x]?.[y];
const player = currentPoint?.color; const player = currentPoint?.color;

@ -9,7 +9,7 @@ import { GoGameboardWrapper } from "./GoGameboardWrapper";
import { boardStyles } from "../boardState/goStyles"; import { boardStyles } from "../boardState/goStyles";
export function GoRoot(): React.ReactElement { export function GoRoot(): React.ReactElement {
const { classes } = boardStyles(); const { classes } = boardStyles({});
const [value, setValue] = React.useState(0); const [value, setValue] = React.useState(0);
function handleChange(event: React.SyntheticEvent, tab: number): void { function handleChange(event: React.SyntheticEvent, tab: number): void {

@ -10,7 +10,7 @@ interface Props {
} }
export const GoScoreExplanation = ({ open, onClose }: Props): React.ReactElement => { export const GoScoreExplanation = ({ open, onClose }: Props): React.ReactElement => {
const { classes } = boardStyles(); const { classes } = boardStyles({});
return ( return (
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>

@ -25,7 +25,7 @@ export const GoScoreModal = ({
showScoreExplanation, showScoreExplanation,
opponent, opponent,
}: Props): React.ReactElement => { }: Props): React.ReactElement => {
const { classes } = boardStyles(); const { classes } = boardStyles({});
const blackScore = finalScore[GoColor.black]; const blackScore = finalScore[GoColor.black];
const whiteScore = finalScore[GoColor.white]; const whiteScore = finalScore[GoColor.white];

@ -18,7 +18,7 @@ interface Props {
} }
export const GoScorePowerSummary = ({ finalScore, opponent }: Props) => { export const GoScorePowerSummary = ({ finalScore, opponent }: Props) => {
const { classes } = boardStyles(); const { classes } = boardStyles({});
const status = getOpponentStats(opponent); const status = getOpponentStats(opponent);
const winStreak = status.winStreak; const winStreak = status.winStreak;
const oldWinStreak = status.winStreak; const oldWinStreak = status.winStreak;

@ -12,7 +12,7 @@ interface GoScoreSummaryTableProps {
} }
export const GoScoreSummaryTable = ({ score, opponent }: GoScoreSummaryTableProps) => { export const GoScoreSummaryTable = ({ score, opponent }: GoScoreSummaryTableProps) => {
const { classes } = boardStyles(); const { classes } = boardStyles({});
const blackScore = score[GoColor.black]; const blackScore = score[GoColor.black];
const whiteScore = score[GoColor.white]; const whiteScore = score[GoColor.white];
const blackPlayerName = opponent === GoOpponent.none ? GoColor.black : "You"; const blackPlayerName = opponent === GoOpponent.none ? GoColor.black : "You";

@ -13,7 +13,7 @@ import { GoOpponent } from "@enums";
export const GoStatusPage = (): React.ReactElement => { export const GoStatusPage = (): React.ReactElement => {
useRerender(400); useRerender(400);
const { classes } = boardStyles(); const { classes } = boardStyles({});
const score = getScore(Go.currentGame); const score = getScore(Go.currentGame);
const opponent = Go.currentGame.ai; const opponent = Go.currentGame.ai;
const playedOpponentList = getRecordKeys(Go.stats).filter((o) => o !== GoOpponent.none); const playedOpponentList = getRecordKeys(Go.stats).filter((o) => o !== GoOpponent.none);

@ -21,7 +21,7 @@ interface IProps {
const boardSizeOptions = boardSizes.filter((size) => size !== 19); const boardSizeOptions = boardSizes.filter((size) => size !== 19);
export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProps): React.ReactElement => { export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProps): React.ReactElement => {
const { classes } = boardStyles(); const { classes } = boardStyles({});
const [opponent, setOpponent] = useState<GoOpponent>(Go.currentGame?.ai ?? GoOpponent.SlumSnakes); const [opponent, setOpponent] = useState<GoOpponent>(Go.currentGame?.ai ?? GoOpponent.SlumSnakes);
const preselectedBoardSize = const preselectedBoardSize =
opponent === GoOpponent.w0r1d_d43m0n ? 19 : Math.min(Go.currentGame?.board?.[0]?.length ?? 7, 13); opponent === GoOpponent.w0r1d_d43m0n ? 19 : Math.min(Go.currentGame?.board?.[0]?.length ?? 7, 13);

@ -33,7 +33,7 @@ export function GoTutorialChallenge({
incorrectText2, incorrectText2,
}: IProps): React.ReactElement { }: IProps): React.ReactElement {
const stateRef = useRef(getStateCopy(state)); const stateRef = useRef(getStateCopy(state));
const { classes } = boardStyles(); const { classes } = boardStyles({});
const [displayText, setDisplayText] = useState(description); const [displayText, setDisplayText] = useState(description);
const [showReset, setShowReset] = useState(false); const [showReset, setShowReset] = useState(false);

@ -53,7 +53,7 @@ describe("Netscript Go API unit tests", () => {
throw new Error("Invalid"); 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."); 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", () => { 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 board = ["OXX..", ".....", "..#..", "...XX", "...X."];
const boardState = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.black); const boardState = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.black);
boardState.previousBoards = ["OX.........#.....XX...X."]; boardState.previousBoards = ["OX.........#.....XX...X."];
@ -240,7 +240,7 @@ describe("Netscript Go API unit tests", () => {
}); });
}); });
describe("cheatPlayTwoMoves() tests", () => { describe("cheatPlayTwoMoves() tests", () => {
it("should handle invalid moves", async () => { it("should handle invalid moves", () => {
const board = ["XOO..", ".....", ".....", ".....", "....."]; const board = ["XOO..", ".....", ".....", ".....", "....."];
Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white); Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white);
const mockError = jest.fn(); const mockError = jest.fn();
@ -289,7 +289,7 @@ describe("Netscript Go API unit tests", () => {
}); });
}); });
describe("cheatRemoveRouter() tests", () => { describe("cheatRemoveRouter() tests", () => {
it("should handle invalid moves", async () => { it("should handle invalid moves", () => {
const board = ["XOO..", ".....", ".....", ".....", "....."]; const board = ["XOO..", ".....", ".....", ".....", "....."];
Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white); Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white);
const mockError = jest.fn(); const mockError = jest.fn();
@ -327,7 +327,7 @@ describe("Netscript Go API unit tests", () => {
}); });
}); });
describe("cheatRepairOfflineNode() tests", () => { describe("cheatRepairOfflineNode() tests", () => {
it("should handle invalid moves", async () => { it("should handle invalid moves", () => {
const board = ["XOO..", ".....", ".....", ".....", "....#"]; const board = ["XOO..", ".....", ".....", ".....", "....#"];
Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white); Go.currentGame = boardStateFromSimpleBoard(board, GoOpponent.Daedalus, GoColor.white);
const mockError = jest.fn(); const mockError = jest.fn();

@ -6,13 +6,15 @@ import {
getAllValidMoves, getAllValidMoves,
boardStateFromSimpleBoard, boardStateFromSimpleBoard,
evaluateIfMoveIsValid, evaluateIfMoveIsValid,
getPreviousMove,
} from "../../../src/Go/boardAnalysis/boardAnalysis"; } from "../../../src/Go/boardAnalysis/boardAnalysis";
import { findAnyMatchedPatterns } from "../../../src/Go/boardAnalysis/patternMatching"; import { findAnyMatchedPatterns } from "../../../src/Go/boardAnalysis/patternMatching";
import { Go } from "../../../src/Go/Go";
setPlayer(new PlayerObject()); setPlayer(new PlayerObject());
describe("Go board analysis tests", () => { describe("Go board analysis tests", () => {
it("identifies chains and liberties", async () => { it("identifies chains and liberties", () => {
const board = ["XOO..", ".....", ".....", ".....", "....."]; const board = ["XOO..", ".....", ".....", ".....", "....."];
const boardState = boardStateFromSimpleBoard(board); const boardState = boardStateFromSimpleBoard(board);
@ -20,7 +22,7 @@ describe("Go board analysis tests", () => {
expect(boardState.board[0]?.[1]?.liberties?.length).toEqual(3); 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 board = ["..O..", "OOOOO", "..XXX", "..XX.", "..X.X"];
const boardState = boardStateFromSimpleBoard(board); const boardState = boardStateFromSimpleBoard(board);
@ -46,7 +48,7 @@ describe("Go board analysis tests", () => {
expect(point?.y).toEqual(2); 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 board = [".X...", "X....", ".....", ".....", "....."];
const boardState = boardStateFromSimpleBoard(board); const boardState = boardStateFromSimpleBoard(board);
const validity = evaluateIfMoveIsValid(boardState, 0, 0, GoColor.white, false); const validity = evaluateIfMoveIsValid(boardState, 0, 0, GoColor.white, false);
@ -54,7 +56,7 @@ describe("Go board analysis tests", () => {
expect(validity).toEqual(GoValidity.noSuicide); expect(validity).toEqual(GoValidity.noSuicide);
}); });
it("identifies invalid moves from repeat", async () => { it("identifies invalid moves from repeat", () => {
const board = [".X...", ".....", ".....", ".....", "....."]; const board = [".X...", ".....", ".....", ".....", "....."];
const boardState = boardStateFromSimpleBoard(board); const boardState = boardStateFromSimpleBoard(board);
boardState.previousBoards.push(".X......................."); boardState.previousBoards.push(".X.......................");
@ -65,4 +67,12 @@ describe("Go board analysis tests", () => {
expect(validity).toEqual(GoValidity.boardRepeated); 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]);
});
}); });

@ -13,13 +13,14 @@ describe("Board analysis utility tests", () => {
.filter((p) => p === "O").length; .filter((p) => p === "O").length;
expect(whitePieceCount).toEqual(1); expect(whitePieceCount).toEqual(1);
expect(result).toEqual({ 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, previousPlayer: GoColor.white,
previousBoards: [], previousBoards: [],
ai: GoOpponent.Illuminati, ai: GoOpponent.Illuminati,
passCount: 0, passCount: 0,
cheatCount: 0, cheatCount: 0,
}); });
expect(result.board?.length).toEqual(5);
}); });
it("Correctly applies the board size and handicap for the special opponent", () => { it("Correctly applies the board size and handicap for the special opponent", () => {