feat : add evaluation bar
This commit is contained in:
@@ -17,9 +17,9 @@ export type CurrentMove = Partial<Move> & {
|
||||
lastEval?: MoveEval;
|
||||
};
|
||||
|
||||
export const useCurrentMove = () => {
|
||||
export const useCurrentMove = (engineName?: EngineName) => {
|
||||
const [currentMove, setCurrentMove] = useState<CurrentMove>({});
|
||||
const engine = useEngine(EngineName.Stockfish16);
|
||||
const engine = useEngine(engineName);
|
||||
const gameEval = useAtomValue(gameEvalAtom);
|
||||
const game = useAtomValue(gameAtom);
|
||||
const board = useAtomValue(boardAtom);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { EngineName } from "@/types/enums";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const useEngine = (engineName: EngineName) => {
|
||||
export const useEngine = (engineName: EngineName | undefined) => {
|
||||
const [engine, setEngine] = useState<UciEngine | null>(null);
|
||||
const multiPv = useAtomValue(engineMultiPvAtom);
|
||||
|
||||
@@ -17,6 +17,8 @@ export const useEngine = (engineName: EngineName) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!engineName) return;
|
||||
|
||||
const engine = pickEngine(engineName);
|
||||
engine.init().then(() => {
|
||||
setEngine(engine);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LineEval } from "@/types/eval";
|
||||
import { Game } from "@/types/game";
|
||||
import { Chess } from "chess.js";
|
||||
|
||||
@@ -77,3 +78,30 @@ export const moveLineUciToSan = (
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getEvaluationBarValue = (
|
||||
bestLine: LineEval,
|
||||
whiteToPlay: boolean
|
||||
): { whiteBarPercentage: number; label: string } => {
|
||||
if (bestLine.mate) {
|
||||
return {
|
||||
whiteBarPercentage: whiteToPlay ? 100 : 0,
|
||||
label: `M${bestLine.mate}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (!bestLine.cp) {
|
||||
return { whiteBarPercentage: 50, label: "0.0" };
|
||||
}
|
||||
|
||||
const cp = bestLine.cp;
|
||||
const whiteBarPercentage = Math.min(50 + cp / 20, 98);
|
||||
|
||||
const label = (cp / 100).toFixed(1);
|
||||
|
||||
if (label.toString().length > 3) {
|
||||
return { whiteBarPercentage, label: (cp / 100).toFixed(0) };
|
||||
}
|
||||
|
||||
return { whiteBarPercentage, label: (cp / 100).toFixed(1) };
|
||||
};
|
||||
|
||||
@@ -39,8 +39,17 @@ export default function GameReport() {
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
marginTop={1}
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
xs={12}
|
||||
md={6}
|
||||
>
|
||||
<Board />
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
item
|
||||
|
||||
86
src/sections/analysis/board/evaluationBar.tsx
Normal file
86
src/sections/analysis/board/evaluationBar.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Box, Grid, Typography } from "@mui/material";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { boardAtom, boardOrientationAtom } from "../states";
|
||||
import { getEvaluationBarValue } from "@/lib/chess";
|
||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||
|
||||
interface Props {
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export default function EvaluationBar({ height }: Props) {
|
||||
const [evalBar, setEvalBar] = useState({
|
||||
whiteBarPercentage: 50,
|
||||
label: "0.0",
|
||||
});
|
||||
const board = useAtomValue(boardAtom);
|
||||
const boardOrientation = useAtomValue(boardOrientationAtom);
|
||||
const currentMove = useCurrentMove();
|
||||
|
||||
useEffect(() => {
|
||||
const bestLine = currentMove?.eval?.lines[0];
|
||||
if (!bestLine) return;
|
||||
|
||||
const evalBar = getEvaluationBarValue(bestLine, board.turn() === "w");
|
||||
setEvalBar(evalBar);
|
||||
}, [currentMove, board.turn()]);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
xs={1}
|
||||
height={height}
|
||||
paddingX={3}
|
||||
>
|
||||
<Box
|
||||
sx={{ backgroundColor: boardOrientation ? "black" : "white" }}
|
||||
height={`${
|
||||
boardOrientation
|
||||
? 100 - evalBar.whiteBarPercentage
|
||||
: evalBar.whiteBarPercentage
|
||||
}%`}
|
||||
width="100%"
|
||||
borderRadius="5px 5px 0 0"
|
||||
>
|
||||
<Typography
|
||||
color={boardOrientation ? "white" : "black"}
|
||||
textAlign="center"
|
||||
width="100%"
|
||||
>
|
||||
{(evalBar.whiteBarPercentage < 50 && boardOrientation) ||
|
||||
(evalBar.whiteBarPercentage >= 50 && !boardOrientation)
|
||||
? evalBar.label
|
||||
: ""}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{ backgroundColor: boardOrientation ? "white" : "black" }}
|
||||
height={`${
|
||||
boardOrientation
|
||||
? evalBar.whiteBarPercentage
|
||||
: 100 - evalBar.whiteBarPercentage
|
||||
}%`}
|
||||
width={"100%"}
|
||||
display="flex"
|
||||
alignItems="flex-end"
|
||||
borderRadius="0 0 5px 5px"
|
||||
>
|
||||
<Typography
|
||||
color={boardOrientation ? "black" : "white"}
|
||||
textAlign="center"
|
||||
width="100%"
|
||||
>
|
||||
{(evalBar.whiteBarPercentage >= 50 && boardOrientation) ||
|
||||
(evalBar.whiteBarPercentage < 50 && !boardOrientation)
|
||||
? evalBar.label
|
||||
: ""}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
import { Arrow, Square } from "react-chessboard/dist/chessboard/types";
|
||||
import { useChessActions } from "@/hooks/useChess";
|
||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useRef } from "react";
|
||||
import PlayerInfo from "./playerInfo";
|
||||
import EvaluationBar from "./evaluationBar";
|
||||
|
||||
export default function Board() {
|
||||
const boardRef = useRef<HTMLDivElement>(null);
|
||||
const board = useAtomValue(boardAtom);
|
||||
const boardOrientation = useAtomValue(boardOrientationAtom);
|
||||
const showBestMoveArrow = useAtomValue(showBestMoveArrowAtom);
|
||||
@@ -69,14 +71,16 @@ export default function Board() {
|
||||
}, [currentMove, showBestMoveArrow, showPlayerMoveArrow]);
|
||||
|
||||
return (
|
||||
<Grid item container justifyContent="center" alignItems="center" xs={12}>
|
||||
<EvaluationBar height={boardRef?.current?.offsetWidth} />
|
||||
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
rowGap={2}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
xs={12}
|
||||
md={6}
|
||||
xs={11}
|
||||
>
|
||||
<PlayerInfo color={boardOrientation ? "black" : "white"} />
|
||||
|
||||
@@ -85,7 +89,7 @@ export default function Board() {
|
||||
container
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
maxWidth={"80vh"}
|
||||
ref={boardRef}
|
||||
>
|
||||
<Chessboard
|
||||
id="AnalysisBoard"
|
||||
@@ -93,10 +97,12 @@ export default function Board() {
|
||||
onPieceDrop={onPieceDrop}
|
||||
boardOrientation={boardOrientation ? "white" : "black"}
|
||||
customArrows={customArrows}
|
||||
customBoardStyle={{ borderRadius: "5px" }}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<PlayerInfo color={boardOrientation ? "white" : "black"} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@ import { boardAtom, engineMultiPvAtom, gameAtom } from "./states";
|
||||
import LineEvaluation from "./lineEvaluation";
|
||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||
import { LineEval } from "@/types/eval";
|
||||
import { EngineName } from "@/types/enums";
|
||||
|
||||
export default function ReviewPanelBody() {
|
||||
const linesNumber = useAtomValue(engineMultiPvAtom);
|
||||
const move = useCurrentMove();
|
||||
const move = useCurrentMove(EngineName.Stockfish16);
|
||||
const game = useAtomValue(gameAtom);
|
||||
const board = useAtomValue(boardAtom);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user