mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-05 15:17:48 +02:00
GO: Various changes before 2.6.0 (#1120)
This commit is contained in:
+40
-49
@@ -1,33 +1,26 @@
|
||||
import React, { useMemo } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import type { BoardState } from "../Types";
|
||||
|
||||
import React from "react";
|
||||
import { Grid } from "@mui/material";
|
||||
|
||||
import { GoOpponent, GoColor } from "@enums";
|
||||
import { getSizeClass, GoPoint } from "./GoPoint";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { getAllValidMoves, getControlledSpace } from "../boardAnalysis/boardAnalysis";
|
||||
import { BoardState, opponents, playerColors } from "../boardState/goConstants";
|
||||
|
||||
interface IProps {
|
||||
interface GoGameboardProps {
|
||||
boardState: BoardState;
|
||||
traditional: boolean;
|
||||
clickHandler: (x: number, y: number) => any;
|
||||
hover: boolean;
|
||||
}
|
||||
|
||||
export function GoGameboard({ boardState, traditional, clickHandler, hover }: IProps): React.ReactElement {
|
||||
useRerender(400);
|
||||
|
||||
export function GoGameboard({ boardState, traditional, clickHandler, hover }: GoGameboardProps): React.ReactElement {
|
||||
const currentPlayer =
|
||||
boardState.ai !== opponents.none || boardState.previousPlayer === playerColors.white
|
||||
? playerColors.black
|
||||
: playerColors.white;
|
||||
boardState.ai !== GoOpponent.none || boardState.previousPlayer === GoColor.white ? GoColor.black : GoColor.white;
|
||||
|
||||
const availablePoints = useMemo(
|
||||
() => (hover ? getAllValidMoves(boardState, currentPlayer) : []),
|
||||
[boardState, hover, currentPlayer],
|
||||
);
|
||||
|
||||
const ownedEmptyNodes = useMemo(() => getControlledSpace(boardState), [boardState]);
|
||||
const availablePoints = hover ? getAllValidMoves(boardState, currentPlayer) : [];
|
||||
const ownedEmptyNodes = getControlledSpace(boardState.board);
|
||||
|
||||
function pointIsValid(x: number, y: number) {
|
||||
return !!availablePoints.find((point) => point.x === x && point.y === y);
|
||||
@@ -37,37 +30,35 @@ export function GoGameboard({ boardState, traditional, clickHandler, hover }: IP
|
||||
const classes = boardStyles();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container id="goGameboard" className={`${classes.board} ${traditional ? classes.traditional : ""}`}>
|
||||
{boardState.board.map((row, y) => {
|
||||
const yIndex = boardState.board[0].length - y - 1;
|
||||
return (
|
||||
<Grid container key={`column_${yIndex}`} item className={getSizeClass(boardSize, classes)}>
|
||||
{row.map((point, x: number) => {
|
||||
const xIndex = x;
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
key={`point_${xIndex}_${yIndex}`}
|
||||
onClick={() => clickHandler(xIndex, yIndex)}
|
||||
className={getSizeClass(boardSize, classes)}
|
||||
>
|
||||
<GoPoint
|
||||
state={boardState}
|
||||
x={xIndex}
|
||||
y={yIndex}
|
||||
traditional={traditional}
|
||||
hover={hover}
|
||||
valid={pointIsValid(xIndex, yIndex)}
|
||||
emptyPointOwner={ownedEmptyNodes[xIndex]?.[yIndex]}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</>
|
||||
<Grid container id="goGameboard" className={`${classes.board} ${traditional ? classes.traditional : ""}`}>
|
||||
{boardState.board.map((column, y) => {
|
||||
const yIndex = boardState.board[0].length - y - 1;
|
||||
return (
|
||||
<Grid container key={`column_${yIndex}`} item className={getSizeClass(boardSize, classes)}>
|
||||
{column.map((point, x: number) => {
|
||||
const xIndex = x;
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
key={`point_${xIndex}_${yIndex}`}
|
||||
onClick={() => clickHandler(xIndex, yIndex)}
|
||||
className={getSizeClass(boardSize, classes)}
|
||||
>
|
||||
<GoPoint
|
||||
state={boardState}
|
||||
x={xIndex}
|
||||
y={yIndex}
|
||||
traditional={traditional}
|
||||
hover={hover}
|
||||
valid={pointIsValid(xIndex, yIndex)}
|
||||
emptyPointOwner={ownedEmptyNodes[xIndex]?.[yIndex]}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { ToastVariant } from "@enums";
|
||||
import type { BoardState } from "../Types";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Box, Button, Typography } from "@mui/material";
|
||||
|
||||
import { BoardState, opponents, playerColors, playTypes, validityReason } from "../boardState/goConstants";
|
||||
import { getNewBoardState, getStateCopy, makeMove, passTurn } from "../boardState/boardState";
|
||||
import { GoOpponent, GoColor, GoPlayType, GoValidity, ToastVariant } from "@enums";
|
||||
import { Go, GoEvents } from "../Go";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { getNewBoardState, getStateCopy, makeMove, passTurn, updateCaptures } from "../boardState/boardState";
|
||||
import { getMove } from "../boardAnalysis/goAI";
|
||||
import { bitverseArt, weiArt } from "../boardState/asciiArt";
|
||||
import { getScore, resetWinstreak } from "../boardAnalysis/scoring";
|
||||
import { evaluateIfMoveIsValid, getAllValidMoves } from "../boardAnalysis/boardAnalysis";
|
||||
import { evaluateIfMoveIsValid, getAllValidMoves, boardFromSimpleBoard } from "../boardAnalysis/boardAnalysis";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
import { OptionSwitch } from "../../ui/React/OptionSwitch";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { Player } from "@player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { GoScoreModal } from "./GoScoreModal";
|
||||
import { GoGameboard } from "./GoGameboard";
|
||||
import { GoSubnetSearch } from "./GoSubnetSearch";
|
||||
import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||
|
||||
interface IProps {
|
||||
interface GoGameboardWrapperProps {
|
||||
showInstructions: () => void;
|
||||
}
|
||||
|
||||
@@ -32,30 +33,37 @@ interface IProps {
|
||||
* play two moves that don't capture
|
||||
*/
|
||||
|
||||
export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactElement {
|
||||
const rerender = useRerender(400);
|
||||
export function GoGameboardWrapper({ showInstructions }: GoGameboardWrapperProps): React.ReactElement {
|
||||
const rerender = useRerender();
|
||||
useEffect(() => {
|
||||
const unsubscribe = GoEvents.subscribe(rerender);
|
||||
return unsubscribe;
|
||||
}, [rerender]);
|
||||
|
||||
const boardState = Player.go.boardState;
|
||||
const boardState = Go.currentGame;
|
||||
// Destructure boardState to allow useMemo to trigger correctly
|
||||
const traditional = Settings.GoTraditionalStyle;
|
||||
const [showPriorMove, setShowPriorMove] = useState(false);
|
||||
const [opponent, setOpponent] = useState<opponents>(boardState.ai);
|
||||
const [opponent, setOpponent] = useState<GoOpponent>(boardState.ai);
|
||||
const [scoreOpen, setScoreOpen] = useState(false);
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const [waitingOnAI, setWaitingOnAI] = useState(false);
|
||||
|
||||
const classes = boardStyles();
|
||||
const boardSize = boardState.board[0].length;
|
||||
const currentPlayer = boardState.previousPlayer === playerColors.white ? playerColors.black : playerColors.white;
|
||||
const currentPlayer = boardState.previousPlayer === GoColor.white ? GoColor.black : GoColor.white;
|
||||
const score = getScore(boardState);
|
||||
|
||||
// Only run this once on first component mount, to handle scenarios where the game was saved or closed while waiting on the AI to make a move
|
||||
useEffect(() => {
|
||||
if (boardState.previousPlayer === playerColors.black && !waitingOnAI) {
|
||||
takeAiTurn(Player.go.boardState);
|
||||
if (boardState.previousPlayer === GoColor.black && !waitingOnAI) {
|
||||
takeAiTurn(Go.currentGame);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Do not implement useCallback for this function without ensuring GoGameboard still rerenders for every move
|
||||
// Currently this function changing is what triggers a GoGameboard rerender, which is needed
|
||||
async function clickHandler(x: number, y: number) {
|
||||
if (showPriorMove) {
|
||||
SnackbarEvents.emit(
|
||||
@@ -68,7 +76,7 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
|
||||
// Lock the board when it isn't the player's turn
|
||||
const gameOver = boardState.previousPlayer === null;
|
||||
const notYourTurn = boardState.previousPlayer === playerColors.black && opponent !== opponents.none;
|
||||
const notYourTurn = boardState.previousPlayer === GoColor.black && opponent !== GoOpponent.none;
|
||||
if (notYourTurn) {
|
||||
SnackbarEvents.emit(`It is not your turn to play.`, ToastVariant.WARNING, 2000);
|
||||
return;
|
||||
@@ -79,65 +87,54 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
}
|
||||
|
||||
const validity = evaluateIfMoveIsValid(boardState, x, y, currentPlayer);
|
||||
if (validity != validityReason.valid) {
|
||||
if (validity != GoValidity.valid) {
|
||||
SnackbarEvents.emit(`Invalid move: ${validity}`, ToastVariant.ERROR, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedBoard = makeMove(boardState, x, y, currentPlayer);
|
||||
if (updatedBoard) {
|
||||
updateBoard(updatedBoard);
|
||||
opponent !== opponents.none && takeAiTurn(updatedBoard);
|
||||
const didUpdateBoard = makeMove(boardState, x, y, currentPlayer);
|
||||
if (didUpdateBoard) {
|
||||
rerender();
|
||||
opponent !== GoOpponent.none && takeAiTurn(boardState);
|
||||
}
|
||||
}
|
||||
|
||||
function passPlayerTurn() {
|
||||
if (boardState.previousPlayer === playerColors.white) {
|
||||
passTurn(boardState, playerColors.black);
|
||||
updateBoard(boardState);
|
||||
if (boardState.previousPlayer === GoColor.white) {
|
||||
passTurn(boardState, GoColor.black);
|
||||
rerender();
|
||||
}
|
||||
if (boardState.previousPlayer === null) {
|
||||
endGame();
|
||||
setScoreOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
opponent !== opponents.none && takeAiTurn(boardState);
|
||||
opponent !== GoOpponent.none && takeAiTurn(boardState);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async function takeAiTurn(board: BoardState) {
|
||||
if (board.previousPlayer === null) {
|
||||
return;
|
||||
}
|
||||
async function takeAiTurn(boardState: BoardState) {
|
||||
setWaitingOnAI(true);
|
||||
const initialState = getStateCopy(board);
|
||||
const move = await getMove(initialState, playerColors.white, opponent);
|
||||
const move = await getMove(boardState, GoColor.white, opponent);
|
||||
|
||||
// If a new game has started while this async code ran, just drop it
|
||||
if (boardState.history.length > Player.go.boardState.history.length) {
|
||||
return;
|
||||
}
|
||||
if (boardState !== Go.currentGame) return;
|
||||
|
||||
if (move.type === playTypes.pass) {
|
||||
if (move.type === GoPlayType.pass) {
|
||||
SnackbarEvents.emit(`The opponent passes their turn; It is now your turn to move.`, ToastVariant.WARNING, 4000);
|
||||
updateBoard(initialState);
|
||||
rerender();
|
||||
return;
|
||||
}
|
||||
|
||||
if (move.type === playTypes.gameOver || move.x === null || move.y === null) {
|
||||
endGame(initialState);
|
||||
if (move.type === GoPlayType.gameOver || move.x === null || move.y === null) {
|
||||
setScoreOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedBoard = await makeMove(initialState, move.x, move.y, playerColors.white);
|
||||
const didUpdateBoard = makeMove(boardState, move.x, move.y, GoColor.white);
|
||||
|
||||
if (updatedBoard) {
|
||||
setTimeout(() => {
|
||||
updateBoard(updatedBoard);
|
||||
setWaitingOnAI(false);
|
||||
}, 500);
|
||||
}
|
||||
if (didUpdateBoard) setWaitingOnAI(false);
|
||||
}
|
||||
|
||||
function newSubnet() {
|
||||
@@ -149,39 +146,25 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
setScoreOpen(false);
|
||||
setSearchOpen(false);
|
||||
setOpponent(newOpponent);
|
||||
if (boardState.previousPlayer !== null && boardState.history.length) {
|
||||
if (boardState.previousPlayer !== null && boardState.previousBoard) {
|
||||
resetWinstreak(boardState.ai, false);
|
||||
}
|
||||
|
||||
const newBoardState = getNewBoardState(newBoardSize, newOpponent, false);
|
||||
updateBoard(newBoardState);
|
||||
}
|
||||
|
||||
function updateBoard(initialBoardState: BoardState) {
|
||||
Player.go.boardState = getStateCopy(initialBoardState);
|
||||
Go.currentGame = getNewBoardState(newBoardSize, newOpponent, false);
|
||||
rerender();
|
||||
}
|
||||
|
||||
function endGame(state = boardState) {
|
||||
setScoreOpen(true);
|
||||
updateBoard(state);
|
||||
}
|
||||
|
||||
function getPriorMove() {
|
||||
if (!boardState.history.length) {
|
||||
return boardState;
|
||||
}
|
||||
const priorBoard = boardState.history.slice(-1)[0];
|
||||
const updatedState = getStateCopy(boardState);
|
||||
updatedState.board = priorBoard;
|
||||
updatedState.previousPlayer =
|
||||
boardState.previousPlayer === playerColors.black ? playerColors.white : playerColors.black;
|
||||
|
||||
return updatedState;
|
||||
if (!boardState.previousBoard) return boardState;
|
||||
const priorState = getStateCopy(boardState);
|
||||
priorState.previousPlayer = boardState.previousPlayer === GoColor.black ? GoColor.white : GoColor.black;
|
||||
priorState.board = boardFromSimpleBoard(boardState.previousBoard);
|
||||
updateCaptures(priorState.board, priorState.previousPlayer);
|
||||
return priorState;
|
||||
}
|
||||
|
||||
function showPreviousMove(newValue: boolean) {
|
||||
if (boardState.history.length) {
|
||||
if (boardState.previousBoard) {
|
||||
setShowPriorMove(newValue);
|
||||
}
|
||||
}
|
||||
@@ -190,16 +173,13 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
Settings.GoTraditionalStyle = newValue;
|
||||
}
|
||||
|
||||
const endGameAvailable = boardState.previousPlayer === playerColors.white && boardState.passCount;
|
||||
const noLegalMoves = useMemo(
|
||||
() => boardState.previousPlayer === playerColors.white && !getAllValidMoves(boardState, playerColors.black).length,
|
||||
[boardState],
|
||||
);
|
||||
const disablePassButton =
|
||||
opponent !== opponents.none && boardState.previousPlayer === playerColors.black && waitingOnAI;
|
||||
const endGameAvailable = boardState.previousPlayer === GoColor.white && boardState.passCount;
|
||||
const noLegalMoves =
|
||||
boardState.previousPlayer === GoColor.white && !getAllValidMoves(boardState, GoColor.black).length;
|
||||
const disablePassButton = opponent !== GoOpponent.none && boardState.previousPlayer === GoColor.black && waitingOnAI;
|
||||
|
||||
const scoreBoxText = boardState.history.length
|
||||
? `Score: Black: ${score[playerColors.black].sum} White: ${score[playerColors.white].sum}`
|
||||
const scoreBoxText = boardState.previousBoard
|
||||
? `Score: Black: ${score[GoColor.black].sum} White: ${score[GoColor.white].sum}`
|
||||
: "Place a router to begin!";
|
||||
|
||||
const getPassButtonLabel = () => {
|
||||
@@ -209,11 +189,11 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
if (boardState.previousPlayer === null) {
|
||||
return "View Final Score";
|
||||
}
|
||||
if (boardState.previousPlayer === playerColors.black && waitingOnAI) {
|
||||
if (boardState.previousPlayer === GoColor.black && waitingOnAI) {
|
||||
return "Waiting for opponent";
|
||||
}
|
||||
const currentPlayer = boardState.previousPlayer === playerColors.black ? playerColors.white : playerColors.black;
|
||||
return `Pass Turn${boardState.ai === opponents.none ? ` (${currentPlayer})` : ""}`;
|
||||
const currentPlayer = boardState.previousPlayer === GoColor.black ? GoColor.white : GoColor.black;
|
||||
return `Pass Turn${boardState.ai === GoOpponent.none ? ` (${currentPlayer})` : ""}`;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -242,8 +222,8 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
<Box className={`${classes.inlineFlexBox} ${classes.opponentTitle}`}>
|
||||
<br />
|
||||
<Typography variant={"h6"} className={classes.opponentLabel}>
|
||||
{opponent !== opponents.none ? "Subnet owner: " : ""}{" "}
|
||||
{opponent === opponents.w0r1d_d43m0n ? <CorruptableText content={opponent} spoiler={false} /> : opponent}
|
||||
{opponent !== GoOpponent.none ? "Subnet owner: " : ""}{" "}
|
||||
{opponent === GoOpponent.w0r1d_d43m0n ? <CorruptableText content={opponent} spoiler={false} /> : opponent}
|
||||
</Typography>
|
||||
<br />
|
||||
</Box>
|
||||
@@ -279,7 +259,7 @@ export function GoGameboardWrapper({ showInstructions }: IProps): React.ReactEle
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={showPriorMove}
|
||||
disabled={!boardState.history.length}
|
||||
disabled={!boardState.previousBoard}
|
||||
onChange={(newValue) => showPreviousMove(newValue)}
|
||||
text="Show previous move"
|
||||
tooltip={<>Show the board as it was before the last move</>}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from "react";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Grid, Table, TableBody, TableCell, TableRow, Tooltip } from "@mui/material";
|
||||
import { Grid, Table, TableBody, TableCell, TableRow, Tooltip, Typography } from "@mui/material";
|
||||
|
||||
import { opponentList, opponents } from "../boardState/goConstants";
|
||||
import { getPlayerStats, getScore } from "../boardAnalysis/scoring";
|
||||
import { Player } from "@player";
|
||||
import { GoOpponent } from "@enums";
|
||||
import { Go } from "../Go";
|
||||
import { getOpponentStats, getScore } from "../boardAnalysis/scoring";
|
||||
import { GoGameboard } from "./GoGameboard";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
@@ -13,15 +12,15 @@ import { formatNumber } from "../../ui/formatNumber";
|
||||
import { GoScoreSummaryTable } from "./GoScoreSummaryTable";
|
||||
import { getNewBoardState } from "../boardState/boardState";
|
||||
import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||
import { showWorldDemon } from "../boardAnalysis/goAI";
|
||||
import { getRecordKeys } from "../../Types/Record";
|
||||
|
||||
export const GoHistoryPage = (): React.ReactElement => {
|
||||
useRerender(400);
|
||||
const classes = boardStyles();
|
||||
const priorBoard = Player.go.previousGameFinalBoardState ?? getNewBoardState(7);
|
||||
const priorBoard = Go.previousGame ?? getNewBoardState(7);
|
||||
const score = getScore(priorBoard);
|
||||
const opponent = priorBoard.ai;
|
||||
const opponentsToShow = showWorldDemon() ? [...opponentList, opponents.w0r1d_d43m0n] : opponentList;
|
||||
const opponentsToShow = getRecordKeys(Go.stats);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -48,13 +47,13 @@ export const GoHistoryPage = (): React.ReactElement => {
|
||||
<Typography variant="h5">Faction Stats:</Typography>
|
||||
<Grid container style={{ maxWidth: "1020px" }}>
|
||||
{opponentsToShow.map((faction, index) => {
|
||||
const data = getPlayerStats(faction);
|
||||
const data = getOpponentStats(faction);
|
||||
return (
|
||||
<Grid item key={opponentsToShow[index]} className={classes.factionStatus}>
|
||||
<Typography>
|
||||
{" "}
|
||||
<strong className={classes.keyText}>
|
||||
{faction === opponents.w0r1d_d43m0n ? (
|
||||
{faction === GoOpponent.w0r1d_d43m0n ? (
|
||||
<CorruptableText content="????????????" spoiler={false} />
|
||||
) : (
|
||||
faction
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from "react";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { Grid, Link, Typography } from "@mui/material";
|
||||
import { getBoardFromSimplifiedBoardState } from "../boardAnalysis/boardAnalysis";
|
||||
import { opponents, playerColors } from "../boardState/goConstants";
|
||||
|
||||
import { GoOpponent, GoColor } from "@enums";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { boardStateFromSimpleBoard } from "../boardAnalysis/boardAnalysis";
|
||||
import { GoTutorialChallenge } from "./GoTutorialChallenge";
|
||||
import { Router } from "../../ui/GameRoot";
|
||||
import { Page } from "../../ui/Router";
|
||||
@@ -10,11 +11,7 @@ import { getMaxFavor } from "../effects/effect";
|
||||
|
||||
const captureChallenge = (
|
||||
<GoTutorialChallenge
|
||||
state={getBoardFromSimplifiedBoardState(
|
||||
[".....", "OX...", "OXX..", "OOX.O", "OOX.."],
|
||||
opponents.none,
|
||||
playerColors.white,
|
||||
)}
|
||||
state={boardStateFromSimpleBoard([".....", "OX...", "OXX..", "OOX.O", "OOX.."], GoOpponent.none, GoColor.white)}
|
||||
description={
|
||||
"CHALLENGE: This white network on the bottom is vulnerable! Click on the board to place a router. Capture some white pieces by cutting off their access to any empty nodes."
|
||||
}
|
||||
@@ -28,11 +25,7 @@ const captureChallenge = (
|
||||
|
||||
const saveTheNetworkChallenge = (
|
||||
<GoTutorialChallenge
|
||||
state={getBoardFromSimplifiedBoardState(
|
||||
["OO.##", "XO..#", "XX..#", "XO...", "XO..."],
|
||||
opponents.none,
|
||||
playerColors.white,
|
||||
)}
|
||||
state={boardStateFromSimpleBoard(["OO.##", "XO..#", "XX..#", "XO...", "XO..."], GoOpponent.none, GoColor.white)}
|
||||
description={
|
||||
"CHALLENGE: Your routers are in trouble! They only have one open port. Save the black network by connecting them to more empty nodes."
|
||||
}
|
||||
@@ -48,11 +41,7 @@ const saveTheNetworkChallenge = (
|
||||
|
||||
const onlyGoodMoveChallenge = (
|
||||
<GoTutorialChallenge
|
||||
state={getBoardFromSimplifiedBoardState(
|
||||
["XXO.O", "XO.O.", ".OOOO", "XXXXX", "X.X.X"],
|
||||
opponents.none,
|
||||
playerColors.white,
|
||||
)}
|
||||
state={boardStateFromSimpleBoard(["XXO.O", "XO.O.", ".OOOO", "XXXXX", "X.X.X"], GoOpponent.none, GoColor.white)}
|
||||
description={"CHALLENGE: Save the black network on the left! Connect the network to more than one empty node."}
|
||||
correctMoves={[{ x: 2, y: 0 }]}
|
||||
correctText={
|
||||
@@ -66,11 +55,7 @@ const onlyGoodMoveChallenge = (
|
||||
|
||||
const makeTwoEyesChallenge = (
|
||||
<GoTutorialChallenge
|
||||
state={getBoardFromSimplifiedBoardState(
|
||||
["XXOO.", ".XXOO", ".XXO.", ".XXOO", "XXOO."],
|
||||
opponents.none,
|
||||
playerColors.white,
|
||||
)}
|
||||
state={boardStateFromSimpleBoard(["XXOO.", ".XXOO", ".XXO.", ".XXOO", "XXOO."], GoOpponent.none, GoColor.white)}
|
||||
description={
|
||||
"CHALLENGE: The black routers are only connected to one empty-node group. Place a router such that they are connected to TWO empty node groups instead."
|
||||
}
|
||||
|
||||
+18
-22
@@ -1,31 +1,33 @@
|
||||
import type { BoardState } from "../Types";
|
||||
|
||||
import React from "react";
|
||||
import { ClassNameMap } from "@mui/styles";
|
||||
|
||||
import { BoardState, columnIndexes, playerColors } from "../boardState/goConstants";
|
||||
import { GoColor } from "@enums";
|
||||
import { columnIndexes } from "../Constants";
|
||||
import { findNeighbors } from "../boardState/boardState";
|
||||
import { pointStyle } from "../boardState/goStyles";
|
||||
import { findAdjacentLibertiesAndAlliesForPoint } from "../boardAnalysis/boardAnalysis";
|
||||
import { findAdjacentLibertiesAndAlliesForPoint, getColorOnSimpleBoard } from "../boardAnalysis/boardAnalysis";
|
||||
|
||||
interface IProps {
|
||||
interface GoPointProps {
|
||||
state: BoardState;
|
||||
x: number;
|
||||
y: number;
|
||||
traditional: boolean;
|
||||
hover: boolean;
|
||||
valid: boolean;
|
||||
emptyPointOwner: playerColors;
|
||||
emptyPointOwner: GoColor;
|
||||
}
|
||||
|
||||
export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwner }: IProps): React.ReactElement {
|
||||
export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwner }: GoPointProps): React.ReactElement {
|
||||
const classes = pointStyle();
|
||||
|
||||
const currentPoint = state.board[x]?.[y];
|
||||
const player = currentPoint?.player;
|
||||
const player = currentPoint?.color;
|
||||
|
||||
const isInAtari =
|
||||
currentPoint && currentPoint.liberties?.length === 1 && player !== playerColors.empty && !traditional;
|
||||
const liberties = player !== playerColors.empty ? findAdjacentLibertiesAndAlliesForPoint(state, x, y) : null;
|
||||
const neighbors = findNeighbors(state, x, y);
|
||||
const isInAtari = currentPoint && currentPoint.liberties?.length === 1 && player !== GoColor.empty && !traditional;
|
||||
const liberties = player !== GoColor.empty ? findAdjacentLibertiesAndAlliesForPoint(state.board, x, y) : null;
|
||||
const neighbors = findNeighbors(state.board, x, y);
|
||||
|
||||
const hasNorthLiberty = traditional ? neighbors.north : liberties?.north;
|
||||
const hasEastLiberty = traditional ? neighbors.east : liberties?.east;
|
||||
@@ -33,25 +35,19 @@ export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwne
|
||||
const hasWestLiberty = traditional ? neighbors.west : liberties?.west;
|
||||
|
||||
const pointClass =
|
||||
player === playerColors.white
|
||||
? classes.whitePoint
|
||||
: player === playerColors.black
|
||||
? classes.blackPoint
|
||||
: classes.emptyPoint;
|
||||
player === GoColor.white ? classes.whitePoint : player === GoColor.black ? classes.blackPoint : classes.emptyPoint;
|
||||
|
||||
const colorLiberty = `${player === playerColors.white ? classes.libertyWhite : classes.libertyBlack} ${
|
||||
classes.liberty
|
||||
}`;
|
||||
const colorLiberty = `${player === GoColor.white ? classes.libertyWhite : classes.libertyBlack} ${classes.liberty}`;
|
||||
|
||||
const sizeClass = getSizeClass(state.board[0].length, classes);
|
||||
|
||||
const isNewStone = state.history?.[state.history?.length - 1]?.[x]?.[y]?.player === playerColors.empty;
|
||||
const isNewStone = state.previousBoard && getColorOnSimpleBoard(state.previousBoard, x, y) === GoColor.empty;
|
||||
const isPriorMove = player === state.previousPlayer && isNewStone;
|
||||
|
||||
const emptyPointColorClass =
|
||||
emptyPointOwner === playerColors.white
|
||||
emptyPointOwner === GoColor.white
|
||||
? classes.libertyWhite
|
||||
: emptyPointOwner === playerColors.black
|
||||
: emptyPointOwner === GoColor.black
|
||||
? classes.libertyBlack
|
||||
: "";
|
||||
|
||||
@@ -70,7 +66,7 @@ export function GoPoint({ state, x, y, traditional, hover, valid, emptyPointOwne
|
||||
<div className={hasWestLiberty ? `${classes.westLiberty} ${colorLiberty}` : classes.liberty}></div>
|
||||
<div className={`${classes.innerPoint} `}>
|
||||
<div
|
||||
className={`${pointClass} ${player !== playerColors.empty ? classes.filledPoint : emptyPointColorClass}`}
|
||||
className={`${pointClass} ${player !== GoColor.empty ? classes.filledPoint : emptyPointColorClass}`}
|
||||
></div>
|
||||
</div>
|
||||
<div className={`${pointClass} ${classes.tradStone}`} />
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { Container, Tab, Tabs } from "@mui/material";
|
||||
|
||||
import { GoInstructionsPage } from "./GoInstructionsPage";
|
||||
import { BorderInnerSharp, Help, ManageSearch, History } from "@mui/icons-material";
|
||||
import { GoStatusPage } from "./GoStatusPage";
|
||||
import { GoHistoryPage } from "./GoHistoryPage";
|
||||
import { GoGameboardWrapper } from "./GoGameboardWrapper";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
|
||||
export function GoRoot(): React.ReactElement {
|
||||
const classes = boardStyles();
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||
setValue(tab);
|
||||
}
|
||||
|
||||
function showInstructions() {
|
||||
setValue(3);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Tabs variant="fullWidth" value={value} onChange={handleChange} sx={{ minWidth: "fit-content", maxWidth: "45%" }}>
|
||||
<Tab label="IPvGO Subnet" icon={<BorderInnerSharp />} iconPosition={"start"} className={classes.tab} />
|
||||
<Tab label="Status" icon={<ManageSearch />} iconPosition={"start"} className={classes.tab} />
|
||||
<Tab label="History" icon={<History />} iconPosition={"start"} className={classes.tab} />
|
||||
<Tab label="How to Play" icon={<Help />} iconPosition={"start"} className={classes.tab} />
|
||||
</Tabs>
|
||||
{value === 0 && <GoGameboardWrapper showInstructions={showInstructions} />}
|
||||
{value === 1 && <GoStatusPage />}
|
||||
{value === 2 && <GoHistoryPage />}
|
||||
{value === 3 && <GoInstructionsPage />}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
+11
-10
@@ -1,28 +1,29 @@
|
||||
import type { GoScore } from "../Types";
|
||||
import React from "react";
|
||||
import { Button, Typography } from "@mui/material";
|
||||
|
||||
import { GoOpponent, GoColor } from "@enums";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { goScore, opponents, playerColors } from "../boardState/goConstants";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { GoScorePowerSummary } from "./GoScorePowerSummary";
|
||||
import { GoScoreSummaryTable } from "./GoScoreSummaryTable";
|
||||
|
||||
interface IProps {
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
finalScore: goScore;
|
||||
finalScore: GoScore;
|
||||
newSubnet: () => void;
|
||||
opponent: opponents;
|
||||
opponent: GoOpponent;
|
||||
}
|
||||
|
||||
export const GoScoreModal = ({ open, onClose, finalScore, newSubnet, opponent }: IProps): React.ReactElement => {
|
||||
export const GoScoreModal = ({ open, onClose, finalScore, newSubnet, opponent }: Props): React.ReactElement => {
|
||||
const classes = boardStyles();
|
||||
|
||||
const blackScore = finalScore[playerColors.black];
|
||||
const whiteScore = finalScore[playerColors.white];
|
||||
const blackScore = finalScore[GoColor.black];
|
||||
const whiteScore = finalScore[GoColor.white];
|
||||
|
||||
const playerWinsText = opponent === opponents.none ? "Black wins!" : "You win!";
|
||||
const opponentWinsText = opponent === opponents.none ? "White wins!" : `Winner: ${opponent}`;
|
||||
const playerWinsText = opponent === GoOpponent.none ? "Black wins!" : "You win!";
|
||||
const opponentWinsText = opponent === GoOpponent.none ? "White wins!" : `Winner: ${opponent}`;
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
@@ -37,7 +38,7 @@ export const GoScoreModal = ({ open, onClose, finalScore, newSubnet, opponent }:
|
||||
{blackScore.sum > whiteScore.sum ? playerWinsText : opponentWinsText}
|
||||
</Typography>
|
||||
<br />
|
||||
{opponent !== opponents.none ? (
|
||||
{opponent !== GoOpponent.none ? (
|
||||
<>
|
||||
<GoScorePowerSummary opponent={opponent} finalScore={finalScore} />
|
||||
<br />
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
import type { GoScore } from "../Types";
|
||||
|
||||
import React from "react";
|
||||
import { Table, TableBody, TableCell, TableRow, Typography, Tooltip } from "@mui/material";
|
||||
|
||||
import { Player } from "@player";
|
||||
import { GoOpponent, GoColor } from "@enums";
|
||||
import { Go } from "../Go";
|
||||
import { getBonusText, getDifficultyMultiplier, getMaxFavor, getWinstreakMultiplier } from "../effects/effect";
|
||||
import { goScore, opponents, playerColors } from "../boardState/goConstants";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { formatNumber } from "../../ui/formatNumber";
|
||||
import { FactionName } from "@enums";
|
||||
import { getPlayerStats } from "../boardAnalysis/scoring";
|
||||
import { getOpponentStats } from "../boardAnalysis/scoring";
|
||||
import { getEnumHelper } from "../../utils/EnumHelper";
|
||||
|
||||
interface IProps {
|
||||
finalScore: goScore;
|
||||
opponent: opponents;
|
||||
interface Props {
|
||||
finalScore: GoScore;
|
||||
opponent: GoOpponent;
|
||||
}
|
||||
|
||||
export const GoScorePowerSummary = ({ finalScore, opponent }: IProps) => {
|
||||
export const GoScorePowerSummary = ({ finalScore, opponent }: Props) => {
|
||||
const classes = boardStyles();
|
||||
const status = getPlayerStats(opponent);
|
||||
const status = getOpponentStats(opponent);
|
||||
const winStreak = status.winStreak;
|
||||
const oldWinStreak = status.winStreak;
|
||||
const nodePower = formatNumber(status.nodePower, 2);
|
||||
const blackScore = finalScore[playerColors.black];
|
||||
const whiteScore = finalScore[playerColors.white];
|
||||
const blackScore = finalScore[GoColor.black];
|
||||
const whiteScore = finalScore[GoColor.white];
|
||||
const faction = getEnumHelper("FactionName").getMember(opponent);
|
||||
|
||||
const difficultyMultiplier = getDifficultyMultiplier(whiteScore.komi, Player.go.boardState.board[0].length);
|
||||
const difficultyMultiplier = getDifficultyMultiplier(whiteScore.komi, Go.currentGame.board[0].length);
|
||||
const winstreakMultiplier = getWinstreakMultiplier(winStreak, oldWinStreak);
|
||||
const nodePowerIncrease = formatNumber(blackScore.sum * difficultyMultiplier * winstreakMultiplier, 2);
|
||||
const showFavorGain =
|
||||
winStreak > 0 && winStreak % 2 === 0 && Player.factions.includes(opponent as unknown as FactionName);
|
||||
const showFavorGain = faction && winStreak > 0 && winStreak % 2 === 0 && Player.factions.includes(faction);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import type { GoScore } from "../Types";
|
||||
|
||||
import React from "react";
|
||||
import { Table, TableBody, TableCell, TableRow, Tooltip } from "@mui/material";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { goScore, opponents, playerColors } from "../boardState/goConstants";
|
||||
|
||||
interface IProps {
|
||||
score: goScore;
|
||||
opponent: opponents;
|
||||
import { GoOpponent, GoColor } from "@enums";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
|
||||
interface GoScoreSummaryTableProps {
|
||||
score: GoScore;
|
||||
opponent: GoOpponent;
|
||||
}
|
||||
|
||||
export const GoScoreSummaryTable = ({ score, opponent }: IProps) => {
|
||||
export const GoScoreSummaryTable = ({ score, opponent }: GoScoreSummaryTableProps) => {
|
||||
const classes = boardStyles();
|
||||
const blackScore = score[playerColors.black];
|
||||
const whiteScore = score[playerColors.white];
|
||||
const blackPlayerName = opponent === opponents.none ? "Black" : "You";
|
||||
const whitePlayerName = opponent === opponents.none ? "White" : opponent;
|
||||
const blackScore = score[GoColor.black];
|
||||
const whiteScore = score[GoColor.white];
|
||||
const blackPlayerName = opponent === GoOpponent.none ? "Black" : "You";
|
||||
const whitePlayerName = opponent === GoOpponent.none ? "White" : opponent;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import React from "react";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Grid, Table, TableBody, TableCell, TableRow, Typography } from "@mui/material";
|
||||
|
||||
import { opponentList } from "../boardState/goConstants";
|
||||
import { Go } from "../Go";
|
||||
import { getScore } from "../boardAnalysis/scoring";
|
||||
import { Player } from "@player";
|
||||
import { Grid, Table, TableBody, TableCell, TableRow } from "@mui/material";
|
||||
import { GoGameboard } from "./GoGameboard";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
import { getBonusText } from "../effects/effect";
|
||||
import { GoScoreSummaryTable } from "./GoScoreSummaryTable";
|
||||
import { getRecordKeys } from "../../Types/Record";
|
||||
|
||||
export const GoStatusPage = (): React.ReactElement => {
|
||||
useRerender(400);
|
||||
const classes = boardStyles();
|
||||
const score = getScore(Player.go.boardState);
|
||||
const opponent = Player.go.boardState.ai;
|
||||
const score = getScore(Go.currentGame);
|
||||
const opponent = Go.currentGame.ai;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -29,7 +28,7 @@ export const GoStatusPage = (): React.ReactElement => {
|
||||
<Grid item>
|
||||
<div className={classes.statusPageGameboard}>
|
||||
<GoGameboard
|
||||
boardState={Player.go.boardState}
|
||||
boardState={Go.currentGame}
|
||||
traditional={false}
|
||||
clickHandler={(x, y) => ({ x, y })}
|
||||
hover={false}
|
||||
@@ -50,7 +49,7 @@ export const GoStatusPage = (): React.ReactElement => {
|
||||
<strong>Effect:</strong>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{opponentList.map((faction, index) => {
|
||||
{getRecordKeys(Go.stats).map((faction, index) => {
|
||||
return (
|
||||
<TableRow key={index}>
|
||||
<TableCell className={classes.cellNone}>
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
import { Box, Button, MenuItem, Select, SelectChangeEvent, Tooltip, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { boardSizes, opponentDetails, opponentList, opponents } from "../boardState/goConstants";
|
||||
import { Player } from "@player";
|
||||
import { Box, Button, MenuItem, Select, SelectChangeEvent, Tooltip, Typography } from "@mui/material";
|
||||
|
||||
import { GoOpponent } from "@enums";
|
||||
import { Go } from "../Go";
|
||||
import { boardSizes, opponentDetails } from "../Constants";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { getHandicap } from "../boardState/boardState";
|
||||
import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { getPlayerStats } from "../boardAnalysis/scoring";
|
||||
import { getOpponentStats } from "../boardAnalysis/scoring";
|
||||
import { showWorldDemon } from "../boardAnalysis/goAI";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
search: (size: number, opponent: opponents) => void;
|
||||
search: (size: number, opponent: GoOpponent) => void;
|
||||
cancel: () => void;
|
||||
showInstructions: () => void;
|
||||
}
|
||||
|
||||
export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProps): React.ReactElement => {
|
||||
const classes = boardStyles();
|
||||
const [opponent, setOpponent] = useState<opponents>(Player.go.boardState?.ai ?? opponents.SlumSnakes);
|
||||
const [opponent, setOpponent] = useState<GoOpponent>(Go.currentGame?.ai ?? GoOpponent.SlumSnakes);
|
||||
const preselectedBoardSize =
|
||||
opponent === opponents.w0r1d_d43m0n ? 19 : Math.min(Player.go.boardState?.board?.[0]?.length ?? 7, 13);
|
||||
opponent === GoOpponent.w0r1d_d43m0n ? 19 : Math.min(Go.currentGame?.board?.[0]?.length ?? 7, 13);
|
||||
const [boardSize, setBoardSize] = useState(preselectedBoardSize);
|
||||
|
||||
const opponentFactions = [opponents.none, ...opponentList];
|
||||
if (showWorldDemon()) {
|
||||
opponentFactions.push(opponents.w0r1d_d43m0n);
|
||||
}
|
||||
const opponentFactions = Object.values(GoOpponent).filter(
|
||||
(opponent) => opponent !== GoOpponent.w0r1d_d43m0n || showWorldDemon(),
|
||||
);
|
||||
|
||||
const handicap = getHandicap(boardSize, opponent);
|
||||
|
||||
function changeOpponent(event: SelectChangeEvent): void {
|
||||
const newOpponent = event.target.value as opponents;
|
||||
const newOpponent = event.target.value as GoOpponent;
|
||||
setOpponent(newOpponent);
|
||||
if (newOpponent === opponents.w0r1d_d43m0n) {
|
||||
if (newOpponent === GoOpponent.w0r1d_d43m0n) {
|
||||
setBoardSize(19);
|
||||
|
||||
const stats = getPlayerStats(opponents.w0r1d_d43m0n);
|
||||
if (stats?.wins + stats?.losses === 0) {
|
||||
const stats = getOpponentStats(GoOpponent.w0r1d_d43m0n);
|
||||
if (stats.wins + stats.losses === 0) {
|
||||
Settings.GoTraditionalStyle = false;
|
||||
}
|
||||
} else if (boardSize > 13) {
|
||||
@@ -67,12 +68,12 @@ export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProp
|
||||
<br />
|
||||
<Box className={`${classes.inlineFlexBox} ${classes.opponentTitle}`}>
|
||||
<Typography className={classes.opponentLabel}>
|
||||
{opponent !== opponents.none ? "Opponent Faction: " : ""}
|
||||
{opponent !== GoOpponent.none ? "Opponent Faction: " : ""}
|
||||
</Typography>
|
||||
<Select value={opponent} onChange={changeOpponent} sx={{ mr: 1 }}>
|
||||
{opponentFactions.map((faction) => (
|
||||
<MenuItem key={faction} value={faction}>
|
||||
{faction === opponents.w0r1d_d43m0n ? (
|
||||
{faction === GoOpponent.w0r1d_d43m0n ? (
|
||||
<CorruptableText content="???????????????" spoiler={false} />
|
||||
) : (
|
||||
`${faction} (${opponentDetails[faction].description})`
|
||||
@@ -83,7 +84,7 @@ export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProp
|
||||
</Box>
|
||||
<Box className={`${classes.inlineFlexBox} ${classes.opponentTitle}`}>
|
||||
<Typography className={classes.opponentLabel}>Subnet size: </Typography>
|
||||
{opponent === opponents.w0r1d_d43m0n ? (
|
||||
{opponent === GoOpponent.w0r1d_d43m0n ? (
|
||||
<Typography>????</Typography>
|
||||
) : (
|
||||
<Select value={`${boardSize}`} onChange={changeBoardSize} sx={{ mr: 1 }}>
|
||||
@@ -118,7 +119,7 @@ export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProp
|
||||
<br />
|
||||
<Box className={`${classes.inlineFlexBox} ${classes.opponentTitle} ${classes.flavorText}`}>
|
||||
<Typography>
|
||||
{opponent === opponents.w0r1d_d43m0n ? (
|
||||
{opponent === GoOpponent.w0r1d_d43m0n ? (
|
||||
<>
|
||||
<CorruptableText content={opponentDetails[opponent].flavorText.slice(0, 40)} spoiler={false} />
|
||||
<CorruptableText content={opponentDetails[opponent].flavorText.slice(40)} spoiler={false} />
|
||||
@@ -132,7 +133,7 @@ export const GoSubnetSearch = ({ open, search, cancel, showInstructions }: IProp
|
||||
<br />
|
||||
<Box className={`${classes.inlineFlexBox} ${classes.opponentTitle}`}>
|
||||
<Typography>
|
||||
{opponent !== opponents.none ? "Faction subnet bonus:" : ""} {opponentDetails[opponent].bonusDescription}
|
||||
{opponent !== GoOpponent.none ? "Faction subnet bonus:" : ""} {opponentDetails[opponent].bonusDescription}
|
||||
</Typography>
|
||||
</Box>
|
||||
<br />
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
|
||||
import { BoardState, playerColors, validityReason } from "../boardState/goConstants";
|
||||
import { GoColor, GoValidity, ToastVariant } from "@enums";
|
||||
import { BoardState } from "../Types";
|
||||
import { GoGameboard } from "./GoGameboard";
|
||||
import { evaluateIfMoveIsValid } from "../boardAnalysis/boardAnalysis";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { ToastVariant } from "@enums";
|
||||
import { getStateCopy, makeMove } from "../boardState/boardState";
|
||||
import { boardStyles } from "../boardState/goStyles";
|
||||
|
||||
@@ -32,31 +32,27 @@ export function GoTutorialChallenge({
|
||||
incorrectMoves2,
|
||||
incorrectText2,
|
||||
}: IProps): React.ReactElement {
|
||||
const stateRef = useRef(getStateCopy(state));
|
||||
const classes = boardStyles();
|
||||
const [currentState, setCurrentState] = useState(getStateCopy(state));
|
||||
const [displayText, setDisplayText] = useState(description);
|
||||
const [showReset, setShowReset] = useState(false);
|
||||
|
||||
const handleClick = (x: number, y: number) => {
|
||||
if (currentState.history.length) {
|
||||
if (stateRef.current.previousBoard) {
|
||||
SnackbarEvents.emit(`Hit 'Reset' to try again`, ToastVariant.WARNING, 2000);
|
||||
return;
|
||||
}
|
||||
setShowReset(true);
|
||||
|
||||
const validity = evaluateIfMoveIsValid(currentState, x, y, playerColors.black);
|
||||
if (validity != validityReason.valid) {
|
||||
const validity = evaluateIfMoveIsValid(stateRef.current, x, y, GoColor.black);
|
||||
if (validity != GoValidity.valid) {
|
||||
setDisplayText(
|
||||
"Invalid move: You cannot suicide your routers by placing them with no access to any empty ports.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedBoard = makeMove(currentState, x, y, playerColors.black);
|
||||
|
||||
if (updatedBoard) {
|
||||
setCurrentState(getStateCopy(updatedBoard));
|
||||
|
||||
if (makeMove(stateRef.current, x, y, GoColor.black)) {
|
||||
if (correctMoves.find((move) => move.x === x && move.y === y)) {
|
||||
setDisplayText(correctText);
|
||||
} else if (incorrectMoves1?.find((move) => move.x === x && move.y === y)) {
|
||||
@@ -70,7 +66,7 @@ export function GoTutorialChallenge({
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setCurrentState(getStateCopy(state));
|
||||
stateRef.current = getStateCopy(state);
|
||||
setDisplayText(description);
|
||||
setShowReset(false);
|
||||
};
|
||||
@@ -78,7 +74,7 @@ export function GoTutorialChallenge({
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.instructionBoard}>
|
||||
<GoGameboard boardState={currentState} traditional={false} clickHandler={handleClick} hover={true} />
|
||||
<GoGameboard boardState={stateRef.current} traditional={false} clickHandler={handleClick} hover={true} />
|
||||
</div>
|
||||
<Typography>{displayText}</Typography>
|
||||
{showReset ? <Button onClick={reset}>Reset</Button> : ""}
|
||||
|
||||
Reference in New Issue
Block a user