From d7d603404f085733855657f6d6a72bcec966f369 Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sun, 9 Aug 2026 02:56:35 +0300 Subject: [PATCH 1/4] use can choose a difficulty level when playing stockfish --- apps/client/src/components/chessSidebar.tsx | 18 ++++++++++++++++++ apps/client/src/constants/stockfish.ts | 9 +++++++++ apps/client/src/hooks/game/useComputerGame.ts | 4 +++- apps/client/src/hooks/game/useStockfish.ts | 13 ++++++++++--- apps/client/src/pages/computerGame.tsx | 12 ++++++++++-- apps/client/tsconfig.json | 3 ++- apps/client/vite.config.ts | 1 + 7 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 apps/client/src/constants/stockfish.ts diff --git a/apps/client/src/components/chessSidebar.tsx b/apps/client/src/components/chessSidebar.tsx index cb78cc2..0d9d6fd 100644 --- a/apps/client/src/components/chessSidebar.tsx +++ b/apps/client/src/components/chessSidebar.tsx @@ -1,11 +1,17 @@ import type { GameStateEvent } from "@chesslab/shared/types"; +import type { DifficultyKey } from "@constants/stockfish"; +import { difficulties } from "@constants/stockfish"; export default function SideBar({ opponent, gameState, + setDifficulty, + difficulty, }: { opponent: string | undefined; gameState: GameStateEvent | undefined; + setDifficulty: React.Dispatch>; + difficulty: DifficultyKey; }) { return (
@@ -27,6 +33,18 @@ export default function SideBar({
)} +
+ +
); } diff --git a/apps/client/src/constants/stockfish.ts b/apps/client/src/constants/stockfish.ts new file mode 100644 index 0000000..5d23687 --- /dev/null +++ b/apps/client/src/constants/stockfish.ts @@ -0,0 +1,9 @@ +export const difficulties = { + beginner: { elo: 800 }, + easy: { elo: 1100 }, + medium: { elo: 1500 }, + hard: { elo: 1900 }, + expert: { elo: 2400 }, +} as const; + +export type DifficultyKey = keyof typeof difficulties; diff --git a/apps/client/src/hooks/game/useComputerGame.ts b/apps/client/src/hooks/game/useComputerGame.ts index 4a55c4f..e5e81d7 100644 --- a/apps/client/src/hooks/game/useComputerGame.ts +++ b/apps/client/src/hooks/game/useComputerGame.ts @@ -5,8 +5,9 @@ import { useState } from "react"; import { getGameOverInfo } from "@chesslab/shared/utils"; import { useCompTimer } from "./useCompTimer"; import { playMoveSound } from "@services/sfx/index"; +import type { DifficultyKey } from "@constants/stockfish"; -export function useComputerGame() { +export function useComputerGame({ difficulty }: { difficulty: DifficultyKey }) { const [chessGame] = useState(() => new Chess()); const [chessPosition, setChessPosition] = useState(() => new Chess().fen()); const [turn, setTurn] = useState<"w" | "b">("w"); @@ -35,6 +36,7 @@ export function useComputerGame() { turn, stockfishSide: side === "w" ? "b" : "w", isGameOver: !!gameOverInfo, + difficulty, onMove: () => { setChessPosition(chessGame.fen()); setTurn(chessGame.turn()); diff --git a/apps/client/src/hooks/game/useStockfish.ts b/apps/client/src/hooks/game/useStockfish.ts index aa2dd37..dbeedcf 100644 --- a/apps/client/src/hooks/game/useStockfish.ts +++ b/apps/client/src/hooks/game/useStockfish.ts @@ -1,6 +1,7 @@ import { useEffect } from "react"; import stockfish from "@stockfish/stockfish"; import type { Chess } from "chess.js"; +import { difficulties, type DifficultyKey } from "@constants/stockfish"; export default function useStockfish({ chessGame, @@ -8,17 +9,23 @@ export default function useStockfish({ stockfishSide, isGameOver, onMove, + difficulty, }: { chessGame: Chess; turn: "w" | "b"; stockfishSide: "w" | "b"; isGameOver: boolean; onMove: () => void; + difficulty: DifficultyKey; }) { useEffect(() => { + if (turn !== stockfishSide || isGameOver) return; + stockfish.postMessage("uci"); stockfish.postMessage("isready"); stockfish.postMessage("ucinewgame"); + stockfish.postMessage("setoption name UCI_LimitStrength value true"); + stockfish.postMessage(`setoption name UCI_Elo value ${difficulties[difficulty].elo}`); stockfish.onmessage = (e) => { const message = e.data; @@ -45,12 +52,12 @@ export default function useStockfish({ stockfish.postMessage("stop"); stockfish.onmessage = null; }; - }, [chessGame, onMove, isGameOver]); + }, [chessGame, onMove, isGameOver, difficulty, stockfishSide, turn]); useEffect(() => { if (turn === stockfishSide && !isGameOver) { stockfish.postMessage(`position fen ${chessGame.fen()}`); - stockfish.postMessage("go depth 15"); + stockfish.postMessage("go depth 15 movetime 1000"); } - }, [turn, chessGame, stockfishSide, isGameOver]); + }, [turn, chessGame, stockfishSide, isGameOver, difficulty]); } diff --git a/apps/client/src/pages/computerGame.tsx b/apps/client/src/pages/computerGame.tsx index 668c2a9..b2b0fa5 100644 --- a/apps/client/src/pages/computerGame.tsx +++ b/apps/client/src/pages/computerGame.tsx @@ -2,16 +2,24 @@ import ChessBoard from "@components/chessBoard"; import { Timer } from "@components/Timer"; import SideBar from "@components/chessSidebar"; import { useComputerGame } from "@hooks/game/useComputerGame"; +import { useState } from "react"; +import type { DifficultyKey } from "@constants/stockfish"; export default function ComputerChessBoard() { - const { turn, side, timeMs, gameOverInfo, chessboardOptions } = useComputerGame(); + const [difficulty, setDifficulty] = useState("beginner"); + const { turn, side, timeMs, gameOverInfo, chessboardOptions } = useComputerGame({ difficulty }); return (
- +
); } diff --git a/apps/client/tsconfig.json b/apps/client/tsconfig.json index cc5b01b..69fa13d 100644 --- a/apps/client/tsconfig.json +++ b/apps/client/tsconfig.json @@ -22,7 +22,8 @@ "@stockfish/*": ["./src/stockfish/*"], "@app-types/*": ["./src/types/*"], "@utils/*": ["./src/utils/*"], - "@services/*": ["./src/services/*"] + "@services/*": ["./src/services/*"], + "@constants/*": ["./src/constants/*"] } }, "include": ["vite.config.ts", "src"] diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts index a7169db..c57db3d 100644 --- a/apps/client/vite.config.ts +++ b/apps/client/vite.config.ts @@ -21,6 +21,7 @@ export default defineConfig({ "@app-types": path.resolve(__dirname, "./src/types"), "@utils": path.resolve(__dirname, "./src/utils"), "@services": path.resolve(__dirname, "./src/services"), + "@constants": path.resolve(__dirname, "./src/constants"), }, }, server: { From 9847e1826c928f0d2725248d295bfb71ba90e415 Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sun, 9 Aug 2026 04:25:02 +0300 Subject: [PATCH 2/4] fix building error --- apps/client/src/components/chessSidebar.tsx | 41 ++++++++++----------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/client/src/components/chessSidebar.tsx b/apps/client/src/components/chessSidebar.tsx index 0d9d6fd..00ef1ee 100644 --- a/apps/client/src/components/chessSidebar.tsx +++ b/apps/client/src/components/chessSidebar.tsx @@ -2,17 +2,14 @@ import type { GameStateEvent } from "@chesslab/shared/types"; import type { DifficultyKey } from "@constants/stockfish"; import { difficulties } from "@constants/stockfish"; -export default function SideBar({ - opponent, - gameState, - setDifficulty, - difficulty, -}: { +interface SideBarProps { opponent: string | undefined; gameState: GameStateEvent | undefined; - setDifficulty: React.Dispatch>; - difficulty: DifficultyKey; -}) { + setDifficulty?: React.Dispatch>; + difficulty?: DifficultyKey; +} + +export default function SideBar({ opponent, gameState, setDifficulty, difficulty }: SideBarProps) { return (
@@ -33,18 +30,20 @@ export default function SideBar({
)}
-
- -
+ {setDifficulty && ( +
+ +
+ )} ); } From d1d678b534658c93ea3f4e9304c93e77647e3ed9 Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sun, 9 Aug 2026 04:30:56 +0300 Subject: [PATCH 3/4] fix(client): update Stockfish beginner and easy Elo ratings to supported minimums --- apps/client/src/constants/stockfish.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/client/src/constants/stockfish.ts b/apps/client/src/constants/stockfish.ts index 5d23687..3becf4e 100644 --- a/apps/client/src/constants/stockfish.ts +++ b/apps/client/src/constants/stockfish.ts @@ -1,6 +1,5 @@ export const difficulties = { - beginner: { elo: 800 }, - easy: { elo: 1100 }, + easy: { elo: 1320 }, medium: { elo: 1500 }, hard: { elo: 1900 }, expert: { elo: 2400 }, From 4a7f284cf0f51332e506ec43fb5b8d4d8317bdf2 Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sun, 9 Aug 2026 04:33:43 +0300 Subject: [PATCH 4/4] fix build error --- apps/client/src/pages/computerGame.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/pages/computerGame.tsx b/apps/client/src/pages/computerGame.tsx index b2b0fa5..c6583ab 100644 --- a/apps/client/src/pages/computerGame.tsx +++ b/apps/client/src/pages/computerGame.tsx @@ -6,7 +6,7 @@ import { useState } from "react"; import type { DifficultyKey } from "@constants/stockfish"; export default function ComputerChessBoard() { - const [difficulty, setDifficulty] = useState("beginner"); + const [difficulty, setDifficulty] = useState("easy"); const { turn, side, timeMs, gameOverInfo, chessboardOptions } = useComputerGame({ difficulty }); return (