Squashed commit of the following:
commit c769a4d3bfbd22804ea4eeb324e7afeaaaa55f82
Author: GuillaumeSD <gsd.lfny@gmail.com>
Date: Fri Apr 12 02:49:21 2024 +0200
style : capturedPieces UI final touches
commit 838ad95bfa1746a3a8de38ace2520fe93ec6a0af
Author: GuillaumeSD <gsd.lfny@gmail.com>
Date: Thu Apr 11 03:00:01 2024 +0200
feat : captured pieces wip
commit e4bb4dcc346aa5dea5527fef4389b01673645e76
Merge: 785dba2 e9e772c
Author: GuillaumeSD <gsd.lfny@gmail.com>
Date: Thu Apr 11 01:15:31 2024 +0200
Merge branch 'main' into feat/add-board-captured-pieces
commit 785dba28509ac04655d4cab736a18a821ca52433
Author: GuillaumeSD <gsd.lfny@gmail.com>
Date: Tue Apr 9 02:25:29 2024 +0200
feat : add board captured pieces init
This commit is contained in:
BIN
public/captured-pieces.png
Normal file
BIN
public/captured-pieces.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
167
src/components/board/capturedPieces.tsx
Normal file
167
src/components/board/capturedPieces.tsx
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { getCapturedPieces, getMaterialDifference } from "@/lib/chess";
|
||||||
|
import { Color } from "@/types/enums";
|
||||||
|
import { Grid, Typography } from "@mui/material";
|
||||||
|
import { Chess } from "chess.js";
|
||||||
|
import { PrimitiveAtom, useAtomValue } from "jotai";
|
||||||
|
import { CSSProperties, useMemo } from "react";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
gameAtom: PrimitiveAtom<Chess>;
|
||||||
|
color: Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PIECE_SCALE = 0.6;
|
||||||
|
|
||||||
|
export default function CapturedPieces({ gameAtom, color }: Props) {
|
||||||
|
const game = useAtomValue(gameAtom);
|
||||||
|
const cssProps = useMemo(() => {
|
||||||
|
const capturedPieces = getCapturedPieces(game.fen(), color);
|
||||||
|
console.log(capturedPieces, color);
|
||||||
|
return getCapturedPiecesCSSProps(capturedPieces, color);
|
||||||
|
}, [game, color]);
|
||||||
|
|
||||||
|
const materialDiff = useMemo(() => {
|
||||||
|
const materialDiff = getMaterialDifference(game.fen());
|
||||||
|
return color === Color.White ? materialDiff : -materialDiff;
|
||||||
|
}, [game, color]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid item container alignItems="end" xs="auto" columnGap={0.6}>
|
||||||
|
{cssProps.map((cssProp, i) => (
|
||||||
|
<span
|
||||||
|
key={i}
|
||||||
|
style={{
|
||||||
|
...cssProp,
|
||||||
|
backgroundSize: `${34.2 * PIECE_SCALE}rem ${30.6 * PIECE_SCALE}rem`,
|
||||||
|
backgroundImage: "url(/captured-pieces.png)",
|
||||||
|
backgroundRepeat: "no-repeat",
|
||||||
|
display: "inline-block",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{materialDiff > 0 && (
|
||||||
|
<Typography
|
||||||
|
lineHeight={`${PIECE_SCALE * 1.5}rem`}
|
||||||
|
fontSize={`${PIECE_SCALE * 1.5}rem`}
|
||||||
|
marginLeft={0.3}
|
||||||
|
>
|
||||||
|
+{materialDiff}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCapturedPiecesCSSProps = (
|
||||||
|
capturedPieces: Record<string, number | undefined>,
|
||||||
|
color: Color
|
||||||
|
): CSSProperties[] => {
|
||||||
|
const cssProps: CSSProperties[] = [];
|
||||||
|
|
||||||
|
if (color === Color.Black) {
|
||||||
|
if (capturedPieces.P) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPositionX: `-${18 * PIECE_SCALE}rem`,
|
||||||
|
backgroundPositionY: `${
|
||||||
|
-20.1 * PIECE_SCALE + capturedPieces.P * 2.5 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
width: `${0.6 * PIECE_SCALE + capturedPieces.P * 0.7 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.7 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.B) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `-${24.7 * PIECE_SCALE}rem ${
|
||||||
|
-5.1 * PIECE_SCALE + capturedPieces.B * 2.6 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
width: `${0.7 * PIECE_SCALE + capturedPieces.B * 0.8 * PIECE_SCALE}rem`,
|
||||||
|
height: `${
|
||||||
|
1.7 * PIECE_SCALE + capturedPieces.B * 0.1 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.N) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `-${27.5 * PIECE_SCALE}rem ${
|
||||||
|
-4.9 * PIECE_SCALE + capturedPieces.N * 2.5 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
width: `${0.9 * PIECE_SCALE + capturedPieces.N * 0.7 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.9 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.R) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `${
|
||||||
|
-30.2 * PIECE_SCALE + capturedPieces.R * 0.1 * PIECE_SCALE
|
||||||
|
}rem ${-5.1 * PIECE_SCALE + capturedPieces.R * 2.5 * PIECE_SCALE}rem`,
|
||||||
|
width: `${0.7 * PIECE_SCALE + capturedPieces.R * 0.8 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.7 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.Q) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `-${32.5 * PIECE_SCALE}rem ${0.1 * PIECE_SCALE}rem`,
|
||||||
|
width: `${1.8 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.9 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (capturedPieces.p) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPositionX: 0,
|
||||||
|
backgroundPositionY: `${
|
||||||
|
-20.1 * PIECE_SCALE + capturedPieces.p * 2.5 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
width: `${0.6 * PIECE_SCALE + capturedPieces.p * 0.7 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.7 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.b) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `-${6.7 * PIECE_SCALE}rem ${
|
||||||
|
-5.1 * PIECE_SCALE + capturedPieces.b * 2.6 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
width: `${0.7 * PIECE_SCALE + capturedPieces.b * 0.8 * PIECE_SCALE}rem`,
|
||||||
|
height: `${
|
||||||
|
1.7 * PIECE_SCALE + capturedPieces.b * 0.1 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.n) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `-${9.5 * PIECE_SCALE}rem ${
|
||||||
|
-4.9 * PIECE_SCALE + capturedPieces.n * 2.5 * PIECE_SCALE
|
||||||
|
}rem`,
|
||||||
|
width: `${0.9 * PIECE_SCALE + capturedPieces.n * 0.7 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.9 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.r) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `${
|
||||||
|
-12.2 * PIECE_SCALE + capturedPieces.r * 0.1 * PIECE_SCALE
|
||||||
|
}rem ${-5.1 * PIECE_SCALE + capturedPieces.r * 2.5 * PIECE_SCALE}rem`,
|
||||||
|
width: `${0.7 * PIECE_SCALE + capturedPieces.r * 0.8 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.7 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedPieces.q) {
|
||||||
|
cssProps.push({
|
||||||
|
backgroundPosition: `-${14.5 * PIECE_SCALE}rem ${0.1 * PIECE_SCALE}rem`,
|
||||||
|
width: `${1.8 * PIECE_SCALE}rem`,
|
||||||
|
height: `${1.9 * PIECE_SCALE}rem`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cssProps;
|
||||||
|
};
|
||||||
@@ -14,6 +14,7 @@ import { Chess } from "chess.js";
|
|||||||
import { getSquareRenderer, moveClassificationColors } from "./squareRenderer";
|
import { getSquareRenderer, moveClassificationColors } from "./squareRenderer";
|
||||||
import { CurrentPosition } from "@/types/eval";
|
import { CurrentPosition } from "@/types/eval";
|
||||||
import EvaluationBar from "./evaluationBar";
|
import EvaluationBar from "./evaluationBar";
|
||||||
|
import CapturedPieces from "./capturedPieces";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -230,10 +231,16 @@ export default function Board({
|
|||||||
xs={12}
|
xs={12}
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
|
columnGap={2}
|
||||||
>
|
>
|
||||||
<Typography variant="h6">
|
<Typography>
|
||||||
{boardOrientation === Color.White ? blackPlayer : whitePlayer}
|
{boardOrientation === Color.White ? blackPlayer : whitePlayer}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<CapturedPieces
|
||||||
|
gameAtom={gameAtom}
|
||||||
|
color={boardOrientation === Color.White ? Color.Black : Color.White}
|
||||||
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
@@ -275,10 +282,13 @@ export default function Board({
|
|||||||
xs={12}
|
xs={12}
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
|
columnGap={2}
|
||||||
>
|
>
|
||||||
<Typography variant="h6">
|
<Typography>
|
||||||
{boardOrientation === Color.White ? whitePlayer : blackPlayer}
|
{boardOrientation === Color.White ? whitePlayer : blackPlayer}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<CapturedPieces gameAtom={gameAtom} color={boardOrientation} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ export const usePlayersNames = (gameAtom: PrimitiveAtom<Chess>) => {
|
|||||||
gameFromUrl?.black?.name || game.header()["Black"] || "Black";
|
gameFromUrl?.black?.name || game.header()["Black"] || "Black";
|
||||||
|
|
||||||
const whiteElo =
|
const whiteElo =
|
||||||
gameFromUrl?.white?.rating || game.header()["WhiteElo"] || "?";
|
gameFromUrl?.white?.rating || game.header()["WhiteElo"] || undefined;
|
||||||
|
|
||||||
const blackElo =
|
const blackElo =
|
||||||
gameFromUrl?.black?.rating || game.header()["BlackElo"] || "?";
|
gameFromUrl?.black?.rating || game.header()["BlackElo"] || undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
whiteName,
|
whiteName,
|
||||||
|
|||||||
@@ -267,3 +267,31 @@ export const isCheck = (fen: string): boolean => {
|
|||||||
const game = new Chess(fen);
|
const game = new Chess(fen);
|
||||||
return game.inCheck();
|
return game.inCheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCapturedPieces = (
|
||||||
|
fen: string,
|
||||||
|
color: Color
|
||||||
|
): Record<string, number | undefined> => {
|
||||||
|
const capturedPieces: Record<string, number | undefined> = {};
|
||||||
|
if (color === Color.White) {
|
||||||
|
capturedPieces.p = 8;
|
||||||
|
capturedPieces.r = 2;
|
||||||
|
capturedPieces.n = 2;
|
||||||
|
capturedPieces.b = 2;
|
||||||
|
capturedPieces.q = 1;
|
||||||
|
} else {
|
||||||
|
capturedPieces.P = 8;
|
||||||
|
capturedPieces.R = 2;
|
||||||
|
capturedPieces.N = 2;
|
||||||
|
capturedPieces.B = 2;
|
||||||
|
capturedPieces.Q = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fenPiecePlacement = fen.split(" ")[0];
|
||||||
|
for (const piece of Object.keys(capturedPieces)) {
|
||||||
|
const count = fenPiecePlacement.match(new RegExp(piece, "g"))?.length;
|
||||||
|
if (count) capturedPieces[piece] = (capturedPieces[piece] ?? 0) - count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return capturedPieces;
|
||||||
|
};
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ export default function BoardContainer() {
|
|||||||
boardSize={boardSize}
|
boardSize={boardSize}
|
||||||
canPlay={true}
|
canPlay={true}
|
||||||
gameAtom={boardAtom}
|
gameAtom={boardAtom}
|
||||||
whitePlayer={`${whiteName} (${whiteElo})`}
|
whitePlayer={whiteElo ? `${whiteName} (${whiteElo})` : whiteName}
|
||||||
blackPlayer={`${blackName} (${blackElo})`}
|
blackPlayer={blackElo ? `${blackName} (${blackElo})` : blackName}
|
||||||
boardOrientation={boardOrientation ? Color.White : Color.Black}
|
boardOrientation={boardOrientation ? Color.White : Color.Black}
|
||||||
currentPositionAtom={currentPositionAtom}
|
currentPositionAtom={currentPositionAtom}
|
||||||
showBestMoveArrow={showBestMoveArrow}
|
showBestMoveArrow={showBestMoveArrow}
|
||||||
|
|||||||
Reference in New Issue
Block a user