diff --git a/README.md b/README.md index 7794572..edb0564 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,15 @@
-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 -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 -- 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, ... - Moves classification (Brilliant, Great, Good, Mistake, Blunder, ...) - Chess960 and Puzzles support diff --git a/public/captured-pieces.png b/public/captured-pieces.png deleted file mode 100644 index 9caaad9..0000000 Binary files a/public/captured-pieces.png and /dev/null differ diff --git a/src/components/board/capturedPieces.tsx b/src/components/board/capturedPieces.tsx index 4c612a5..39e1f18 100644 --- a/src/components/board/capturedPieces.tsx +++ b/src/components/board/capturedPieces.tsx @@ -1,7 +1,7 @@ import { getCapturedPieces, getMaterialDifference } from "@/lib/chess"; import { Color } from "@/types/enums"; -import { Grid2 as Grid, Typography } from "@mui/material"; -import { CSSProperties, useMemo } from "react"; +import { Box, Grid2 as Grid, Stack, Typography } from "@mui/material"; +import { ReactElement, useMemo } from "react"; export interface Props { fen: string; @@ -11,9 +11,11 @@ export interface Props { const PIECE_SCALE = 0.55; export default function CapturedPieces({ fen, color }: Props) { - const cssProps = useMemo(() => { + const piecesComponents = useMemo(() => { const capturedPieces = getCapturedPieces(fen, color); - return getCapturedPiecesCSSProps(capturedPieces, color); + return capturedPieces.map(({ piece, count }) => + getCapturedPiecesComponents(piece, count) + ); }, [fen, color]); const materialDiff = useMemo(() => { @@ -22,19 +24,16 @@ export default function CapturedPieces({ fen, color }: Props) { }, [fen, color]); return ( - - {cssProps.map((cssProp, i) => ( - - ))} + + + {piecesComponents} + {materialDiff > 0 && ( , - color: Color -): CSSProperties[] => { - const cssProps: CSSProperties[] = []; +const getCapturedPiecesComponents = ( + pieceSymbol: string, + pieceCount: number | undefined +): ReactElement | null => { + if (!pieceCount) return null; - 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; + return ( + + {new Array(pieceCount).fill( + + )} + + ); }; diff --git a/src/lib/chess.ts b/src/lib/chess.ts index 4b9130a..15ca5f5 100644 --- a/src/lib/chess.ts +++ b/src/lib/chess.ts @@ -3,6 +3,7 @@ import { Game, Player } from "@/types/game"; import { Chess, PieceSymbol, Square } from "chess.js"; import { getPositionWinPercentage } from "./engine/helpers/winPercentage"; import { Color } from "@/types/enums"; +import { Piece } from "react-chessboard/dist/chessboard/types"; export const getEvaluateGameParams = (game: Chess): EvaluateGameParams => { const history = game.history({ verbose: true }); @@ -288,29 +289,56 @@ export const isCheck = (fen: string): boolean => { export const getCapturedPieces = ( fen: string, color: Color -): Record => { - const capturedPieces: Record = {}; - 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; - } +): { + piece: string; + count: number; +}[] => { + const capturedPieces = + color === Color.White + ? [ + { piece: "p", count: 8 }, + { piece: "b", count: 2 }, + { piece: "n", count: 2 }, + { piece: "r", count: 2 }, + { piece: "q", count: 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]; - 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 = { + 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 = ( diff --git a/src/sections/layout/index.tsx b/src/sections/layout/index.tsx index 87d5c2a..8cc0176 100644 --- a/src/sections/layout/index.tsx +++ b/src/sections/layout/index.tsx @@ -16,7 +16,7 @@ export default function Layout({ children }: PropsWithChildren) { main: red[400], }, primary: { - main: "#5d9948", + main: "#5a9943", }, secondary: { main: isDarkMode ? "#424242" : "#ffffff",