feat : new captured piece icons
This commit is contained in:
@@ -16,15 +16,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
Chesskit is an open-source chess web app to play, view and analyze your chess games for free on any device with Stockfish !
|
Chesskit is an open-source chess website to play, view, analyze and review your chess games for free on any device with Stockfish !
|
||||||
|
|
||||||
## Mission
|
## Mission
|
||||||
|
|
||||||
It aims to offer all the features it can from the best chess apps, while being free and open-source. It is designed to be easy to use, fast, and reliable.
|
Chesskit aims to offer all the chess related features it can, while being free and open-source. It is designed to be easy to use, fast, and reliable.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Load and analyze games from [chess.com](https://chess.com) and [lichess.org](https://lichess.org)
|
- Load and review games from [chess.com](https://chess.com) and [lichess.org](https://lichess.org)
|
||||||
- Analysis board with live engine evaluation, custom arrows, evaluation graph, ...
|
- Analysis board with live engine evaluation, custom arrows, evaluation graph, ...
|
||||||
- Moves classification (Brilliant, Great, Good, Mistake, Blunder, ...)
|
- Moves classification (Brilliant, Great, Good, Mistake, Blunder, ...)
|
||||||
- Chess960 and Puzzles support
|
- Chess960 and Puzzles support
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,7 +1,7 @@
|
|||||||
import { getCapturedPieces, getMaterialDifference } from "@/lib/chess";
|
import { getCapturedPieces, getMaterialDifference } from "@/lib/chess";
|
||||||
import { Color } from "@/types/enums";
|
import { Color } from "@/types/enums";
|
||||||
import { Grid2 as Grid, Typography } from "@mui/material";
|
import { Box, Grid2 as Grid, Stack, Typography } from "@mui/material";
|
||||||
import { CSSProperties, useMemo } from "react";
|
import { ReactElement, useMemo } from "react";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
fen: string;
|
fen: string;
|
||||||
@@ -11,9 +11,11 @@ export interface Props {
|
|||||||
const PIECE_SCALE = 0.55;
|
const PIECE_SCALE = 0.55;
|
||||||
|
|
||||||
export default function CapturedPieces({ fen, color }: Props) {
|
export default function CapturedPieces({ fen, color }: Props) {
|
||||||
const cssProps = useMemo(() => {
|
const piecesComponents = useMemo(() => {
|
||||||
const capturedPieces = getCapturedPieces(fen, color);
|
const capturedPieces = getCapturedPieces(fen, color);
|
||||||
return getCapturedPiecesCSSProps(capturedPieces, color);
|
return capturedPieces.map(({ piece, count }) =>
|
||||||
|
getCapturedPiecesComponents(piece, count)
|
||||||
|
);
|
||||||
}, [fen, color]);
|
}, [fen, color]);
|
||||||
|
|
||||||
const materialDiff = useMemo(() => {
|
const materialDiff = useMemo(() => {
|
||||||
@@ -22,19 +24,16 @@ export default function CapturedPieces({ fen, color }: Props) {
|
|||||||
}, [fen, color]);
|
}, [fen, color]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container alignItems="end" columnGap={0.5} size="auto">
|
<Grid
|
||||||
{cssProps.map((cssProp, i) => (
|
container
|
||||||
<span
|
alignItems="end"
|
||||||
key={i}
|
spacing={0.7}
|
||||||
style={{
|
size="auto"
|
||||||
...cssProp,
|
marginLeft={`-${0.3 * PIECE_SCALE}rem`}
|
||||||
backgroundSize: `${34.2 * PIECE_SCALE}rem ${30.6 * PIECE_SCALE}rem`,
|
>
|
||||||
backgroundImage: "url(/captured-pieces.png)",
|
<Stack direction="row" spacing={0.1}>
|
||||||
backgroundRepeat: "no-repeat",
|
{piecesComponents}
|
||||||
display: "inline-block",
|
</Stack>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{materialDiff > 0 && (
|
{materialDiff > 0 && (
|
||||||
<Typography
|
<Typography
|
||||||
@@ -49,115 +48,28 @@ export default function CapturedPieces({ fen, color }: Props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCapturedPiecesCSSProps = (
|
const getCapturedPiecesComponents = (
|
||||||
capturedPieces: Record<string, number | undefined>,
|
pieceSymbol: string,
|
||||||
color: Color
|
pieceCount: number | undefined
|
||||||
): CSSProperties[] => {
|
): ReactElement | null => {
|
||||||
const cssProps: CSSProperties[] = [];
|
if (!pieceCount) return null;
|
||||||
|
|
||||||
if (color === Color.Black) {
|
return (
|
||||||
if (capturedPieces.P) {
|
<Stack
|
||||||
cssProps.push({
|
direction="row"
|
||||||
backgroundPositionX: `-${18 * PIECE_SCALE}rem`,
|
key={pieceSymbol}
|
||||||
backgroundPositionY: `${
|
spacing={`-${1.2 * PIECE_SCALE}rem`}
|
||||||
-20.1 * PIECE_SCALE + capturedPieces.P * 2.5 * PIECE_SCALE
|
>
|
||||||
}rem`,
|
{new Array(pieceCount).fill(
|
||||||
width: `${0.6 * PIECE_SCALE + capturedPieces.P * 0.7 * PIECE_SCALE}rem`,
|
<Box
|
||||||
height: `${1.7 * PIECE_SCALE}rem`,
|
width={`${2 * PIECE_SCALE}rem`}
|
||||||
});
|
height={`${2 * PIECE_SCALE}rem`}
|
||||||
}
|
sx={{
|
||||||
|
backgroundImage: `url(/piece/cardinal/${pieceSymbol}.svg)`,
|
||||||
if (capturedPieces.B) {
|
backgroundRepeat: "no-repeat",
|
||||||
cssProps.push({
|
}}
|
||||||
backgroundPosition: `-${24.7 * PIECE_SCALE}rem ${
|
/>
|
||||||
-5.1 * PIECE_SCALE + capturedPieces.B * 2.6 * PIECE_SCALE
|
)}
|
||||||
}rem`,
|
</Stack>
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Game, Player } from "@/types/game";
|
|||||||
import { Chess, PieceSymbol, Square } from "chess.js";
|
import { Chess, PieceSymbol, Square } from "chess.js";
|
||||||
import { getPositionWinPercentage } from "./engine/helpers/winPercentage";
|
import { getPositionWinPercentage } from "./engine/helpers/winPercentage";
|
||||||
import { Color } from "@/types/enums";
|
import { Color } from "@/types/enums";
|
||||||
|
import { Piece } from "react-chessboard/dist/chessboard/types";
|
||||||
|
|
||||||
export const getEvaluateGameParams = (game: Chess): EvaluateGameParams => {
|
export const getEvaluateGameParams = (game: Chess): EvaluateGameParams => {
|
||||||
const history = game.history({ verbose: true });
|
const history = game.history({ verbose: true });
|
||||||
@@ -288,29 +289,56 @@ export const isCheck = (fen: string): boolean => {
|
|||||||
export const getCapturedPieces = (
|
export const getCapturedPieces = (
|
||||||
fen: string,
|
fen: string,
|
||||||
color: Color
|
color: Color
|
||||||
): Record<string, number | undefined> => {
|
): {
|
||||||
const capturedPieces: Record<string, number | undefined> = {};
|
piece: string;
|
||||||
if (color === Color.White) {
|
count: number;
|
||||||
capturedPieces.p = 8;
|
}[] => {
|
||||||
capturedPieces.r = 2;
|
const capturedPieces =
|
||||||
capturedPieces.n = 2;
|
color === Color.White
|
||||||
capturedPieces.b = 2;
|
? [
|
||||||
capturedPieces.q = 1;
|
{ piece: "p", count: 8 },
|
||||||
} else {
|
{ piece: "b", count: 2 },
|
||||||
capturedPieces.P = 8;
|
{ piece: "n", count: 2 },
|
||||||
capturedPieces.R = 2;
|
{ piece: "r", count: 2 },
|
||||||
capturedPieces.N = 2;
|
{ piece: "q", count: 1 },
|
||||||
capturedPieces.B = 2;
|
]
|
||||||
capturedPieces.Q = 1;
|
: [
|
||||||
}
|
{ piece: "P", count: 8 },
|
||||||
|
{ piece: "B", count: 2 },
|
||||||
|
{ piece: "N", count: 2 },
|
||||||
|
{ piece: "R", count: 2 },
|
||||||
|
{ piece: "Q", count: 1 },
|
||||||
|
];
|
||||||
|
|
||||||
const fenPiecePlacement = fen.split(" ")[0];
|
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;
|
return capturedPieces.map(({ piece, count }) => {
|
||||||
|
const piecesLeftCount = fenPiecePlacement.match(
|
||||||
|
new RegExp(piece, "g")
|
||||||
|
)?.length;
|
||||||
|
const newPiece = pieceFenToSymbol[piece] ?? piece;
|
||||||
|
if (!count) return { piece: newPiece, count };
|
||||||
|
|
||||||
|
return {
|
||||||
|
piece: newPiece,
|
||||||
|
count: count - (piecesLeftCount ?? 0),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const pieceFenToSymbol: Record<string, Piece | undefined> = {
|
||||||
|
p: "bP",
|
||||||
|
b: "bB",
|
||||||
|
n: "bN",
|
||||||
|
r: "bR",
|
||||||
|
q: "bQ",
|
||||||
|
k: "bK",
|
||||||
|
P: "wP",
|
||||||
|
B: "wB",
|
||||||
|
N: "wN",
|
||||||
|
R: "wR",
|
||||||
|
Q: "wQ",
|
||||||
|
K: "wK",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLineEvalLabel = (
|
export const getLineEvalLabel = (
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function Layout({ children }: PropsWithChildren) {
|
|||||||
main: red[400],
|
main: red[400],
|
||||||
},
|
},
|
||||||
primary: {
|
primary: {
|
||||||
main: "#5d9948",
|
main: "#5a9943",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: isDarkMode ? "#424242" : "#ffffff",
|
main: isDarkMode ? "#424242" : "#ffffff",
|
||||||
|
|||||||
Reference in New Issue
Block a user