feat : init game eval

This commit is contained in:
GuillaumeSD
2024-02-17 00:19:40 +01:00
parent 36f7577ac0
commit 770ae13517
12 changed files with 200 additions and 95 deletions

View File

@@ -1,14 +1,16 @@
import { GameEval, MoveEval } from "@/types/eval";
export class Stockfish {
private worker: Worker;
private ready: boolean = false;
constructor() {
this.worker = new Worker(
this.isWasmSupported() ? "./stockfish.wasm.js" : "./stockfish.js"
this.isWasmSupported()
? "engines/stockfish.wasm.js"
: "engines/stockfish.js"
);
this.sendCommands(["uci"], "uciok");
this.sendCommands(["setoption name MultiPV value 2", "isready"], "readyok");
console.log("Stockfish created");
}
@@ -21,6 +23,25 @@ export class Stockfish {
);
}
public async init(): Promise<void> {
await this.sendCommands(["uci"], "uciok");
await this.sendCommands(
["setoption name MultiPV value 2", "isready"],
"readyok"
);
this.ready = true;
}
public shutdown(): void {
this.ready = false;
this.worker.postMessage("quit");
this.worker.terminate();
}
public isReady(): boolean {
return this.ready;
}
private async sendCommands(
commands: string[],
finalMessage: string
@@ -42,15 +63,20 @@ export class Stockfish {
}
public async evaluateGame(fens: string[], depth = 16): Promise<GameEval> {
this.ready = false;
console.log("Evaluating game");
await this.sendCommands(["ucinewgame", "isready"], "readyok");
this.worker.postMessage("position startpos");
const moves: MoveEval[] = [];
for (const fen of fens) {
console.log(`Evaluating position: ${fen}`);
const result = await this.evaluatePosition(fen, depth);
moves.push(result);
}
this.ready = true;
console.log("Game evaluated");
return { moves };
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { GameEval } from "@/types/eval";
import { atom } from "jotai";
export const gameReviewAtom = atom<GameEval | undefined>(undefined);

View File

@@ -1,8 +1,34 @@
import { useEffect, useState } from "react";
import ReviewResult from "./reviewResult";
import SelectDepth from "./selectDepth";
import SelectGameOrigin from "./selectGame/selectGameOrigin";
import { Stockfish } from "@/lib/engine/stockfish";
import { useAtomValue } from "jotai";
import { gameFensAtom } from "./selectGame/gameOrigin.state";
export default function ReviewPanelBody() {
const [engine, setEngine] = useState<Stockfish | null>(null);
const gameFens = useAtomValue(gameFensAtom);
useEffect(() => {
const engine = new Stockfish();
engine.init().then(() => {
console.log("Engine initialized");
});
setEngine(engine);
return () => {
engine.shutdown();
console.log("Engine shutdown");
};
}, []);
const handleAnalyse = () => {
if (engine?.isReady() && gameFens) {
engine.evaluateGame(gameFens);
}
};
return (
<div id="review-panel-main">
<h1 id="review-panel-title" className="white">
@@ -13,7 +39,7 @@ export default function ReviewPanelBody() {
<button id="review-button" className="std-btn success-btn white">
<img src="analysis_icon.png" height="25" />
<b>Analyse</b>
<b onClick={handleAnalyse}>Analyse</b>
</button>
<SelectDepth />

View File

@@ -0,0 +1,3 @@
import { atom } from "jotai";
export const gameFensAtom = atom<string[]>([]);

View File

@@ -1,8 +1,27 @@
import { GameOrigin } from "@/types/enums";
import { useSetAtom } from "jotai";
import { gameFensAtom } from "./gameOrigin.state";
import { useEffect, useState } from "react";
import { Chess } from "chess.js";
interface Props {
gameOrigin: GameOrigin;
placeholder?: string;
}
export default function InputGame({ placeholder }: Props) {
const [gamePgn, setGamePgn] = useState("");
const setGameFens = useSetAtom(gameFensAtom);
useEffect(() => {
const chess = new Chess();
chess.loadPgn(gamePgn);
const fens = chess.history({ verbose: true }).map((move) => {
return move.after;
});
setGameFens(fens);
}, [gamePgn]);
return (
<textarea
id="pgn"
@@ -11,6 +30,8 @@ export default function InputGame({ placeholder }: Props) {
rows={10}
spellCheck="false"
placeholder={placeholder}
value={gamePgn}
onChange={(e) => setGamePgn(e.target.value)}
/>
);
}

View File

@@ -5,6 +5,7 @@ import { GameOrigin } from "@/types/enums";
export default function SelectGameOrigin() {
const [gameOrigin, setGameOrigin] = useState(GameOrigin.Pgn);
return (
<>
<div id="load-type-dropdown-container" className="white">
@@ -37,9 +38,13 @@ const gameOriginLabel: Record<GameOrigin, string> = {
const renderSelectGameInfo = (gameOrigin: GameOrigin) => {
switch (gameOrigin) {
case GameOrigin.Pgn:
return <InputGame placeholder="Enter PGN here..." />;
return (
<InputGame gameOrigin={gameOrigin} placeholder="Enter PGN here..." />
);
case GameOrigin.Json:
return <InputGame placeholder="Enter JSON here..." />;
return (
<InputGame gameOrigin={gameOrigin} placeholder="Enter JSON here..." />
);
default:
return <SelectGameAccount gameOrigin={gameOrigin} />;
}

View File

@@ -1,14 +1,14 @@
interface MoveEval {
export interface MoveEval {
bestMove: string;
lines: LineEval[];
}
interface LineEval {
export interface LineEval {
pv: string[];
score?: number;
mate?: number;
}
interface GameEval {
export interface GameEval {
moves: MoveEval[];
}