feat : add live move classification
This commit is contained in:
@@ -5,12 +5,15 @@ import {
|
||||
engineMultiPvAtom,
|
||||
gameAtom,
|
||||
gameEvalAtom,
|
||||
savedEvalsAtom,
|
||||
} from "@/sections/analysis/states";
|
||||
import { CurrentPosition, PositionEval } from "@/types/eval";
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import { useEffect } from "react";
|
||||
import { useEngine } from "../../../hooks/useEngine";
|
||||
import { EngineName } from "@/types/enums";
|
||||
import { getEvaluateGameParams } from "@/lib/chess";
|
||||
import { getMovesClassification } from "@/lib/engine/helpers/moveClassification";
|
||||
|
||||
export const useCurrentPosition = (engineName?: EngineName) => {
|
||||
const [currentPosition, setCurrentPosition] = useAtom(currentPositionAtom);
|
||||
@@ -20,6 +23,7 @@ export const useCurrentPosition = (engineName?: EngineName) => {
|
||||
const board = useAtomValue(boardAtom);
|
||||
const depth = useAtomValue(engineDepthAtom);
|
||||
const multiPv = useAtomValue(engineMultiPvAtom);
|
||||
const [savedEvals, setSavedEvals] = useAtom(savedEvalsAtom);
|
||||
|
||||
useEffect(() => {
|
||||
const position: CurrentPosition = {
|
||||
@@ -44,21 +48,92 @@ export const useCurrentPosition = (engineName?: EngineName) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!position.eval && engine?.isReady()) {
|
||||
const setPartialEval = (positionEval: PositionEval) => {
|
||||
setCurrentPosition({ ...position, eval: positionEval });
|
||||
setCurrentPosition(position);
|
||||
|
||||
if (!position.eval && engine?.isReady() && engineName) {
|
||||
const getFenEngineEval = async (
|
||||
fen: string,
|
||||
setPartialEval?: (positionEval: PositionEval) => void
|
||||
) => {
|
||||
if (!engine?.isReady() || !engineName)
|
||||
throw new Error("Engine not ready");
|
||||
const savedEval = savedEvals[fen];
|
||||
if (
|
||||
savedEval &&
|
||||
savedEval.engine === engineName &&
|
||||
savedEval.lines[0].depth >= depth
|
||||
) {
|
||||
setPartialEval?.(savedEval);
|
||||
return savedEval;
|
||||
}
|
||||
|
||||
const rawPositionEval = await engine.evaluatePositionWithUpdate({
|
||||
fen,
|
||||
depth,
|
||||
multiPv,
|
||||
setPartialEval,
|
||||
});
|
||||
|
||||
setSavedEvals((prev) => ({
|
||||
...prev,
|
||||
[fen]: { ...rawPositionEval, engine: engineName },
|
||||
}));
|
||||
|
||||
return rawPositionEval;
|
||||
};
|
||||
|
||||
engine.evaluatePositionWithUpdate({
|
||||
fen: board.fen(),
|
||||
depth,
|
||||
multiPv,
|
||||
setPartialEval,
|
||||
});
|
||||
const getPositionEval = async () => {
|
||||
const setPartialEval = (positionEval: PositionEval) => {
|
||||
setCurrentPosition({ ...position, eval: positionEval });
|
||||
};
|
||||
const rawPositionEval = await getFenEngineEval(
|
||||
board.fen(),
|
||||
setPartialEval
|
||||
);
|
||||
|
||||
if (boardHistory.length === 0) return;
|
||||
|
||||
const params = getEvaluateGameParams(board);
|
||||
const fens = params.fens.slice(board.turn() === "w" ? -3 : -4);
|
||||
const uciMoves = params.uciMoves.slice(board.turn() === "w" ? -3 : -4);
|
||||
|
||||
const lastRawEval = await getFenEngineEval(fens.slice(-2)[0]);
|
||||
const rawPositions: PositionEval[] = fens.map((_, idx) => {
|
||||
if (idx === fens.length - 2) return lastRawEval;
|
||||
if (idx === fens.length - 1) return rawPositionEval;
|
||||
return {
|
||||
lines: [
|
||||
{
|
||||
pv: [],
|
||||
depth: 0,
|
||||
multiPv: 1,
|
||||
cp: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
const positionsWithMoveClassification = getMovesClassification(
|
||||
rawPositions,
|
||||
uciMoves,
|
||||
fens
|
||||
);
|
||||
|
||||
setCurrentPosition({
|
||||
...position,
|
||||
eval: positionsWithMoveClassification.slice(-1)[0],
|
||||
lastEval: positionsWithMoveClassification.slice(-2)[0],
|
||||
});
|
||||
};
|
||||
|
||||
getPositionEval();
|
||||
}
|
||||
|
||||
setCurrentPosition(position);
|
||||
}, [gameEval, board, game, engine, depth, multiPv, setCurrentPosition]);
|
||||
return () => {
|
||||
engine?.stopSearch();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [gameEval, board, game, engine, depth, multiPv]);
|
||||
|
||||
return currentPosition;
|
||||
};
|
||||
|
||||
@@ -6,13 +6,15 @@ import {
|
||||
evaluationProgressAtom,
|
||||
gameAtom,
|
||||
gameEvalAtom,
|
||||
savedEvalsAtom,
|
||||
} from "../states";
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { getEvaluateGameParams } from "@/lib/chess";
|
||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { useEngine } from "@/hooks/useEngine";
|
||||
import { logAnalyticsEvent } from "@/lib/firebase";
|
||||
import { SavedEvals } from "@/types/eval";
|
||||
|
||||
export default function AnalyzeButton() {
|
||||
const engineName = useAtomValue(engineNameAtom);
|
||||
@@ -25,6 +27,7 @@ export default function AnalyzeButton() {
|
||||
const { setGameEval, gameFromUrl } = useGameDatabase();
|
||||
const [gameEval, setEval] = useAtom(gameEvalAtom);
|
||||
const game = useAtomValue(gameAtom);
|
||||
const setSavedEvals = useSetAtom(savedEvalsAtom);
|
||||
|
||||
const readyToAnalyse =
|
||||
engine?.isReady() && game.history().length > 0 && !evaluationProgress;
|
||||
@@ -49,6 +52,15 @@ export default function AnalyzeButton() {
|
||||
setGameEval(gameFromUrl.id, newGameEval);
|
||||
}
|
||||
|
||||
const gameSavedEvals: SavedEvals = params.fens.reduce((acc, fen, idx) => {
|
||||
acc[fen] = { ...newGameEval.positions[idx], engine: engineName };
|
||||
return acc;
|
||||
}, {} as SavedEvals);
|
||||
setSavedEvals((prev) => ({
|
||||
...prev,
|
||||
...gameSavedEvals,
|
||||
}));
|
||||
|
||||
logAnalyticsEvent("analyze_game", {
|
||||
engine: engineName,
|
||||
depth: engineDepth,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EngineName } from "@/types/enums";
|
||||
import { CurrentPosition, GameEval } from "@/types/eval";
|
||||
import { CurrentPosition, GameEval, SavedEvals } from "@/types/eval";
|
||||
import { Chess } from "chess.js";
|
||||
import { atom } from "jotai";
|
||||
|
||||
@@ -16,3 +16,5 @@ export const engineNameAtom = atom<EngineName>(EngineName.Stockfish16);
|
||||
export const engineDepthAtom = atom(16);
|
||||
export const engineMultiPvAtom = atom(3);
|
||||
export const evaluationProgressAtom = atom(0);
|
||||
|
||||
export const savedEvalsAtom = atom<SavedEvals>({});
|
||||
|
||||
Reference in New Issue
Block a user