diff --git a/src/client/api.ts b/src/client/api.ts index 7f3443c4..d458c158 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -9,10 +9,9 @@ import { useQuery } from "@tanstack/react-query"; /** * A wrapper around `useQuery` which causes the wrapped query to be trigged once each time the page renders. */ -export function useEffectQuery( +export function useEffectQuery( queryKey: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - queryFn: () => Promise, + queryFn: () => Promise, retry?: boolean | number, ) { // id guarantees query is the same every time diff --git a/src/client/game/game-end-dialog.tsx b/src/client/game/game-end-dialog.tsx index d4c0cfa6..c3fab8c7 100644 --- a/src/client/game/game-end-dialog.tsx +++ b/src/client/game/game-end-dialog.tsx @@ -81,10 +81,12 @@ function gameOverIcon(reason: GameEndReason, side: Side) { // check which side won const whiteWon = reason === GameFinishedReason.BLACK_CHECKMATED || - reason === GameInterruptedReason.BLACK_RESIGNED; + reason === GameInterruptedReason.BLACK_RESIGNED || + reason === GameFinishedReason.PUZZLE_SOLVED; const blackWon = reason === GameFinishedReason.WHITE_CHECKMATED || - reason === GameInterruptedReason.WHITE_RESIGNED; + reason === GameInterruptedReason.WHITE_RESIGNED || + reason === GameFinishedReason.PUZZLE_SOLVED; // checks which side is asking and assigns win/lost accordingly const won = side === Side.WHITE ? whiteWon : blackWon; @@ -133,6 +135,8 @@ function gameOverMessage(reason: GameEndReason) { return "Checkmate - White Wins"; case GameFinishedReason.STALEMATE: return "Draw - Stalemate"; + case GameFinishedReason.PUZZLE_SOLVED: + return "Puzzle Solved"; case GameFinishedReason.THREEFOLD_REPETITION: return "Draw - Threefold Repetition"; case GameFinishedReason.INSUFFICIENT_MATERIAL: diff --git a/src/client/game/game.tsx b/src/client/game/game.tsx index 88d872ef..c76c51ee 100644 --- a/src/client/game/game.tsx +++ b/src/client/game/game.tsx @@ -1,9 +1,11 @@ import { Dispatch, useState } from "react"; import { + GameEndMessage, GameFinishedMessage, GameHoldMessage, GameInterruptedMessage, + SetChessMessage, } from "../../common/message/game-message"; import { MoveMessage } from "../../common/message/game-message"; import { @@ -35,14 +37,28 @@ function getMessageHandler( chess: ChessEngine, setChess: Dispatch, setGameInterruptedReason: Dispatch, + setGameEndedReason: Dispatch, setGameHoldReason: Dispatch, ): MessageHandler { return (message) => { if (message instanceof MoveMessage) { // Must be a new instance of ChessEngine to trigger UI redraw - setChess(chess.copy(message.move)); + // short wait so the pieces don't teleport into place + setTimeout(() => { + setChess(chess.copy(message.move)); + }, 500); + } else if (message instanceof SetChessMessage) { + const fen = message.chess; + if (fen) { + setTimeout(() => { + chess.loadFen(fen); + setChess(chess.copy()); + }, 500); + } } else if (message instanceof GameInterruptedMessage) { setGameInterruptedReason(message.reason); + } else if (message instanceof GameEndMessage) { + setGameEndedReason(message.reason); } else if (message instanceof GameHoldMessage) { setGameHoldReason(message.reason); } @@ -57,6 +73,7 @@ export function Game(): JSX.Element { const [chess, setChess] = useState(new ChessEngine()); const [gameInterruptedReason, setGameInterruptedReason] = useState(); + const [gameEndedReason, setGameEndedReason] = useState(); const [gameHoldReason, setGameHoldReason] = useState(); const [rotation, setRotation] = useState(0); @@ -66,6 +83,7 @@ export function Game(): JSX.Element { chess, setChess, setGameInterruptedReason, + setGameEndedReason, setGameHoldReason, ), ); @@ -103,7 +121,9 @@ export function Game(): JSX.Element { // check if the game has ended or been interrupted let gameEndReason: GameEndReason | undefined = undefined; const gameFinishedReason = chess.getGameFinishedReason(); - if (gameFinishedReason !== undefined) { + if (gameEndedReason !== undefined) { + gameEndReason = gameEndedReason; + } else if (gameFinishedReason !== undefined) { sendMessage(new GameFinishedMessage(gameFinishedReason)); gameEndReason = gameFinishedReason; } else if (gameInterruptedReason !== undefined) { @@ -115,7 +135,6 @@ export function Game(): JSX.Element { gameEndReason !== undefined ? : null; - const gameOfferDialog = gameHoldReason !== undefined ? gameHoldReason === GameHoldReason.DRAW_CONFIRMATION ? @@ -142,6 +161,8 @@ export function Game(): JSX.Element {
diff --git a/src/client/game/navbar-menu.tsx b/src/client/game/navbar-menu.tsx index 9a46c3a0..4c5cc545 100644 --- a/src/client/game/navbar-menu.tsx +++ b/src/client/game/navbar-menu.tsx @@ -28,6 +28,8 @@ import "../colors.css"; interface NavbarMenuProps { sendMessage: SendMessage; side: Side; + difficulty?: string; + aiDifficulty?: number; setRotation: Dispatch>; //set state type } @@ -40,6 +42,20 @@ interface NavbarMenuProps { export function NavbarMenu(props: NavbarMenuProps): JSX.Element { // Store react router state for game const navigate = useNavigate(); + const difficultyButton = + props.difficulty ? +