feat : add move classification
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
showPlayerMoveArrowAtom,
|
||||
} from "../states";
|
||||
import { Arrow, Square } from "react-chessboard/dist/chessboard/types";
|
||||
import { useChessActions } from "@/hooks/useChess";
|
||||
import { useChessActions } from "@/hooks/useChessActions";
|
||||
import { useMemo, useRef } from "react";
|
||||
import PlayerInfo from "./playerInfo";
|
||||
import EvaluationBar from "./evaluationBar";
|
||||
@@ -25,12 +25,16 @@ export default function Board() {
|
||||
const { makeMove: makeBoardMove } = useChessActions(boardAtom);
|
||||
const currentMove = useAtomValue(currentMoveAtom);
|
||||
|
||||
const onPieceDrop = (source: Square, target: Square): boolean => {
|
||||
const onPieceDrop = (
|
||||
source: Square,
|
||||
target: Square,
|
||||
piece: string
|
||||
): boolean => {
|
||||
try {
|
||||
const result = makeBoardMove({
|
||||
from: source,
|
||||
to: target,
|
||||
promotion: "q", // TODO: Let the user choose the promotion
|
||||
promotion: piece[1]?.toLowerCase() ?? "q",
|
||||
});
|
||||
|
||||
return !!result;
|
||||
@@ -42,7 +46,7 @@ export default function Board() {
|
||||
const customArrows: Arrow[] = useMemo(() => {
|
||||
const arrows: Arrow[] = [];
|
||||
|
||||
if (currentMove?.lastEval && showBestMoveArrow) {
|
||||
if (currentMove?.lastEval?.bestMove && showBestMoveArrow) {
|
||||
const bestMoveArrow = [
|
||||
currentMove.lastEval.bestMove.slice(0, 2),
|
||||
currentMove.lastEval.bestMove.slice(2, 4),
|
||||
@@ -108,7 +112,10 @@ export default function Board() {
|
||||
onPieceDrop={onPieceDrop}
|
||||
boardOrientation={boardOrientation ? "white" : "black"}
|
||||
customArrows={customArrows}
|
||||
customBoardStyle={{ borderRadius: "5px" }}
|
||||
customBoardStyle={{
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom } from "../states";
|
||||
import { useMemo } from "react";
|
||||
import { moveLineUciToSan } from "@/lib/chess";
|
||||
|
||||
export default function BestMove() {
|
||||
const move = useCurrentMove();
|
||||
const board = useAtomValue(boardAtom);
|
||||
|
||||
const bestMove = move?.lastEval?.bestMove;
|
||||
|
||||
const bestMoveSan = useMemo(() => {
|
||||
if (!bestMove) return undefined;
|
||||
|
||||
const lastPosition = board.history({ verbose: true }).at(-1)?.before;
|
||||
if (!lastPosition) return undefined;
|
||||
|
||||
return moveLineUciToSan(lastPosition)(bestMove);
|
||||
}, [bestMove, board]);
|
||||
|
||||
if (!bestMoveSan) return null;
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Typography align="center">{`${bestMoveSan} was the best move`}</Typography>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,8 @@ import { LineEval } from "@/types/eval";
|
||||
import { EngineName } from "@/types/enums";
|
||||
import EngineSettingsButton from "@/sections/engineSettings/engineSettingsButton";
|
||||
import Accuracies from "./accuracies";
|
||||
import BestMove from "./bestMove";
|
||||
import MoveInfo from "./moveInfo";
|
||||
import Opening from "./opening";
|
||||
|
||||
export default function ReviewPanelBody() {
|
||||
const linesNumber = useAtomValue(engineMultiPvAtom);
|
||||
@@ -20,7 +21,10 @@ export default function ReviewPanelBody() {
|
||||
const gameHistory = game.history();
|
||||
|
||||
const isGameOver =
|
||||
gameHistory.length > 0 && boardHistory.join() === gameHistory.join();
|
||||
boardHistory.length > 0 &&
|
||||
(board.isCheckmate() ||
|
||||
board.isDraw() ||
|
||||
boardHistory.join() === gameHistory.join());
|
||||
|
||||
const linesSkeleton: LineEval[] = Array.from({ length: linesNumber }).map(
|
||||
(_, i) => ({ pv: [`${i}`], depth: 0, multiPv: i + 1 })
|
||||
@@ -73,7 +77,9 @@ export default function ReviewPanelBody() {
|
||||
|
||||
<Accuracies />
|
||||
|
||||
<BestMove />
|
||||
<MoveInfo />
|
||||
|
||||
<Opening />
|
||||
|
||||
{isGameOver && (
|
||||
<Grid item xs={12}>
|
||||
|
||||
53
src/sections/analysis/reviewPanelBody/moveInfo.tsx
Normal file
53
src/sections/analysis/reviewPanelBody/moveInfo.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom } from "../states";
|
||||
import { useMemo } from "react";
|
||||
import { moveLineUciToSan } from "@/lib/chess";
|
||||
import { MoveClassification } from "@/types/enums";
|
||||
|
||||
export default function MoveInfo() {
|
||||
const move = useCurrentMove();
|
||||
const board = useAtomValue(boardAtom);
|
||||
|
||||
const bestMove = move?.lastEval?.bestMove;
|
||||
|
||||
const bestMoveSan = useMemo(() => {
|
||||
if (!bestMove) return undefined;
|
||||
|
||||
const lastPosition = board.history({ verbose: true }).at(-1)?.before;
|
||||
if (!lastPosition) return undefined;
|
||||
|
||||
return moveLineUciToSan(lastPosition)(bestMove);
|
||||
}, [bestMove, board]);
|
||||
|
||||
if (!bestMoveSan) return null;
|
||||
|
||||
const moveClassification = move.eval?.moveClassification;
|
||||
const moveLabel = moveClassification
|
||||
? `${move.san} is ${moveClassificationLabels[moveClassification]}`
|
||||
: null;
|
||||
|
||||
const bestMoveLabel =
|
||||
moveClassification === MoveClassification.Best ||
|
||||
moveClassification === MoveClassification.Book
|
||||
? null
|
||||
: `${bestMoveSan} was the best move`;
|
||||
|
||||
return (
|
||||
<Grid item container columnGap={5} xs={12} justifyContent="center">
|
||||
{moveLabel && <Typography align="center">{moveLabel}</Typography>}
|
||||
{bestMoveLabel && <Typography align="center">{bestMoveLabel}</Typography>}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
const moveClassificationLabels: Record<MoveClassification, string> = {
|
||||
[MoveClassification.Blunder]: "a blunder",
|
||||
[MoveClassification.Mistake]: "a mistake",
|
||||
[MoveClassification.Inaccuracy]: "an inaccuracy",
|
||||
[MoveClassification.Good]: "good",
|
||||
[MoveClassification.Excellent]: "excellent",
|
||||
[MoveClassification.Best]: "the best move",
|
||||
[MoveClassification.Book]: "an opening move",
|
||||
};
|
||||
15
src/sections/analysis/reviewPanelBody/opening.tsx
Normal file
15
src/sections/analysis/reviewPanelBody/opening.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||
import { Grid, Typography } from "@mui/material";
|
||||
|
||||
export default function Opening() {
|
||||
const move = useCurrentMove();
|
||||
|
||||
const opening = move?.eval?.opening;
|
||||
if (!opening) return null;
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Typography align="center">{opening}</Typography>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
gameEvalAtom,
|
||||
} from "../states";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { getFens } from "@/lib/chess";
|
||||
import { getEvaluateGameParams } from "@/lib/chess";
|
||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { useEngine } from "@/hooks/useEngine";
|
||||
@@ -27,18 +27,22 @@ export default function AnalyzeButton() {
|
||||
engine?.isReady() && game.history().length > 0 && !evaluationInProgress;
|
||||
|
||||
const handleAnalyze = async () => {
|
||||
const gameFens = getFens(game);
|
||||
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress) {
|
||||
const params = getEvaluateGameParams(game);
|
||||
if (
|
||||
!engine?.isReady() ||
|
||||
params.fens.length === 0 ||
|
||||
evaluationInProgress
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setEvaluationInProgress(true);
|
||||
|
||||
const newGameEval = await engine.evaluateGame(
|
||||
gameFens,
|
||||
engineDepth,
|
||||
engineMultiPv
|
||||
);
|
||||
const newGameEval = await engine.evaluateGame({
|
||||
...params,
|
||||
depth: engineDepth,
|
||||
multiPv: engineMultiPv,
|
||||
});
|
||||
|
||||
setEval(newGameEval);
|
||||
setEvaluationInProgress(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import LoadGameButton from "../../loadGame/loadGameButton";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useChessActions } from "@/hooks/useChess";
|
||||
import { useChessActions } from "@/hooks/useChessActions";
|
||||
import {
|
||||
boardAtom,
|
||||
boardOrientationAtom,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Icon } from "@iconify/react";
|
||||
import { Grid, IconButton, Tooltip } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom, gameAtom } from "../states";
|
||||
import { useChessActions } from "@/hooks/useChess";
|
||||
import { useChessActions } from "@/hooks/useChessActions";
|
||||
|
||||
export default function GoToLastPositionButton() {
|
||||
const { setPgn: setBoardPgn } = useChessActions(boardAtom);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Grid, IconButton, Tooltip } from "@mui/material";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom } from "../states";
|
||||
import { useChessActions } from "@/hooks/useChess";
|
||||
import { useChessActions } from "@/hooks/useChessActions";
|
||||
import FlipBoardButton from "./flipBoardButton";
|
||||
import NextMoveButton from "./nextMoveButton";
|
||||
import GoToLastPositionButton from "./goToLastPositionButton";
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Icon } from "@iconify/react";
|
||||
import { Grid, IconButton, Tooltip } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { boardAtom, gameAtom } from "../states";
|
||||
import { useChessActions } from "@/hooks/useChess";
|
||||
import { useChessActions } from "@/hooks/useChessActions";
|
||||
|
||||
export default function NextMoveButton() {
|
||||
const { makeMove: makeBoardMove } = useChessActions(boardAtom);
|
||||
|
||||
Reference in New Issue
Block a user