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:
GuillaumeSD
2024-04-12 02:49:48 +02:00
parent e9e772ca81
commit daaf145fa9
6 changed files with 211 additions and 6 deletions

BIN
public/captured-pieces.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View 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;
};

View File

@@ -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>

View File

@@ -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,

View File

@@ -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;
};

View File

@@ -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}