UNPKG

@chessle/chess.js-extended

Version:

A powerful browser-only chess engine package that bundles Stockfish for position evaluation and analysis on top of chess.js

376 lines (274 loc) 10.6 kB
# chess.js-extended A powerful browser-only chess engine package that extends chess.js with built-in Stockfish evaluation capabilities. This package bundles Stockfish for position evaluation and analysis directly in the browser using Web Workers and WebAssembly. ## Features - 🚀 **Built on chess.js**: All standard chess.js functionality included - 🧠 **Stockfish Integration**: Bundled Stockfish engine for position evaluation - 🌐 **Browser-Only**: Designed specifically for browser environments with Web Worker support - ⚡ **WebAssembly Support**: Automatically uses WASM when available for better performance - 🎯 **Position Evaluation**: Get numerical evaluations and mate scores - 📈 **Multi-PV Analysis**: Get multiple principal variations for deeper insight - 🔍 **Suggested Lines**: Get full suggested lines of play in SAN format - ⚙️ **Flexible Options**: Configure search depth, time limits, and more - 📦 **Multiple Formats**: CommonJS and ES Module builds included ## Installation ```bash pnpm install @chessle/chess.js-extended ``` ## Quick Start ```typescript import { ChessEngine } from "@chessle/chess.js-extended"; const engine = new ChessEngine(); // Make some moves engine.move("e4"); engine.move("e5"); engine.move("Nf3"); // Evaluate the position const lines = await engine.evaluatePosition(); if (lines.length > 0) { const bestLine = lines[0]; console.log(`Evaluation: ${bestLine.evaluation}`); console.log(`Best move: ${bestLine.line[0]}`); console.log(`Suggested line: ${bestLine.line.join(" ")}`); } ``` ## Dual-Mode API: Promise and Streaming `ChessEngine` offers two distinct modes for analysis to suit different use cases: a simple **Promise-based** mode for one-shot evaluations and a powerful **Streaming API** for continuous analysis. **Important:** A single `ChessEngine` instance can only perform one analysis at a time. Attempting to start a new evaluation or analysis while one is already in progress will result in an error. ### 1. Promise-Based Analysis (One-Shot) The `evaluatePosition()` method is perfect for when you need a single, final evaluation of the current board state. It returns a promise that resolves once Stockfish has completed its analysis. ```typescript import { ChessEngine } from "@chessle/chess.js-extended"; const engine = new ChessEngine(); engine.load("rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2"); async function getEvaluation() { console.log("Evaluating position..."); const lines = await engine.evaluatePosition({ depth: 18 }); console.log("Evaluation complete."); if (lines.length > 0) { const bestLine = lines[0]; console.log(`Evaluation: ${bestLine.evaluation}`); console.log(`Best move: ${bestLine.line[0]}`); } } getEvaluation(); ``` ### 2. Streaming Analysis (Continuous) The streaming API is ideal for applications that need to display real-time engine analysis, such as a live chessboard UI. - `startAnalysis(options)`: Begins an infinite analysis of the current position. - `on('analysis', callback)`: Listens for analysis updates. The callback receives an array of the latest `AnalysisLine` objects as the engine deepens its search. - `stopAnalysis()`: Sends the `stop` command to Stockfish, which will then emit a final `bestmove` and terminate the analysis. ```typescript import { ChessEngine } from "@chessle/chess.js-extended"; const engine = new ChessEngine(); engine.load("r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3"); // 1. Listen for analysis updates engine.on("analysis", (lines) => { console.clear(); console.log("Current Analysis:"); lines.forEach((line) => { console.log(`- ${line.line.join(" ")} (Eval: ${line.evaluation})`); }); }); // 2. Start the analysis console.log("Starting continuous analysis..."); engine.startAnalysis({ multiPV: 3 }); // 3. Stop the analysis after a few seconds setTimeout(() => { console.log("Stopping analysis..."); engine.stopAnalysis(); console.log("Analysis stopped."); }, 5000); ``` ## API Reference ### ChessEngine Class The `ChessEngine` class extends the standard chess.js `Chess` class with evaluation capabilities. #### Constructor ```typescript const engine = new ChessEngine(); ``` #### Properties - `lines: AnalysisLine[]` - An array of the most recent final analysis lines from the last completed `evaluatePosition` call. #### Methods ##### evaluatePosition(options?) Evaluates the current position using Stockfish and returns a single result. ```typescript await engine.evaluatePosition(options?: StockfishOptions): Promise<AnalysisLine[]>; ``` ##### startAnalysis(options?) Starts a continuous, infinite analysis of the current position. Use the `on('analysis', ...)` method to receive updates. ```typescript engine.startAnalysis(options?: StockfishOptions): void; ``` ##### stopAnalysis() Stops a running analysis that was started with `startAnalysis()`. ```typescript engine.stopAnalysis(): void; ``` ##### on(event, listener) Subscribes to engine events. Currently, only the `analysis` event is supported. ```typescript engine.on('analysis', (lines: AnalysisLine[]) => void): void; ``` ##### off(event, listener) Unsubscribes from engine events. ```typescript engine.off('analysis', (lines: AnalysisLine[]) => void): void; ``` ### StockfishOptions Interface ```typescript interface StockfishOptions { /** Search depth (default: 15) */ depth?: number; /** Thinking time in milliseconds */ time?: number; /** Exact time to think in milliseconds */ movetime?: number; /** Number of nodes to search */ nodes?: number; /** Number of principal variations to search for (default: 1) */ multiPV?: number; /** Skill level of the engine (0-20) */ skillLevel?: number; /** Contempt value for the engine (-100 to 100) */ contempt?: number; /** Number of threads to use (1-512) */ threads?: number; /** Hash table size in MB (1-32768) */ hash?: number; /** Custom Stockfish worker URL (optional) */ stockfishUrl?: string; } ``` ### AnalysisLine Interface ```typescript interface AnalysisLine { /** The evaluation of the position (e.g., 0.5, -1.2, "M5", "M-3") */ evaluation: number | string; /** The suggested sequence of moves in SAN format */ line: string[]; } ``` ## Usage Examples ### Basic Position Evaluation ```typescript import { ChessEngine } from "chess.js-extended"; const engine = new ChessEngine(); engine.load("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"); const lines = await engine.evaluatePosition(); if (lines.length > 0) { console.log(`Position evaluation: ${lines[0].evaluation}`); // Output: Position evaluation: 0.15 } ``` ### Multi-PV Analysis Get the top 3 best moves and their evaluations. ```typescript const lines = await engine.evaluatePosition({ multiPV: 3 }); lines.forEach((line, index) => { console.log( `Rank ${index + 1}: ${line.line.join(" ")} (Eval: ${line.evaluation})`, ); }); // Output: // Rank 1: Nc6 ... (Eval: 0.15) // Rank 2: c5 ... (Eval: 0.20) // Rank 3: e6 ... (Eval: 0.22) ``` ### Time-Limited Analysis ```typescript // Analyze for exactly 5 seconds const lines = await engine.evaluatePosition({ movetime: 5000 }); if (lines.length > 0) { console.log(`Timed evaluation: ${lines[0].evaluation}`); } ``` ### Configuring Engine Parameters You can fine-tune the engine's performance and playing style by providing additional options. ```typescript const lines = await engine.evaluatePosition({ skillLevel: 10, // Set skill level to 10 (0-20) contempt: 20, // Set contempt to 20 (-100-100) threads: 4, // Use 4 threads hash: 128, // Use 128MB of hash memory }); if (lines.length > 0) { console.log(`Evaluation with custom parameters: ${lines[0].evaluation}`); } ``` ### Getting the Best Move and Line ```typescript const lines = await engine.evaluatePosition(); if (lines.length > 0) { const bestLine = lines[0]; console.log(`Best move: ${bestLine.line[0]}`); engine.move(bestLine.line[0]); console.log(`Principal variation: ${bestLine.line.join(" ")}`); // Output: Principal variation: Nf3 Nc6 Bc4 Bc5 } ``` ### Handling Mate Scores ```typescript // Load a position with checkmate engine.load("rnb1kbnr/pppp1ppp/4p3/8/6Pq/5P2/PPPPP2P/RNBQKBNR w KQkq - 1 3"); const lines = await engine.evaluatePosition(); if (lines.length > 0) { console.log(`Evaluation: ${lines[0].evaluation}`); // Output: Evaluation: M-1 (mate in 1 for black) } ``` ### Using All chess.js Methods Since `ChessEngine` extends `Chess`, you have access to all standard chess.js functionality: ```typescript const engine = new ChessEngine(); // Standard chess.js methods work as expected console.log(engine.ascii()); console.log(engine.moves()); console.log(engine.inCheck()); console.log(engine.isGameOver()); // Make moves engine.move("e4"); engine.move({ from: "e7", to: "e5" }); // Undo moves engine.undo(); // Get FEN console.log(engine.fen()); // And then evaluate const lines = await engine.evaluatePosition(); ``` ## Browser Compatibility This package requires: - Web Worker support - WebAssembly support (recommended, falls back to JavaScript if unavailable) - Modern browser with ES6+ support ## Performance Notes - **WebAssembly**: Automatically detected and used when available - **Web Workers**: Stockfish runs in a separate thread to avoid blocking the main thread - **Memory Management**: Worker instances are properly cleaned up after analysis - **Timeout Protection**: Analysis automatically times out after 30 seconds to prevent hanging ## Error Handling ```typescript try { const lines = await engine.evaluatePosition(); if (lines.length > 0) { console.log(lines[0].evaluation); } } catch (error) { if (error.message.includes("browser environment")) { console.log("This package only works in browsers"); } else if (error.message.includes("worker failed")) { console.log("Stockfish failed to load"); } } ``` ## Common Evaluation Values - **Positive numbers**: Advantage for White (e.g., `+1.5` = White is ahead by 1.5 pawns) - **Negative numbers**: Advantage for Black (e.g., `-0.8` = Black is ahead by 0.8 pawns) - **Mate scores**: `M3` = mate in 3, `M-2` = mate in 2 for opponent - **Zero**: Equal position ## License GPL-3.0 - See [LICENSE](LICENSE) file for details. ## Contributing This package is part of the Chessle project. Contributions welcome! ## Related Packages - [chess.js](https://github.com/jhlywa/chess.js) - The core chess library this extends - [Stockfish](https://stockfishchess.org/) - The powerful chess engine bundled within