feat : set depth & multipv

This commit is contained in:
GuillaumeSD
2024-02-24 01:20:39 +01:00
parent 89ca7d8f13
commit 6156e4a228
13 changed files with 318 additions and 142 deletions

View File

@@ -1,61 +0,0 @@
import { Stockfish } from "@/lib/engine/stockfish";
import { Icon } from "@iconify/react";
import { Button, Grid } from "@mui/material";
import { useEffect, useState } from "react";
import { gameAtom, gameEvalAtom } from "./states";
import { useAtomValue, useSetAtom } from "jotai";
import { getFens } from "@/lib/chess";
import { useGameDatabase } from "@/hooks/useGameDatabase";
export default function AnalyzeButton() {
const [engine, setEngine] = useState<Stockfish | null>(null);
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
const { setGameEval, gameFromUrl } = useGameDatabase();
const setEval = useSetAtom(gameEvalAtom);
const game = useAtomValue(gameAtom);
useEffect(() => {
const engine = new Stockfish();
engine.init().then(() => {
setEngine(engine);
});
return () => {
engine.shutdown();
};
}, []);
const readyToAnalyse =
engine?.isReady() && game.history().length > 0 && !evaluationInProgress;
const handleAnalyze = async () => {
const gameFens = getFens(game);
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress)
return;
setEvaluationInProgress(true);
const newGameEval = await engine.evaluateGame(gameFens);
setEval(newGameEval);
setEvaluationInProgress(false);
if (gameFromUrl) {
setGameEval(gameFromUrl.id, newGameEval);
}
};
return (
<Grid item container xs={12} justifyContent="center" alignItems="center">
<Button
variant="contained"
size="large"
startIcon={<Icon icon="streamline:magnifying-glass-solid" />}
onClick={handleAnalyze}
disabled={!readyToAnalyse}
>
Analyse
</Button>
</Grid>
);
}

View File

@@ -6,26 +6,16 @@ import LineEvaluation from "./lineEvaluation";
import { useCurrentMove } from "@/hooks/useCurrentMove";
export default function ReviewPanelBody() {
const move = useCurrentMove();
const game = useAtomValue(gameAtom);
const board = useAtomValue(boardAtom);
const move = useCurrentMove();
const boardHistory = board.history();
const gameHistory = game.history();
const getBestMoveLabel = () => {
const bestMove = move?.lastEval?.bestMove;
if (bestMove) {
return `${bestMove} was the best move`;
}
const boardHistory = board.history();
const gameHistory = game.history();
if (game.isGameOver() && boardHistory.join() === gameHistory.join()) {
return "Game is over";
}
return null;
};
const bestMove = move?.lastEval?.bestMove;
const isGameOver =
gameHistory.length > 0 && boardHistory.join() === gameHistory.join();
return (
<>
@@ -49,9 +39,21 @@ export default function ReviewPanelBody() {
</Typography>
</Grid>
<Typography variant="h6" align="center">
{getBestMoveLabel()}
</Typography>
{!!bestMove && (
<Grid item xs={12}>
<Typography variant="h6" align="center">
{`${bestMove} was the best move`}
</Typography>
</Grid>
)}
{isGameOver && (
<Grid item xs={12}>
<Typography variant="h6" align="center">
Game is over
</Typography>
</Grid>
)}
<Grid item container xs={12} justifyContent="center" alignItems="center">
<List>

View File

@@ -0,0 +1,111 @@
import { Stockfish } from "@/lib/engine/stockfish";
import { Icon } from "@iconify/react";
import { Grid } from "@mui/material";
import { useEffect, useState } from "react";
import {
engineDepthAtom,
engineMultiPvAtom,
gameAtom,
gameEvalAtom,
} from "../states";
import { useAtomValue, useSetAtom } from "jotai";
import { getFens } from "@/lib/chess";
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { LoadingButton } from "@mui/lab";
import Slider from "@/components/slider";
export default function AnalyzePanel() {
const [engine, setEngine] = useState<Stockfish | null>(null);
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
const engineDepth = useAtomValue(engineDepthAtom);
const engineMultiPv = useAtomValue(engineMultiPvAtom);
const { setGameEval, gameFromUrl } = useGameDatabase();
const setEval = useSetAtom(gameEvalAtom);
const game = useAtomValue(gameAtom);
useEffect(() => {
const engine = new Stockfish();
engine.init().then(() => {
setEngine(engine);
});
return () => {
engine.shutdown();
};
}, []);
const readyToAnalyse =
engine?.isReady() && game.history().length > 0 && !evaluationInProgress;
const handleAnalyze = async () => {
const gameFens = getFens(game);
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress)
return;
setEvaluationInProgress(true);
const newGameEval = await engine.evaluateGame(
gameFens,
engineDepth,
engineMultiPv
);
setEval(newGameEval);
setEvaluationInProgress(false);
if (gameFromUrl) {
setGameEval(gameFromUrl.id, newGameEval);
}
};
return (
<Grid
item
container
xs={12}
justifyContent="center"
alignItems="center"
rowGap={4}
>
<Slider label="Maximum depth" atom={engineDepthAtom} min={10} max={30} />
<Slider
label="Number of lines"
atom={engineMultiPvAtom}
min={1}
max={6}
xs={6}
/>
<Grid
item
container
xs={12}
justifyContent="center"
alignItems="center"
marginTop={1}
>
<LoadingButton
variant="contained"
size="large"
startIcon={
!evaluationInProgress && (
<Icon icon="streamline:magnifying-glass-solid" />
)
}
onClick={handleAnalyze}
disabled={!readyToAnalyse}
loading={evaluationInProgress}
loadingPosition={evaluationInProgress ? "end" : undefined}
endIcon={
evaluationInProgress && (
<Icon icon="streamline:magnifying-glass-solid" />
)
}
>
{evaluationInProgress ? "Analyzing..." : "Analyze"}
</LoadingButton>
</Grid>
</Grid>
);
}

View File

@@ -1,7 +1,7 @@
import { Icon } from "@iconify/react";
import { Grid, Typography } from "@mui/material";
import LoadGame from "./loadGame";
import AnalyzeButton from "./analyzeButton";
import AnalyzePanel from "./analyzePanel";
export default function ReviewPanelHeader() {
return (
@@ -29,7 +29,7 @@ export default function ReviewPanelHeader() {
<LoadGame />
<AnalyzeButton />
<AnalyzePanel />
</Grid>
);
}

View File

@@ -1,5 +1,5 @@
import { Grid } from "@mui/material";
import LoadGameButton from "../loadGame/loadGameButton";
import LoadGameButton from "../../loadGame/loadGameButton";
import { useCallback, useEffect } from "react";
import { useChessActions } from "@/hooks/useChess";
import {
@@ -7,7 +7,7 @@ import {
boardOrientationAtom,
gameAtom,
gameEvalAtom,
} from "./states";
} from "../states";
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { useAtomValue, useSetAtom } from "jotai";
import { Chess } from "chess.js";
@@ -43,7 +43,7 @@ export default function LoadGame() {
};
loadGame();
}, [gameFromUrl, resetAndSetGamePgn, setEval]);
}, [gameFromUrl, game, resetAndSetGamePgn, setEval]);
if (gameFromUrl) return null;

View File

@@ -11,11 +11,13 @@ export default function SaveButton() {
const board = useAtomValue(boardAtom);
const gameEval = useAtomValue(gameEvalAtom);
const { addGame, setGameEval, gameFromUrl } = useGameDatabase();
const router = useRouter();
const enableSave =
!gameFromUrl && (board.history().length || game.history().length);
const handleSave = async () => {
if (gameFromUrl) return;
if (!enableSave) return;
const gameToSave = getGameToSave(game, board);
@@ -35,8 +37,16 @@ export default function SaveButton() {
};
return (
<IconButton onClick={handleSave} disabled={!!gameFromUrl}>
<Icon icon="ri:save-3-line" />
</IconButton>
<>
{gameFromUrl ? (
<IconButton disabled={true}>
<Icon icon="ri:folder-check-line" />
</IconButton>
) : (
<IconButton onClick={handleSave} disabled={!enableSave}>
<Icon icon="ri:save-3-line" />
</IconButton>
)}
</>
);
}

View File

@@ -6,3 +6,6 @@ export const gameEvalAtom = atom<GameEval | undefined>(undefined);
export const gameAtom = atom(new Chess());
export const boardAtom = atom(new Chess());
export const boardOrientationAtom = atom(true);
export const engineDepthAtom = atom(16);
export const engineMultiPvAtom = atom(3);