feat : add game panel info

This commit is contained in:
GuillaumeSD
2024-02-24 16:32:22 +01:00
parent 6156e4a228
commit 035591208f
17 changed files with 290 additions and 73 deletions

View File

@@ -1,15 +1,15 @@
import { Grid, Typography } from "@mui/material";
import { Grid } from "@mui/material";
import { Chessboard } from "react-chessboard";
import { useAtomValue } from "jotai";
import { boardAtom, boardOrientationAtom, gameAtom } from "./states";
import { boardAtom, boardOrientationAtom } from "../states";
import { Arrow, Square } from "react-chessboard/dist/chessboard/types";
import { useChessActions } from "@/hooks/useChess";
import { useCurrentMove } from "@/hooks/useCurrentMove";
import { useMemo } from "react";
import PlayerInfo from "./playerInfo";
export default function Board() {
const board = useAtomValue(boardAtom);
const game = useAtomValue(gameAtom);
const boardOrientation = useAtomValue(boardOrientationAtom);
const boardActions = useChessActions(boardAtom);
const currentMove = useCurrentMove();
@@ -49,9 +49,6 @@ export default function Board() {
return [[currentMove.from, currentMove.to, "#ffaa00"], bestMoveArrow];
}, [currentMove]);
const whiteLabel = game.header()["White"] || "White Player (?)";
const blackLabel = game.header()["Black"] || "Black Player (?)";
return (
<Grid
item
@@ -62,11 +59,7 @@ export default function Board() {
xs={12}
md={6}
>
<Grid item container xs={12} justifyContent="center" alignItems="center">
<Typography variant="h4" align="center">
{boardOrientation ? blackLabel : whiteLabel}
</Typography>
</Grid>
<PlayerInfo color={boardOrientation ? "black" : "white"} />
<Grid
item
@@ -84,11 +77,7 @@ export default function Board() {
/>
</Grid>
<Grid item container xs={12} justifyContent="center" alignItems="center">
<Typography variant="h4" align="center">
{boardOrientation ? whiteLabel : blackLabel}
</Typography>
</Grid>
<PlayerInfo color={boardOrientation ? "white" : "black"} />
</Grid>
);
}

View File

@@ -0,0 +1,32 @@
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { Grid, Typography } from "@mui/material";
import { useAtomValue } from "jotai";
import { gameAtom } from "../states";
interface Props {
color: "white" | "black";
}
export default function PlayerInfo({ color }: Props) {
const { gameFromUrl } = useGameDatabase();
const game = useAtomValue(gameAtom);
const playerName =
gameFromUrl?.[color]?.name ||
game.header()[color === "white" ? "White" : "Black"];
return (
<Grid
item
container
xs={12}
justifyContent="center"
alignItems="center"
gap={1}
>
<Typography variant="h4">
{playerName || (color === "white" ? "White" : "Black")}
</Typography>
</Grid>
);
}

View File

@@ -1,7 +1,6 @@
import { Stockfish } from "@/lib/engine/stockfish";
import { Icon } from "@iconify/react";
import { Grid } from "@mui/material";
import { useEffect, useState } from "react";
import { useState } from "react";
import {
engineDepthAtom,
engineMultiPvAtom,
@@ -13,9 +12,11 @@ import { getFens } from "@/lib/chess";
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { LoadingButton } from "@mui/lab";
import Slider from "@/components/slider";
import { useEngine } from "@/hooks/useEngine";
import { EngineName } from "@/types/enums";
export default function AnalyzePanel() {
const [engine, setEngine] = useState<Stockfish | null>(null);
const engine = useEngine(EngineName.Stockfish16);
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
const engineDepth = useAtomValue(engineDepthAtom);
const engineMultiPv = useAtomValue(engineMultiPvAtom);
@@ -23,24 +24,14 @@ export default function AnalyzePanel() {
const setEval = useSetAtom(gameEvalAtom);
const game = useAtomValue(gameAtom);
useEffect(() => {
const engine = new Stockfish();
engine.init().then(() => {
setEngine(engine);
});
return () => {
engine.shutdown();
};
}, []);
const readyToAnalyse =
engine?.isReady() && game.history().length > 0 && !evaluationInProgress;
const handleAnalyze = async () => {
const gameFens = getFens(game);
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress)
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress) {
return;
}
setEvaluationInProgress(true);
@@ -67,7 +58,13 @@ export default function AnalyzePanel() {
alignItems="center"
rowGap={4}
>
<Slider label="Maximum depth" atom={engineDepthAtom} min={10} max={30} />
<Slider
label="Maximum depth"
atom={engineDepthAtom}
min={10}
max={30}
marksFilter={2}
/>
<Slider
label="Number of lines"

View File

@@ -0,0 +1,64 @@
import { Grid, Typography } from "@mui/material";
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { useAtomValue } from "jotai";
import { gameAtom } from "../states";
import PlayerInfo from "./playerInfo";
export default function GamePanel() {
const { gameFromUrl } = useGameDatabase();
const game = useAtomValue(gameAtom);
const hasGameInfo = gameFromUrl !== undefined || !!game.header().White;
if (!hasGameInfo) {
return (
<Grid item container xs={12} justifyContent="center" alignItems="center">
<Typography variant="h6">No game loaded</Typography>
</Grid>
);
}
return (
<Grid
item
container
xs={12}
justifyContent="center"
alignItems="center"
gap={3}
marginY={1}
>
<Grid item container xs={12} justifyContent="center" alignItems="center">
<PlayerInfo color="white" />
<Grid item container xs={1} justifyContent="center" alignItems="center">
<Typography variant="h6">vs</Typography>
</Grid>
<PlayerInfo color="black" />
</Grid>
<Grid
item
container
xs={10}
justifyContent="space-evenly"
alignItems="center"
gap={3}
>
<Typography>
Site : {gameFromUrl?.site || game.header().Site || "?"}
</Typography>
<Typography>
Date : {gameFromUrl?.date || game.header().Date || "?"}
</Typography>
<Typography>
Result :{" "}
{gameFromUrl?.termination || game.header().Termination || "?"}
</Typography>
</Grid>
</Grid>
);
}

View File

@@ -1,7 +1,8 @@
import { Icon } from "@iconify/react";
import { Grid, Typography } from "@mui/material";
import LoadGame from "./loadGame";
import { Divider, Grid, Typography } from "@mui/material";
import AnalyzePanel from "./analyzePanel";
import GamePanel from "./gamePanel";
import LoadGame from "./loadGame";
export default function ReviewPanelHeader() {
return (
@@ -27,7 +28,19 @@ export default function ReviewPanelHeader() {
</Typography>
</Grid>
<LoadGame />
<Grid
item
container
xs={12}
justifyContent="center"
alignItems="center"
gap={4}
>
<GamePanel />
<LoadGame />
</Grid>
<Divider sx={{ width: "90%", marginY: 3 }} />
<AnalyzePanel />
</Grid>

View File

@@ -11,8 +11,10 @@ import {
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { useAtomValue, useSetAtom } from "jotai";
import { Chess } from "chess.js";
import { useRouter } from "next/router";
export default function LoadGame() {
const router = useRouter();
const game = useAtomValue(gameAtom);
const gameActions = useChessActions(gameAtom);
const boardActions = useChessActions(boardAtom);
@@ -45,11 +47,17 @@ export default function LoadGame() {
loadGame();
}, [gameFromUrl, game, resetAndSetGamePgn, setEval]);
if (gameFromUrl) return null;
const isGameLoaded = gameFromUrl !== undefined || !!game.header().White;
return (
<Grid item container xs={12} justifyContent="center" alignItems="center">
<LoadGameButton setGame={(game) => resetAndSetGamePgn(game.pgn())} />
<LoadGameButton
label={isGameLoaded ? "Load another game" : "Load game"}
setGame={async (game) => {
await router.push("");
resetAndSetGamePgn(game.pgn());
}}
/>
</Grid>
);
}

View File

@@ -0,0 +1,38 @@
import { useGameDatabase } from "@/hooks/useGameDatabase";
import { Grid, Typography } from "@mui/material";
import { useAtomValue } from "jotai";
import { gameAtom } from "../states";
interface Props {
color: "white" | "black";
}
export default function PlayerInfo({ color }: Props) {
const { gameFromUrl } = useGameDatabase();
const game = useAtomValue(gameAtom);
const rating =
gameFromUrl?.[color]?.rating ||
game.header()[color === "white" ? "WhiteElo" : "BlackElo"];
const playerName =
gameFromUrl?.[color]?.name ||
game.header()[color === "white" ? "White" : "Black"];
return (
<Grid
item
container
xs={5}
justifyContent={color === "white" ? "flex-end" : "flex-start"}
alignItems="center"
gap={1}
>
<Typography variant="h6">
{playerName || (color === "white" ? "White" : "Black")}
</Typography>
<Typography variant="h6">{rating ? `(${rating})` : "(?)"}</Typography>
</Grid>
);
}

View File

@@ -5,15 +5,16 @@ import { Chess } from "chess.js";
interface Props {
setGame?: (game: Chess) => void;
label?: string;
}
export default function LoadGameButton({ setGame }: Props) {
export default function LoadGameButton({ setGame, label }: Props) {
const [openDialog, setOpenDialog] = useState(false);
return (
<>
<Button variant="contained" onClick={() => setOpenDialog(true)}>
Add game
{label || "Add game"}
</Button>
<NewGameDialog