feat : add game panel info
This commit is contained in:
@@ -7,9 +7,17 @@ interface Props {
|
|||||||
max: number;
|
max: number;
|
||||||
label: string;
|
label: string;
|
||||||
xs?: number;
|
xs?: number;
|
||||||
|
marksFilter?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Slider({ min, max, label, atom, xs }: Props) {
|
export default function Slider({
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
label,
|
||||||
|
atom,
|
||||||
|
xs,
|
||||||
|
marksFilter = 1,
|
||||||
|
}: Props) {
|
||||||
const [value, setValue] = useAtom(atom);
|
const [value, setValue] = useAtom(atom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -28,13 +36,15 @@ export default function Slider({ min, max, label, atom, xs }: Props) {
|
|||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<MuiSlider
|
<MuiSlider
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
marks={Array.from({ length: max - min + 1 }, (_, i) => ({
|
marks={Array.from({ length: max - min + 1 }, (_, i) => ({
|
||||||
value: i + min,
|
value: i + min,
|
||||||
label: `${i + min}`,
|
label: `${i + min}`,
|
||||||
}))}
|
})).filter((_, i) => i % marksFilter === 0)}
|
||||||
|
step={1}
|
||||||
valueLabelDisplay="off"
|
valueLabelDisplay="off"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(_, value) => setValue(value as number)}
|
onChange={(_, value) => setValue(value as number)}
|
||||||
|
|||||||
28
src/hooks/useEngine.ts
Normal file
28
src/hooks/useEngine.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Stockfish16 } from "@/lib/engine/stockfish16";
|
||||||
|
import { UciEngine } from "@/lib/engine/uciEngine";
|
||||||
|
import { EngineName } from "@/types/enums";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export const useEngine = (engineName: EngineName) => {
|
||||||
|
const [engine, setEngine] = useState<UciEngine | null>(null);
|
||||||
|
|
||||||
|
const pickEngine = (engine: EngineName): UciEngine => {
|
||||||
|
switch (engine) {
|
||||||
|
case EngineName.Stockfish16:
|
||||||
|
return new Stockfish16();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const engine = pickEngine(engineName);
|
||||||
|
engine.init().then(() => {
|
||||||
|
setEngine(engine);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
engine.shutdown();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return engine;
|
||||||
|
};
|
||||||
@@ -105,10 +105,14 @@ export const useGameDatabase = (shouldFetchGames?: boolean) => {
|
|||||||
const { gameId } = router.query;
|
const { gameId } = router.query;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof gameId === "string") {
|
switch (typeof gameId) {
|
||||||
|
case "string":
|
||||||
getGame(parseInt(gameId)).then((game) => {
|
getGame(parseInt(gameId)).then((game) => {
|
||||||
setGameFromUrl(game);
|
setGameFromUrl(game);
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setGameFromUrl(undefined);
|
||||||
}
|
}
|
||||||
}, [gameId, setGameFromUrl, getGame]);
|
}, [gameId, setGameFromUrl, getGame]);
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,17 @@ export const formatGameToDatabase = (game: Chess): Omit<Game, "id"> => {
|
|||||||
site: headers.Site,
|
site: headers.Site,
|
||||||
date: headers.Date,
|
date: headers.Date,
|
||||||
round: headers.Round,
|
round: headers.Round,
|
||||||
white: headers.White,
|
white: {
|
||||||
black: headers.Black,
|
name: headers.White,
|
||||||
|
rating: headers.WhiteElo ? Number(headers.WhiteElo) : undefined,
|
||||||
|
},
|
||||||
|
black: {
|
||||||
|
name: headers.Black,
|
||||||
|
rating: headers.BlackElo ? Number(headers.BlackElo) : undefined,
|
||||||
|
},
|
||||||
result: headers.Result,
|
result: headers.Result,
|
||||||
|
termination: headers.Termination,
|
||||||
|
timeControl: headers.TimeControl,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
23
src/lib/engine/stockfish16.ts
Normal file
23
src/lib/engine/stockfish16.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { EngineName } from "@/types/enums";
|
||||||
|
import { UciEngine } from "./uciEngine";
|
||||||
|
|
||||||
|
export class Stockfish16 extends UciEngine {
|
||||||
|
constructor() {
|
||||||
|
const isWasmSupported = Stockfish16.isWasmSupported();
|
||||||
|
|
||||||
|
const enginePath = isWasmSupported
|
||||||
|
? "engines/stockfish-wasm/stockfish-nnue-16-single.js"
|
||||||
|
: "engines/stockfish.js";
|
||||||
|
|
||||||
|
super(EngineName.Stockfish16, enginePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isWasmSupported() {
|
||||||
|
return (
|
||||||
|
typeof WebAssembly === "object" &&
|
||||||
|
WebAssembly.validate(
|
||||||
|
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +1,30 @@
|
|||||||
import { Engine } from "@/types/enums";
|
import { EngineName } from "@/types/enums";
|
||||||
import { GameEval, LineEval, MoveEval } from "@/types/eval";
|
import { GameEval, LineEval, MoveEval } from "@/types/eval";
|
||||||
|
|
||||||
export class Stockfish {
|
export abstract class UciEngine {
|
||||||
private worker: Worker;
|
private worker: Worker;
|
||||||
private ready = false;
|
private ready = false;
|
||||||
|
private engineName: EngineName;
|
||||||
|
private multiPv = 3;
|
||||||
|
|
||||||
constructor() {
|
constructor(engineName: EngineName, enginePath: string) {
|
||||||
this.worker = new Worker(
|
this.engineName = engineName;
|
||||||
this.isWasmSupported()
|
|
||||||
? "engines/stockfish-wasm/stockfish-nnue-16-single.js"
|
|
||||||
: "engines/stockfish.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("Stockfish created");
|
this.worker = new Worker(enginePath);
|
||||||
}
|
|
||||||
|
|
||||||
public isWasmSupported() {
|
console.log(`${engineName} created`);
|
||||||
return (
|
|
||||||
typeof WebAssembly === "object" &&
|
|
||||||
WebAssembly.validate(
|
|
||||||
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
public async init(): Promise<void> {
|
||||||
await this.sendCommands(["uci"], "uciok");
|
await this.sendCommands(["uci"], "uciok");
|
||||||
await this.setMultiPv(3, false);
|
await this.setMultiPv(3, false);
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
console.log("Stockfish initialized");
|
console.log(`${this.engineName} initialized`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setMultiPv(multiPv: number, checkIsReady = true) {
|
public async setMultiPv(multiPv: number, checkIsReady = true) {
|
||||||
|
if (multiPv === this.multiPv) return;
|
||||||
|
|
||||||
if (checkIsReady) {
|
if (checkIsReady) {
|
||||||
this.throwErrorIfNotReady();
|
this.throwErrorIfNotReady();
|
||||||
}
|
}
|
||||||
@@ -44,11 +37,13 @@ export class Stockfish {
|
|||||||
[`setoption name MultiPV value ${multiPv}`, "isready"],
|
[`setoption name MultiPV value ${multiPv}`, "isready"],
|
||||||
"readyok"
|
"readyok"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.multiPv = multiPv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private throwErrorIfNotReady() {
|
private throwErrorIfNotReady() {
|
||||||
if (!this.ready) {
|
if (!this.ready) {
|
||||||
throw new Error("Stockfish is not ready");
|
throw new Error(`${this.engineName} is not ready`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +51,7 @@ export class Stockfish {
|
|||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.worker.postMessage("quit");
|
this.worker.postMessage("quit");
|
||||||
this.worker.terminate();
|
this.worker.terminate();
|
||||||
console.log("Stockfish shutdown");
|
console.log(`${this.engineName} shutdown`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isReady(): boolean {
|
public isReady(): boolean {
|
||||||
@@ -108,7 +103,7 @@ export class Stockfish {
|
|||||||
moves,
|
moves,
|
||||||
accuracy: { white: 82.34, black: 67.49 }, // TODO: Calculate accuracy
|
accuracy: { white: 82.34, black: 67.49 }, // TODO: Calculate accuracy
|
||||||
settings: {
|
settings: {
|
||||||
name: Engine.Stockfish16,
|
engine: this.engineName,
|
||||||
date: new Date().toISOString(),
|
date: new Date().toISOString(),
|
||||||
depth,
|
depth,
|
||||||
multiPv,
|
multiPv,
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Grid, Typography } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
import { Chessboard } from "react-chessboard";
|
import { Chessboard } from "react-chessboard";
|
||||||
import { useAtomValue } from "jotai";
|
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 { Arrow, Square } from "react-chessboard/dist/chessboard/types";
|
||||||
import { useChessActions } from "@/hooks/useChess";
|
import { useChessActions } from "@/hooks/useChess";
|
||||||
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
import { useCurrentMove } from "@/hooks/useCurrentMove";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import PlayerInfo from "./playerInfo";
|
||||||
|
|
||||||
export default function Board() {
|
export default function Board() {
|
||||||
const board = useAtomValue(boardAtom);
|
const board = useAtomValue(boardAtom);
|
||||||
const game = useAtomValue(gameAtom);
|
|
||||||
const boardOrientation = useAtomValue(boardOrientationAtom);
|
const boardOrientation = useAtomValue(boardOrientationAtom);
|
||||||
const boardActions = useChessActions(boardAtom);
|
const boardActions = useChessActions(boardAtom);
|
||||||
const currentMove = useCurrentMove();
|
const currentMove = useCurrentMove();
|
||||||
@@ -49,9 +49,6 @@ export default function Board() {
|
|||||||
return [[currentMove.from, currentMove.to, "#ffaa00"], bestMoveArrow];
|
return [[currentMove.from, currentMove.to, "#ffaa00"], bestMoveArrow];
|
||||||
}, [currentMove]);
|
}, [currentMove]);
|
||||||
|
|
||||||
const whiteLabel = game.header()["White"] || "White Player (?)";
|
|
||||||
const blackLabel = game.header()["Black"] || "Black Player (?)";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
@@ -62,11 +59,7 @@ export default function Board() {
|
|||||||
xs={12}
|
xs={12}
|
||||||
md={6}
|
md={6}
|
||||||
>
|
>
|
||||||
<Grid item container xs={12} justifyContent="center" alignItems="center">
|
<PlayerInfo color={boardOrientation ? "black" : "white"} />
|
||||||
<Typography variant="h4" align="center">
|
|
||||||
{boardOrientation ? blackLabel : whiteLabel}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
@@ -84,11 +77,7 @@ export default function Board() {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item container xs={12} justifyContent="center" alignItems="center">
|
<PlayerInfo color={boardOrientation ? "white" : "black"} />
|
||||||
<Typography variant="h4" align="center">
|
|
||||||
{boardOrientation ? whiteLabel : blackLabel}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
32
src/sections/analysis/board/playerInfo.tsx
Normal file
32
src/sections/analysis/board/playerInfo.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Stockfish } from "@/lib/engine/stockfish";
|
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
import { Grid } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
engineDepthAtom,
|
engineDepthAtom,
|
||||||
engineMultiPvAtom,
|
engineMultiPvAtom,
|
||||||
@@ -13,9 +12,11 @@ import { getFens } from "@/lib/chess";
|
|||||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from "@mui/lab";
|
||||||
import Slider from "@/components/slider";
|
import Slider from "@/components/slider";
|
||||||
|
import { useEngine } from "@/hooks/useEngine";
|
||||||
|
import { EngineName } from "@/types/enums";
|
||||||
|
|
||||||
export default function AnalyzePanel() {
|
export default function AnalyzePanel() {
|
||||||
const [engine, setEngine] = useState<Stockfish | null>(null);
|
const engine = useEngine(EngineName.Stockfish16);
|
||||||
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
|
const [evaluationInProgress, setEvaluationInProgress] = useState(false);
|
||||||
const engineDepth = useAtomValue(engineDepthAtom);
|
const engineDepth = useAtomValue(engineDepthAtom);
|
||||||
const engineMultiPv = useAtomValue(engineMultiPvAtom);
|
const engineMultiPv = useAtomValue(engineMultiPvAtom);
|
||||||
@@ -23,24 +24,14 @@ export default function AnalyzePanel() {
|
|||||||
const setEval = useSetAtom(gameEvalAtom);
|
const setEval = useSetAtom(gameEvalAtom);
|
||||||
const game = useAtomValue(gameAtom);
|
const game = useAtomValue(gameAtom);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const engine = new Stockfish();
|
|
||||||
engine.init().then(() => {
|
|
||||||
setEngine(engine);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
engine.shutdown();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const readyToAnalyse =
|
const readyToAnalyse =
|
||||||
engine?.isReady() && game.history().length > 0 && !evaluationInProgress;
|
engine?.isReady() && game.history().length > 0 && !evaluationInProgress;
|
||||||
|
|
||||||
const handleAnalyze = async () => {
|
const handleAnalyze = async () => {
|
||||||
const gameFens = getFens(game);
|
const gameFens = getFens(game);
|
||||||
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress)
|
if (!engine?.isReady() || gameFens.length === 0 || evaluationInProgress) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setEvaluationInProgress(true);
|
setEvaluationInProgress(true);
|
||||||
|
|
||||||
@@ -67,7 +58,13 @@ export default function AnalyzePanel() {
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
rowGap={4}
|
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
|
<Slider
|
||||||
label="Number of lines"
|
label="Number of lines"
|
||||||
|
|||||||
64
src/sections/analysis/reviewPanelHeader/gamePanel.tsx
Normal file
64
src/sections/analysis/reviewPanelHeader/gamePanel.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
import { Grid, Typography } from "@mui/material";
|
import { Divider, Grid, Typography } from "@mui/material";
|
||||||
import LoadGame from "./loadGame";
|
|
||||||
import AnalyzePanel from "./analyzePanel";
|
import AnalyzePanel from "./analyzePanel";
|
||||||
|
import GamePanel from "./gamePanel";
|
||||||
|
import LoadGame from "./loadGame";
|
||||||
|
|
||||||
export default function ReviewPanelHeader() {
|
export default function ReviewPanelHeader() {
|
||||||
return (
|
return (
|
||||||
@@ -27,7 +28,19 @@ export default function ReviewPanelHeader() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
container
|
||||||
|
xs={12}
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
gap={4}
|
||||||
|
>
|
||||||
|
<GamePanel />
|
||||||
<LoadGame />
|
<LoadGame />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Divider sx={{ width: "90%", marginY: 3 }} />
|
||||||
|
|
||||||
<AnalyzePanel />
|
<AnalyzePanel />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import {
|
|||||||
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
import { useGameDatabase } from "@/hooks/useGameDatabase";
|
||||||
import { useAtomValue, useSetAtom } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import { Chess } from "chess.js";
|
import { Chess } from "chess.js";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
export default function LoadGame() {
|
export default function LoadGame() {
|
||||||
|
const router = useRouter();
|
||||||
const game = useAtomValue(gameAtom);
|
const game = useAtomValue(gameAtom);
|
||||||
const gameActions = useChessActions(gameAtom);
|
const gameActions = useChessActions(gameAtom);
|
||||||
const boardActions = useChessActions(boardAtom);
|
const boardActions = useChessActions(boardAtom);
|
||||||
@@ -45,11 +47,17 @@ export default function LoadGame() {
|
|||||||
loadGame();
|
loadGame();
|
||||||
}, [gameFromUrl, game, resetAndSetGamePgn, setEval]);
|
}, [gameFromUrl, game, resetAndSetGamePgn, setEval]);
|
||||||
|
|
||||||
if (gameFromUrl) return null;
|
const isGameLoaded = gameFromUrl !== undefined || !!game.header().White;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid item container xs={12} justifyContent="center" alignItems="center">
|
<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>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/sections/analysis/reviewPanelHeader/playerInfo.tsx
Normal file
38
src/sections/analysis/reviewPanelHeader/playerInfo.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,15 +5,16 @@ import { Chess } from "chess.js";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setGame?: (game: Chess) => void;
|
setGame?: (game: Chess) => void;
|
||||||
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LoadGameButton({ setGame }: Props) {
|
export default function LoadGameButton({ setGame, label }: Props) {
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button variant="contained" onClick={() => setOpenDialog(true)}>
|
<Button variant="contained" onClick={() => setOpenDialog(true)}>
|
||||||
Add game
|
{label || "Add game"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<NewGameDialog
|
<NewGameDialog
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ export enum GameOrigin {
|
|||||||
Lichess = "lichess",
|
Lichess = "lichess",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Engine {
|
export enum EngineName {
|
||||||
Stockfish16 = "stockfish_16",
|
Stockfish16 = "stockfish_16",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Engine } from "./enums";
|
import { EngineName } from "./enums";
|
||||||
|
|
||||||
export interface MoveEval {
|
export interface MoveEval {
|
||||||
bestMove: string;
|
bestMove: string;
|
||||||
@@ -19,7 +19,7 @@ export interface Accuracy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface EngineSettings {
|
export interface EngineSettings {
|
||||||
name: Engine;
|
engine: EngineName;
|
||||||
depth: number;
|
depth: number;
|
||||||
multiPv: number;
|
multiPv: number;
|
||||||
date: string;
|
date: string;
|
||||||
|
|||||||
@@ -7,8 +7,15 @@ export interface Game {
|
|||||||
site?: string;
|
site?: string;
|
||||||
date?: string;
|
date?: string;
|
||||||
round?: string;
|
round?: string;
|
||||||
white?: string;
|
white: Player;
|
||||||
black?: string;
|
black: Player;
|
||||||
result?: string;
|
result?: string;
|
||||||
eval?: GameEval;
|
eval?: GameEval;
|
||||||
|
termination?: string;
|
||||||
|
timeControl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Player {
|
||||||
|
name?: string;
|
||||||
|
rating?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user