feat : add progress bar

This commit is contained in:
GuillaumeSD
2024-03-08 23:56:29 +01:00
parent 91b56fde60
commit 81cbc2ca80
7 changed files with 72 additions and 27 deletions

View File

@@ -0,0 +1,39 @@
import {
Grid,
LinearProgress,
LinearProgressProps,
Typography,
} from "@mui/material";
const LinearProgressBar = (
props: LinearProgressProps & { value: number; label: string }
) => {
if (props.value === 0) return null;
return (
<Grid item container alignItems="center" justifyContent="center" xs={12}>
<Typography variant="caption" align="center">
{props.label}
</Typography>
<Grid
item
container
xs={12}
alignItems="center"
justifyContent="center"
wrap="nowrap"
>
<Grid item sx={{ width: "100%", mr: 2 }}>
<LinearProgress variant="determinate" {...props} />
</Grid>
<Grid item sx={{ minWidth: 35 }}>
<Typography variant="body2" color="text.secondary">{`${Math.round(
props.value
)}%`}</Typography>
</Grid>
</Grid>
</Grid>
);
};
export default LinearProgressBar;

View File

@@ -100,8 +100,10 @@ export abstract class UciEngine {
uciMoves, uciMoves,
depth = 16, depth = 16,
multiPv = this.multiPv, multiPv = this.multiPv,
setEvaluationProgress,
}: EvaluateGameParams): Promise<GameEval> { }: EvaluateGameParams): Promise<GameEval> {
this.throwErrorIfNotReady(); this.throwErrorIfNotReady();
setEvaluationProgress?.(1);
await this.setMultiPv(multiPv); await this.setMultiPv(multiPv);
this.ready = false; this.ready = false;
@@ -126,6 +128,9 @@ export abstract class UciEngine {
} }
const result = await this.evaluatePosition(fen, depth); const result = await this.evaluatePosition(fen, depth);
positions.push(result); positions.push(result);
setEvaluationProgress?.(
Math.max((fens.indexOf(fen) / fens.length) * 100 - 5, 2)
);
} }
const positionsWithClassification = getMovesClassification( const positionsWithClassification = getMovesClassification(
@@ -136,6 +141,7 @@ export abstract class UciEngine {
const accuracy = computeAccuracy(positions); const accuracy = computeAccuracy(positions);
this.ready = true; this.ready = true;
setEvaluationProgress?.(99);
return { return {
positions: positionsWithClassification, positions: positionsWithClassification,
accuracy, accuracy,

View File

@@ -1,12 +1,12 @@
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
import { useState } from "react";
import { import {
engineDepthAtom, engineDepthAtom,
engineMultiPvAtom, engineMultiPvAtom,
evaluationProgressAtom,
gameAtom, gameAtom,
gameEvalAtom, gameEvalAtom,
} from "../states"; } from "../states";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import { getEvaluateGameParams } from "@/lib/chess"; import { getEvaluateGameParams } from "@/lib/chess";
import { useGameDatabase } from "@/hooks/useGameDatabase"; import { useGameDatabase } from "@/hooks/useGameDatabase";
import { LoadingButton } from "@mui/lab"; import { LoadingButton } from "@mui/lab";
@@ -16,36 +16,33 @@ import { logAnalyticsEvent } from "@/lib/firebase";
export default function AnalyzeButton() { export default function AnalyzeButton() {
const engine = useEngine(EngineName.Stockfish16); const engine = useEngine(EngineName.Stockfish16);
const [evaluationInProgress, setEvaluationInProgress] = useState(false); const [evaluationProgress, setEvaluationProgress] = useAtom(
evaluationProgressAtom
);
const engineDepth = useAtomValue(engineDepthAtom); const engineDepth = useAtomValue(engineDepthAtom);
const engineMultiPv = useAtomValue(engineMultiPvAtom); const engineMultiPv = useAtomValue(engineMultiPvAtom);
const { setGameEval, gameFromUrl } = useGameDatabase(); const { setGameEval, gameFromUrl } = useGameDatabase();
const setEval = useSetAtom(gameEvalAtom); const [gameEval, setEval] = useAtom(gameEvalAtom);
const game = useAtomValue(gameAtom); const game = useAtomValue(gameAtom);
const readyToAnalyse = const readyToAnalyse =
engine?.isReady() && game.history().length > 0 && !evaluationInProgress; engine?.isReady() && game.history().length > 0 && !evaluationProgress;
const handleAnalyze = async () => { const handleAnalyze = async () => {
const params = getEvaluateGameParams(game); const params = getEvaluateGameParams(game);
if ( if (!engine?.isReady() || params.fens.length === 0 || evaluationProgress) {
!engine?.isReady() ||
params.fens.length === 0 ||
evaluationInProgress
) {
return; return;
} }
setEvaluationInProgress(true);
const newGameEval = await engine.evaluateGame({ const newGameEval = await engine.evaluateGame({
...params, ...params,
depth: engineDepth, depth: engineDepth,
multiPv: engineMultiPv, multiPv: engineMultiPv,
setEvaluationProgress,
}); });
setEval(newGameEval); setEval(newGameEval);
setEvaluationInProgress(false); setEvaluationProgress(0);
if (gameFromUrl) { if (gameFromUrl) {
setGameEval(gameFromUrl.id, newGameEval); setGameEval(gameFromUrl.id, newGameEval);
@@ -59,26 +56,17 @@ export default function AnalyzeButton() {
}); });
}; };
if (evaluationProgress) return null;
return ( return (
<LoadingButton <LoadingButton
variant="contained" variant="contained"
size="small" size="small"
startIcon={ startIcon={<Icon icon="streamline:magnifying-glass-solid" />}
!evaluationInProgress && (
<Icon icon="streamline:magnifying-glass-solid" />
)
}
onClick={handleAnalyze} onClick={handleAnalyze}
disabled={!readyToAnalyse} disabled={!readyToAnalyse}
loading={evaluationInProgress}
loadingPosition={evaluationInProgress ? "end" : undefined}
endIcon={
evaluationInProgress && (
<Icon icon="streamline:magnifying-glass-solid" />
)
}
> >
{evaluationInProgress ? "Analyzing..." : "Analyze"} {gameEval ? "Analyze again" : "Analyze"}
</LoadingButton> </LoadingButton>
); );
} }

View File

@@ -3,8 +3,13 @@ import { Grid, Typography } from "@mui/material";
import GamePanel from "./gamePanel"; import GamePanel from "./gamePanel";
import LoadGame from "./loadGame"; import LoadGame from "./loadGame";
import AnalyzeButton from "./analyzeButton"; import AnalyzeButton from "./analyzeButton";
import LinearProgressBar from "@/components/LinearProgressBar";
import { useAtomValue } from "jotai";
import { evaluationProgressAtom } from "../states";
export default function ReviewPanelHeader() { export default function ReviewPanelHeader() {
const evaluationProgress = useAtomValue(evaluationProgressAtom);
return ( return (
<Grid <Grid
item item
@@ -36,11 +41,12 @@ export default function ReviewPanelHeader() {
justifyContent="center" justifyContent="center"
alignItems="center" alignItems="center"
rowGap={3} rowGap={3}
columnGap={15} columnGap={12}
> >
<GamePanel /> <GamePanel />
<LoadGame /> <LoadGame />
<AnalyzeButton /> <AnalyzeButton />
<LinearProgressBar value={evaluationProgress} label="Analyzing..." />
</Grid> </Grid>
</Grid> </Grid>
); );

View File

@@ -4,6 +4,7 @@ import { useChessActions } from "@/hooks/useChessActions";
import { import {
boardAtom, boardAtom,
boardOrientationAtom, boardOrientationAtom,
evaluationProgressAtom,
gameAtom, gameAtom,
gameEvalAtom, gameEvalAtom,
} from "../states"; } from "../states";
@@ -20,6 +21,7 @@ export default function LoadGame() {
const { gameFromUrl } = useGameDatabase(); const { gameFromUrl } = useGameDatabase();
const setEval = useSetAtom(gameEvalAtom); const setEval = useSetAtom(gameEvalAtom);
const setBoardOrientation = useSetAtom(boardOrientationAtom); const setBoardOrientation = useSetAtom(boardOrientationAtom);
const evaluationProgress = useAtomValue(evaluationProgressAtom);
const resetAndSetGamePgn = useCallback( const resetAndSetGamePgn = useCallback(
(pgn: string) => { (pgn: string) => {
@@ -48,6 +50,8 @@ export default function LoadGame() {
const isGameLoaded = gameFromUrl !== undefined || !!game.header().White; const isGameLoaded = gameFromUrl !== undefined || !!game.header().White;
if (evaluationProgress) return null;
return ( return (
<LoadGameButton <LoadGameButton
label={isGameLoaded ? "Load another game" : "Load game"} label={isGameLoaded ? "Load another game" : "Load game"}

View File

@@ -13,3 +13,4 @@ export const showPlayerMoveIconAtom = atom(true);
export const engineDepthAtom = atom(16); export const engineDepthAtom = atom(16);
export const engineMultiPvAtom = atom(3); export const engineMultiPvAtom = atom(3);
export const evaluationProgressAtom = atom(0);

View File

@@ -52,4 +52,5 @@ export interface EvaluateGameParams {
uciMoves: string[]; uciMoves: string[];
depth?: number; depth?: number;
multiPv?: number; multiPv?: number;
setEvaluationProgress?: (value: number) => void;
} }