feat : add move classification

This commit is contained in:
GuillaumeSD
2024-03-29 03:53:16 +01:00
parent 714ed1039e
commit d9b322d9fa
9 changed files with 257 additions and 16 deletions

View File

@@ -11,14 +11,13 @@ import { useMemo } from "react";
import { useScreenSize } from "@/hooks/useScreenSize";
import { Color } from "@/types/enums";
import Board from "@/components/board";
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { usePlayersNames } from "@/hooks/usePlayerNames";
export default function BoardContainer() {
const screenSize = useScreenSize();
const boardOrientation = useAtomValue(boardOrientationAtom);
const showBestMoveArrow = useAtomValue(showBestMoveArrowAtom);
const { gameFromUrl } = useGameDatabase();
const game = useAtomValue(gameAtom);
const { whiteName, blackName } = usePlayersNames(gameAtom);
const boardSize = useMemo(() => {
const width = screenSize.width;
@@ -38,12 +37,8 @@ export default function BoardContainer() {
boardSize={boardSize}
canPlay={true}
gameAtom={boardAtom}
whitePlayer={
gameFromUrl?.white?.name || game.header()["White"] || "White"
}
blackPlayer={
gameFromUrl?.black?.name || game.header()["Black"] || "Black"
}
whitePlayer={whiteName}
blackPlayer={blackName}
boardOrientation={boardOrientation ? Color.White : Color.Black}
currentPositionAtom={currentPositionAtom}
showBestMoveArrow={showBestMoveArrow}

View File

@@ -34,7 +34,7 @@ export const useCurrentPosition = (engineName?: EngineName) => {
boardHistory.length <= gameHistory.length &&
gameHistory.slice(0, boardHistory.length).join() === boardHistory.join()
) {
const evalIndex = board.history().length;
const evalIndex = boardHistory.length;
position.eval = gameEval.positions[evalIndex];
position.lastEval =

View File

@@ -0,0 +1,130 @@
import { Color, MoveClassification } from "@/types/enums";
import { Grid, Typography } from "@mui/material";
import { useAtomValue } from "jotai";
import { boardAtom, gameAtom, gameEvalAtom } from "../states";
import { useMemo } from "react";
import { moveClassificationColors } from "@/components/board/squareRenderer";
import Image from "next/image";
import { capitalize } from "@/lib/helpers";
import { useChessActions } from "@/hooks/useChessActions";
interface Props {
classification: MoveClassification;
}
export default function ClassificationRow({ classification }: Props) {
const gameEval = useAtomValue(gameEvalAtom);
const board = useAtomValue(boardAtom);
const game = useAtomValue(gameAtom);
const { goToMove } = useChessActions(boardAtom);
const whiteNb = useMemo(() => {
if (!gameEval) return 0;
return gameEval.positions.filter(
(position, idx) =>
idx % 2 !== 0 && position.moveClassification === classification
).length;
}, [gameEval, classification]);
const blackNb = useMemo(() => {
if (!gameEval) return 0;
return gameEval.positions.filter(
(position, idx) =>
idx % 2 === 0 && position.moveClassification === classification
).length;
}, [gameEval, classification]);
const handleClick = (color: Color) => {
if (
!gameEval ||
(color === Color.White && !whiteNb) ||
(color === Color.Black && !blackNb)
) {
return;
}
const filterColor = (idx: number) =>
(idx % 2 !== 0 && color === Color.White) ||
(idx % 2 === 0 && color === Color.Black);
const moveIdx = board.history().length;
const nextPositionIdx = gameEval.positions.findIndex(
(position, idx) =>
filterColor(idx) &&
position.moveClassification === classification &&
idx > moveIdx
);
if (nextPositionIdx > 0) {
goToMove(nextPositionIdx, game);
} else {
const firstPositionIdx = gameEval.positions.findIndex(
(position, idx) =>
filterColor(idx) && position.moveClassification === classification
);
if (firstPositionIdx > 0 && firstPositionIdx !== moveIdx) {
goToMove(firstPositionIdx, game);
}
}
};
if (!gameEval?.positions.length) return null;
return (
<Grid
container
item
justifyContent="space-evenly"
alignItems="center"
xs={12}
wrap="nowrap"
color={moveClassificationColors[classification]}
>
<Grid
container
item
justifyContent="center"
alignItems="center"
width={"3rem"}
style={{ cursor: whiteNb ? "pointer" : "default" }}
onClick={() => handleClick(Color.White)}
>
{whiteNb}
</Grid>
<Grid
container
item
justifyContent="start"
alignItems="center"
width={"7rem"}
gap={1}
>
<Image
src={`/icons/${classification}.png`}
alt="move-icon"
width={20}
height={20}
style={{
maxWidth: "3.6vw",
maxHeight: "3.6vw",
}}
/>
<Typography align="center">{capitalize(classification)}</Typography>
</Grid>
<Grid
container
item
justifyContent="center"
alignItems="center"
width={"3rem"}
style={{ cursor: blackNb ? "pointer" : "default" }}
onClick={() => handleClick(Color.Black)}
>
{blackNb}
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,74 @@
import { usePlayersNames } from "@/hooks/usePlayerNames";
import { Grid, Typography } from "@mui/material";
import { gameAtom, gameEvalAtom } from "../states";
import { MoveClassification } from "@/types/enums";
import ClassificationRow from "./classificationRow";
import { useAtomValue } from "jotai";
export default function MovesClassificationsRecap() {
const { whiteName, blackName } = usePlayersNames(gameAtom);
const gameEval = useAtomValue(gameEvalAtom);
if (!gameEval?.positions.length) return null;
return (
<Grid
container
item
justifyContent="center"
alignItems="center"
borderRadius={2}
border={1}
borderColor={"secondary.main"}
sx={{
backgroundColor: "secondary.main",
borderColor: "primary.main",
borderWidth: 2,
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)",
}}
marginTop={{ xs: 0, lg: "2.5em" }}
paddingY={3}
rowGap={2}
xs
style={{ maxWidth: "50rem" }}
>
<Grid
item
container
alignItems="center"
justifyContent="space-evenly"
wrap="nowrap"
xs={12}
>
<Typography width="12rem" align="center">
{whiteName}
</Typography>
<Typography width="7rem" />
<Typography width="12rem" align="center">
{blackName}
</Typography>
</Grid>
{sortedMoveClassfications.map((classification) => (
<ClassificationRow
key={classification}
classification={classification}
/>
))}
</Grid>
);
}
export const sortedMoveClassfications = [
MoveClassification.Brilliant,
MoveClassification.Great,
MoveClassification.Best,
MoveClassification.Excellent,
MoveClassification.Good,
MoveClassification.Book,
MoveClassification.Inaccuracy,
MoveClassification.Mistake,
MoveClassification.Blunder,
];

View File

@@ -27,7 +27,7 @@ export default function ReviewPanelHeader() {
alignItems="center"
columnGap={1}
>
<Icon icon="ph:file-magnifying-glass-fill" height={28} />
<Icon icon="streamline:clipboard-check" height={24} />
<Typography variant="h5" align="center">
Game Report