svelte4-chess
Version:
Fully playable chess component for Svelte 4. Powered by Chess.js logic, Chessground chessboard and optionally Stockfish chess AI.
101 lines (100 loc) • 3.51 kB
JavaScript
;
var State;
(function (State) {
State["Uninitialised"] = "uninitialised";
State["Initialising"] = "initialising";
State["Waiting"] = "waiting";
State["Searching"] = "searching";
})(State || (State = {}));
;
export class Engine {
stockfish;
state = State.Uninitialised;
moveTime;
depth;
color;
stockfishPath;
externalUciCallback = undefined;
// Callbacks used when waiting for specific UCI messages
onUciOk = undefined; // "uciok" marks end of initialisation
onBestMove = undefined; // "uciok", used during initialisation
// Constructor
constructor(options = {}) {
this.moveTime = options.moveTime || 2000;
this.depth = options.depth || 40;
this.color = options.color || 'b';
this.stockfishPath = options.stockfishPath || 'stockfish.js';
}
// Initialise Stockfish. Resolve promise after receiving uciok.
init() {
return new Promise((resolve) => {
this.state = State.Initialising;
// NOTE: stockfish.js is not part of the npm package due to its size (1-2 MB).
// You can find the file here: https://github.com/gtim/svelte-chess/tree/main/static
this.stockfish = new Worker(this.stockfishPath);
this.stockfish.addEventListener('message', (e) => this._onUci(e));
this.onUciOk = () => {
if (this.state === State.Initialising) {
this.state = State.Waiting;
this.onUciOk = undefined;
resolve();
}
};
this.stockfish.postMessage('uci');
});
}
// Callback when receiving UCI messages from Stockfish.
_onUci({ data }) {
const uci = data;
if (this.onUciOk && uci === 'uciok') {
this.onUciOk();
}
if (this.onBestMove && uci.slice(0, 8) === 'bestmove') {
this.onBestMove(uci);
}
if (this.externalUciCallback) {
this.externalUciCallback(uci);
}
}
setUciCallback(callback) {
this.externalUciCallback = callback;
}
getMove(fen) {
return new Promise((resolve) => {
if (!this.stockfish)
throw new Error('Engine not initialised');
if (this.state !== State.Waiting)
throw new Error('Engine not ready (state: ' + this.state + ')');
this.state = State.Searching;
this.stockfish.postMessage('position fen ' + fen);
this.stockfish.postMessage(`go depth ${this.depth} movetime ${this.moveTime}`);
this.onBestMove = (uci) => {
const uciArray = uci.split(' ');
const bestMoveLan = uciArray[1];
this.state = State.Waiting;
this.onBestMove = undefined;
resolve(bestMoveLan);
};
});
}
getColor() {
return this.color;
}
isSearching() {
return this.state === State.Searching;
}
async stopSearch() {
return new Promise((resolve) => {
if (!this.stockfish)
throw new Error('Engine not initialised');
if (this.state !== State.Searching)
resolve();
this.onBestMove = (uci) => {
this.state = State.Waiting;
this.onBestMove = undefined;
resolve();
};
this.stockfish.postMessage('stop');
});
}
}