feat : add click on engine lines

This commit is contained in:
GuillaumeSD
2025-05-11 18:21:45 +02:00
parent 74a2adbb7d
commit 6535fce2f4
22 changed files with 285 additions and 156 deletions

View File

@@ -1,15 +1,23 @@
import { LineEval } from "@/types/eval";
import { ListItem, Skeleton, Typography } from "@mui/material";
import { Box, ListItem, Skeleton, Typography, useTheme } from "@mui/material";
import { useAtomValue } from "jotai";
import { boardAtom } from "../../states";
import { getLineEvalLabel, moveLineUciToSan } from "@/lib/chess";
import localFont from "next/font/local";
import { useChessActions } from "@/hooks/useChessActions";
const myFont = localFont({
src: "./chess_merida_unicode.ttf",
});
interface Props {
line: LineEval;
}
export default function LineEvaluation({ line }: Props) {
const theme = useTheme();
const board = useAtomValue(boardAtom);
const { addMoves } = useChessActions(boardAtom);
const lineLabel = getLineEvalLabel(line);
const isBlackCp =
@@ -18,6 +26,26 @@ export default function LineEvaluation({ line }: Props) {
const showSkeleton = line.depth < 6;
const uciToSan = moveLineUciToSan(board.fen());
const initialTurn = board.turn();
const isDarkMode = theme.palette.mode === "dark";
const formatSan = (
san: string,
moveIdx: number
): { icon?: string; text: string } => {
const firstChar = san.charAt(0);
const isPiece = ["K", "Q", "R", "B", "N"].includes(firstChar);
if (!isPiece) return { text: san };
const turn = isDarkMode ? initialTurn : initialTurn === "w" ? "b" : "w";
const moveColor = moveIdx % 2 === 0 ? turn : turn === "w" ? "b" : "w";
const icon = unicodeMap[firstChar][moveColor];
return { icon, text: san.slice(1) };
};
return (
<ListItem disablePadding>
<Typography
@@ -58,9 +86,52 @@ export default function LineEvaluation({ line }: Props) {
{showSkeleton ? (
<Skeleton variant="rounded" animation="wave" width="15em" />
) : (
line.pv.map(moveLineUciToSan(board.fen())).join(", ")
line.pv.map((uci, i) => {
const san = uciToSan(uci);
const { icon, text } = formatSan(san, i);
return (
<Box
component="span"
key={i}
onClick={() => {
addMoves(line.pv.slice(0, i + 1));
}}
sx={{
cursor: "pointer",
ml: i ? 0.5 : 0,
transition: "opacity 0.2s ease-in-out",
"&:hover": {
opacity: 0.5,
},
}}
>
{icon && (
<Typography
component="span"
fontFamily={myFont.style.fontFamily}
>
{icon}
</Typography>
)}
<Typography component="span">
{text}
{i < line.pv.length - 1 && ","}
</Typography>
</Box>
);
})
)}
</Typography>
</ListItem>
);
}
const unicodeMap: Record<string, Record<"w" | "b", string>> = {
K: { w: "♚", b: "♔" },
Q: { w: "♛", b: "♕" },
R: { w: "♜", b: "♖" },
B: { w: "♝", b: "♗" },
N: { w: "♞", b: "♘" },
};

View File

@@ -6,7 +6,7 @@ import { useMemo } from "react";
import Image from "next/image";
import { capitalize } from "@/lib/helpers";
import { useChessActions } from "@/hooks/useChessActions";
import { moveClassificationColors } from "@/lib/chess";
import { CLASSIFICATION_COLORS } from "@/constants";
interface Props {
classification: MoveClassification;
@@ -74,7 +74,7 @@ export default function ClassificationRow({ classification }: Props) {
justifyContent="space-evenly"
alignItems="center"
wrap="nowrap"
color={moveClassificationColors[classification]}
color={CLASSIFICATION_COLORS[classification]}
size={12}
>
<Grid

View File

@@ -6,7 +6,7 @@ import { boardAtom, currentPositionAtom, gameAtom } from "../../../states";
import { useChessActions } from "@/hooks/useChessActions";
import { useEffect } from "react";
import { isInViewport } from "@/lib/helpers";
import { moveClassificationColors } from "@/lib/chess";
import { CLASSIFICATION_COLORS } from "@/constants";
interface Props {
san: string;
@@ -89,7 +89,7 @@ const getMoveColor = (moveClassification?: MoveClassification) => {
return undefined;
}
return moveClassificationColors[moveClassification];
return CLASSIFICATION_COLORS[moveClassification];
};
const moveClassificationsToIgnore: MoveClassification[] = [

View File

@@ -1,6 +1,6 @@
import { DotProps } from "recharts";
import { ChartItemData } from "./types";
import { moveClassificationColors } from "@/lib/chess";
import { CLASSIFICATION_COLORS } from "@/constants";
export default function CustomDot({
cx,
@@ -9,7 +9,7 @@ export default function CustomDot({
payload,
}: DotProps & { payload?: ChartItemData }) {
const moveColor = payload?.moveClassification
? moveClassificationColors[payload.moveClassification]
? CLASSIFICATION_COLORS[payload.moveClassification]
: "grey";
return (

View File

@@ -21,7 +21,7 @@ import type { ReactElement } from "react";
import CustomTooltip from "./tooltip";
import { ChartItemData } from "./types";
import { PositionEval } from "@/types/eval";
import { moveClassificationColors } from "@/lib/chess";
import { CLASSIFICATION_COLORS } from "@/constants";
import CustomDot from "./dot";
import { MoveClassification } from "@/types/enums";
import { useChessActions } from "@/hooks/useChessActions";
@@ -51,7 +51,7 @@ export default function GraphTab(props: GridProps) {
}, [chartData]);
const boardMoveColor = currentPosition.eval?.moveClassification
? moveClassificationColors[currentPosition.eval.moveClassification]
? CLASSIFICATION_COLORS[currentPosition.eval.moveClassification]
: "grey";
// Render a dot only on selected classifications (always returns an element)

View File

@@ -6,7 +6,7 @@ import { useChessActions } from "@/hooks/useChessActions";
import { useCallback, useEffect } from "react";
export default function NextMoveButton() {
const { makeMove: makeBoardMove } = useChessActions(boardAtom);
const { playMove: playBoardMove } = useChessActions(boardAtom);
const game = useAtomValue(gameAtom);
const board = useAtomValue(boardAtom);
@@ -27,14 +27,14 @@ export default function NextMoveButton() {
.find((c) => c.fen === nextMove.after)?.comment;
if (nextMove) {
makeBoardMove({
playBoardMove({
from: nextMove.from,
to: nextMove.to,
promotion: nextMove.promotion,
comment,
});
}
}, [isButtonEnabled, boardHistory, game, makeBoardMove]);
}, [isButtonEnabled, boardHistory, game, playBoardMove]);
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {

View File

@@ -1,3 +1,4 @@
import { DEFAULT_ENGINE } from "@/constants";
import { EngineName } from "@/types/enums";
import { CurrentPosition, GameEval, SavedEvals } from "@/types/eval";
import { Chess } from "chess.js";
@@ -12,7 +13,7 @@ export const boardOrientationAtom = atom(true);
export const showBestMoveArrowAtom = atom(true);
export const showPlayerMoveIconAtom = atom(true);
export const engineNameAtom = atom<EngineName>(EngineName.Stockfish17Lite);
export const engineNameAtom = atom<EngineName>(DEFAULT_ENGINE);
export const engineDepthAtom = atom(14);
export const engineMultiPvAtom = atom(3);
export const evaluationProgressAtom = atom(0);