feat : add live position evaluation

This commit is contained in:
GuillaumeSD
2024-02-24 21:05:45 +01:00
parent 7b328d3159
commit 1f748f99ca
8 changed files with 220 additions and 105 deletions

View File

@@ -57,3 +57,23 @@ export const getGameToSave = (game: Chess, board: Chess): Chess => {
return board;
};
export const moveLineUciToSan = (
fen: string
): ((moveUci: string) => string) => {
const game = new Chess(fen);
return (moveUci: string): string => {
try {
const move = game.move({
from: moveUci.slice(0, 2),
to: moveUci.slice(2, 4),
promotion: moveUci.slice(4, 5) || undefined,
});
return move.san;
} catch (e) {
return moveUci;
}
};
};

View File

@@ -1,5 +1,10 @@
import { EngineName } from "@/types/enums";
import { GameEval, LineEval, MoveEval } from "@/types/eval";
import {
EvaluatePositionWithUpdateParams,
GameEval,
LineEval,
MoveEval,
} from "@/types/eval";
export abstract class UciEngine {
private worker: Worker;
@@ -18,15 +23,15 @@ export abstract class UciEngine {
public async init(): Promise<void> {
await this.sendCommands(["uci"], "uciok");
await this.setMultiPv(this.multiPv, false);
await this.setMultiPv(this.multiPv, true);
this.ready = true;
console.log(`${this.engineName} initialized`);
}
public async setMultiPv(multiPv: number, checkIsReady = true) {
if (multiPv === this.multiPv) return;
private async setMultiPv(multiPv: number, initCase = false) {
if (!initCase) {
if (multiPv === this.multiPv) return;
if (checkIsReady) {
this.throwErrorIfNotReady();
}
@@ -59,15 +64,23 @@ export abstract class UciEngine {
return this.ready;
}
private async stopSearch(): Promise<void> {
await this.sendCommands(["stop", "isready"], "readyok");
}
private async sendCommands(
commands: string[],
finalMessage: string
finalMessage: string,
onNewMessage?: (messages: string[]) => void
): Promise<string[]> {
return new Promise((resolve) => {
const messages: string[] = [];
this.worker.onmessage = (event) => {
const messageData: string = event.data;
messages.push(messageData);
onNewMessage?.(messages);
if (messageData.startsWith(finalMessage)) {
resolve(messages);
}
@@ -85,6 +98,7 @@ export abstract class UciEngine {
multiPv = this.multiPv
): Promise<GameEval> {
this.throwErrorIfNotReady();
await this.setMultiPv(multiPv);
this.ready = false;
await this.sendCommands(["ucinewgame", "isready"], "readyok");
@@ -92,7 +106,7 @@ export abstract class UciEngine {
const moves: MoveEval[] = [];
for (const fen of fens) {
const result = await this.evaluatePosition(fen, depth, multiPv, false);
const result = await this.evaluatePosition(fen, depth);
moves.push(result);
}
@@ -109,44 +123,46 @@ export abstract class UciEngine {
};
}
public async evaluatePosition(
fen: string,
depth = 16,
multiPv = this.multiPv,
checkIsReady = true
): Promise<MoveEval> {
if (checkIsReady) {
this.throwErrorIfNotReady();
}
await this.setMultiPv(multiPv, checkIsReady);
private async evaluatePosition(fen: string, depth = 16): Promise<MoveEval> {
console.log(`Evaluating position: ${fen}`);
const results = await this.sendCommands(
[`position fen ${fen}`, `go depth ${depth}`],
"bestmove"
);
const parsedResults = this.parseResults(results);
const whiteToPlay = fen.split(" ")[1] === "w";
return this.parseResults(results, whiteToPlay);
}
public async evaluatePositionWithUpdate({
fen,
depth = 16,
multiPv = this.multiPv,
setPartialEval,
}: EvaluatePositionWithUpdateParams): Promise<void> {
this.throwErrorIfNotReady();
await this.stopSearch();
await this.setMultiPv(multiPv);
const whiteToPlay = fen.split(" ")[1] === "w";
if (!whiteToPlay) {
const lines = parsedResults.lines.map((line) => ({
...line,
cp: line.cp ? -line.cp : line.cp,
}));
const onNewMessage = (messages: string[]) => {
const parsedResults = this.parseResults(messages, whiteToPlay);
setPartialEval(parsedResults);
};
return {
...parsedResults,
lines,
};
}
return parsedResults;
console.log(`Evaluating position: ${fen}`);
await this.sendCommands(
[`position fen ${fen}`, `go depth ${depth}`],
"bestmove",
onNewMessage
);
}
private parseResults(results: string[]): MoveEval {
private parseResults(results: string[], whiteToPlay: boolean): MoveEval {
const parsedResults: MoveEval = {
bestMove: "",
lines: [],
@@ -189,6 +205,13 @@ export abstract class UciEngine {
parsedResults.lines = Object.values(tempResults).sort(this.sortLines);
if (!whiteToPlay) {
parsedResults.lines = parsedResults.lines.map((line) => ({
...line,
cp: line.cp ? -line.cp : line.cp,
}));
}
return parsedResults;
}