feat : add evaluation bar

This commit is contained in:
GuillaumeSD
2024-02-25 00:35:02 +01:00
parent 1f748f99ca
commit 892004f0d0
7 changed files with 158 additions and 26 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) };
};

View File

@@ -40,7 +40,16 @@ export default function GameReport() {
alignItems="center"
marginTop={1}
>
<Board />
<Grid
item
container
justifyContent="center"
alignItems="center"
xs={12}
md={6}
>
<Board />
</Grid>
<Grid
item

View 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>
);
}

View File

@@ -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,34 +71,38 @@ export default function Board() {
}, [currentMove, showBestMoveArrow, showPlayerMoveArrow]);
return (
<Grid
item
container
rowGap={2}
justifyContent="center"
alignItems="center"
xs={12}
md={6}
>
<PlayerInfo color={boardOrientation ? "black" : "white"} />
<Grid item container justifyContent="center" alignItems="center" xs={12}>
<EvaluationBar height={boardRef?.current?.offsetWidth} />
<Grid
item
container
rowGap={2}
justifyContent="center"
alignItems="center"
maxWidth={"80vh"}
xs={11}
>
<Chessboard
id="AnalysisBoard"
position={board.fen()}
onPieceDrop={onPieceDrop}
boardOrientation={boardOrientation ? "white" : "black"}
customArrows={customArrows}
/>
</Grid>
<PlayerInfo color={boardOrientation ? "black" : "white"} />
<PlayerInfo color={boardOrientation ? "white" : "black"} />
<Grid
item
container
justifyContent="center"
alignItems="center"
ref={boardRef}
>
<Chessboard
id="AnalysisBoard"
position={board.fen()}
onPieceDrop={onPieceDrop}
boardOrientation={boardOrientation ? "white" : "black"}
customArrows={customArrows}
customBoardStyle={{ borderRadius: "5px" }}
/>
</Grid>
<PlayerInfo color={boardOrientation ? "white" : "black"} />
</Grid>
</Grid>
);
}

View File

@@ -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);