fix : board perf optimization

This commit is contained in:
GuillaumeSD
2025-05-15 13:26:38 +02:00
parent cc9a45a45d
commit 4b841c7d2e
2 changed files with 151 additions and 115 deletions

View File

@@ -10,7 +10,7 @@ import {
Square, Square,
} from "react-chessboard/dist/chessboard/types"; } from "react-chessboard/dist/chessboard/types";
import { useChessActions } from "@/hooks/useChessActions"; import { useChessActions } from "@/hooks/useChessActions";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useMemo, useRef, useState } from "react";
import { Color, MoveClassification } from "@/types/enums"; import { Color, MoveClassification } from "@/types/enums";
import { Chess } from "chess.js"; import { Chess } from "chess.js";
import { getSquareRenderer } from "./squareRenderer"; import { getSquareRenderer } from "./squareRenderer";
@@ -64,10 +64,11 @@ export default function Board({
const boardHue = useAtomValue(boardHueAtom); const boardHue = useAtomValue(boardHueAtom);
const gameFen = game.fen(); const gameFen = game.fen();
const [previousFen, setPreviousFen] = useState(gameFen);
useEffect(() => { if (gameFen !== previousFen) {
setPreviousFen(gameFen);
setClickedSquares([]); setClickedSquares([]);
}, [gameFen, setClickedSquares]); }
const isPiecePlayable = useCallback( const isPiecePlayable = useCallback(
({ piece }: { piece: string }): boolean => { ({ piece }: { piece: string }): boolean => {
@@ -78,117 +79,134 @@ export default function Board({
[canPlay, game] [canPlay, game]
); );
const onPieceDrop = ( const onPieceDrop = useCallback(
source: Square, (source: Square, target: Square, piece: string): boolean => {
target: Square, if (!isPiecePlayable({ piece })) return false;
piece: string
): boolean => {
if (!isPiecePlayable({ piece })) return false;
const result = playMove({ const result = playMove({
from: source, from: source,
to: target, to: target,
promotion: piece[1]?.toLowerCase() ?? "q", promotion: piece[1]?.toLowerCase() ?? "q",
}); });
return !!result; return !!result;
}; },
[isPiecePlayable, playMove]
);
const resetMoveClick = (square?: Square | null) => { const resetMoveClick = useCallback(
setMoveClickFrom(square ?? null); (square?: Square | null) => {
setMoveClickTo(null); setMoveClickFrom(square ?? null);
setShowPromotionDialog(false); setMoveClickTo(null);
if (square) { setShowPromotionDialog(false);
const moves = game.moves({ square, verbose: true }); if (square) {
setPlayableSquares(moves.map((m) => m.to)); const moves = game.moves({ square, verbose: true });
} else { setPlayableSquares(moves.map((m) => m.to));
setPlayableSquares([]); } else {
} setPlayableSquares([]);
}; }
},
[setMoveClickFrom, setMoveClickTo, setPlayableSquares, game]
);
const handleSquareLeftClick = (square: Square, piece?: string) => { const handleSquareLeftClick = useCallback(
setClickedSquares([]); (square: Square, piece?: string) => {
setClickedSquares([]);
if (!moveClickFrom) { if (!moveClickFrom) {
if (piece && !isPiecePlayable({ piece })) return; if (piece && !isPiecePlayable({ piece })) return;
resetMoveClick(square); resetMoveClick(square);
return; return;
} }
const validMoves = game.moves({ square: moveClickFrom, verbose: true }); const validMoves = game.moves({ square: moveClickFrom, verbose: true });
const move = validMoves.find((m) => m.to === square); const move = validMoves.find((m) => m.to === square);
if (!move) { if (!move) {
resetMoveClick(square); resetMoveClick(square);
return; return;
} }
setMoveClickTo(square); setMoveClickTo(square);
if ( if (
move.piece === "p" && move.piece === "p" &&
((move.color === "w" && square[1] === "8") || ((move.color === "w" && square[1] === "8") ||
(move.color === "b" && square[1] === "1")) (move.color === "b" && square[1] === "1"))
) { ) {
setShowPromotionDialog(true); setShowPromotionDialog(true);
return; return;
} }
const result = playMove({
from: moveClickFrom,
to: square,
});
resetMoveClick(result ? undefined : square);
};
const handleSquareRightClick = (square: Square) => {
setClickedSquares((prev) =>
prev.includes(square)
? prev.filter((s) => s !== square)
: [...prev, square]
);
};
const handlePieceDragBegin = (_: string, square: Square) => {
resetMoveClick(square);
};
const handlePieceDragEnd = () => {
resetMoveClick();
};
const onPromotionPieceSelect = (
piece?: PromotionPieceOption,
from?: Square,
to?: Square
) => {
if (!piece) return false;
const promotionPiece = piece[1]?.toLowerCase() ?? "q";
if (moveClickFrom && moveClickTo) {
const result = playMove({ const result = playMove({
from: moveClickFrom, from: moveClickFrom,
to: moveClickTo, to: square,
promotion: promotionPiece,
}); });
resetMoveClick();
return !!result;
}
if (from && to) { resetMoveClick(result ? undefined : square);
const result = playMove({ },
from, [
to, game,
promotion: promotionPiece, isPiecePlayable,
}); moveClickFrom,
resetMoveClick(); playMove,
return !!result; resetMoveClick,
} setClickedSquares,
]
);
resetMoveClick(moveClickFrom); const handleSquareRightClick = useCallback(
return false; (square: Square) => {
}; setClickedSquares((prev) =>
prev.includes(square)
? prev.filter((s) => s !== square)
: [...prev, square]
);
},
[setClickedSquares]
);
const handlePieceDragBegin = useCallback(
(_: string, square: Square) => {
resetMoveClick(square);
},
[resetMoveClick]
);
const handlePieceDragEnd = useCallback(() => {
resetMoveClick();
}, [resetMoveClick]);
const onPromotionPieceSelect = useCallback(
(piece?: PromotionPieceOption, from?: Square, to?: Square) => {
if (!piece) return false;
const promotionPiece = piece[1]?.toLowerCase() ?? "q";
if (moveClickFrom && moveClickTo) {
const result = playMove({
from: moveClickFrom,
to: moveClickTo,
promotion: promotionPiece,
});
resetMoveClick();
return !!result;
}
if (from && to) {
const result = playMove({
from,
to,
promotion: promotionPiece,
});
resetMoveClick();
return !!result;
}
resetMoveClick(moveClickFrom);
return false;
},
[moveClickFrom, moveClickTo, playMove, resetMoveClick]
);
const customArrows: Arrow[] = useMemo(() => { const customArrows: Arrow[] = useMemo(() => {
const bestMove = position?.lastEval?.bestMove; const bestMove = position?.lastEval?.bestMove;
@@ -249,6 +267,22 @@ export default function Board({
[pieceSet] [pieceSet]
); );
const customBoardStyle = useMemo(() => {
const commonBoardStyle = {
borderRadius: "5px",
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
};
if (boardHue) {
return {
...commonBoardStyle,
filter: `hue-rotate(${boardHue}deg)`,
};
}
return commonBoardStyle;
}, [boardHue]);
return ( return (
<Grid <Grid
container container
@@ -293,11 +327,7 @@ export default function Board({
boardOrientation={ boardOrientation={
boardOrientation === Color.White ? "white" : "black" boardOrientation === Color.White ? "white" : "black"
} }
customBoardStyle={{ customBoardStyle={customBoardStyle}
borderRadius: "5px",
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
filter: `hue-rotate(${boardHue}deg)`,
}}
customArrows={customArrows} customArrows={customArrows}
isDraggablePiece={isPiecePlayable} isDraggablePiece={isPiecePlayable}
customSquare={SquareRenderer} customSquare={SquareRenderer}

View File

@@ -2,7 +2,7 @@ import { CurrentPosition } from "@/types/eval";
import { MoveClassification } from "@/types/enums"; import { MoveClassification } from "@/types/enums";
import { PrimitiveAtom, atom, useAtomValue } from "jotai"; import { PrimitiveAtom, atom, useAtomValue } from "jotai";
import Image from "next/image"; import Image from "next/image";
import { CSSProperties, forwardRef } from "react"; import { CSSProperties, forwardRef, useMemo } from "react";
import { import {
CustomSquareProps, CustomSquareProps,
Square, Square,
@@ -36,15 +36,21 @@ export function getSquareRenderer({
const toSquare = position.lastMove?.to; const toSquare = position.lastMove?.to;
const moveClassification = position?.eval?.moveClassification; const moveClassification = position?.eval?.moveClassification;
const highlightSquareStyle: CSSProperties | undefined = const highlightSquareStyle: CSSProperties | undefined = useMemo(
clickedSquares.includes(square) () =>
? rightClickSquareStyle clickedSquares.includes(square)
: fromSquare === square || toSquare === square ? rightClickSquareStyle
? previousMoveSquareStyle(moveClassification) : fromSquare === square || toSquare === square
: undefined; ? previousMoveSquareStyle(moveClassification)
: undefined,
[clickedSquares, square, fromSquare, toSquare, moveClassification]
);
const playableSquareStyle: CSSProperties | undefined = const playableSquareStyle: CSSProperties | undefined = useMemo(
playableSquares.includes(square) ? playableSquareStyles : undefined; () =>
playableSquares.includes(square) ? playableSquareStyles : undefined,
[playableSquares, square]
);
return ( return (
<div <div
@@ -52,7 +58,7 @@ export function getSquareRenderer({
style={{ style={{
...style, ...style,
position: "relative", position: "relative",
filter: `hue-rotate(-${boardHue}deg)`, filter: boardHue ? `hue-rotate(-${boardHue}deg)` : undefined,
}} }}
> >
{children} {children}