// engine.js import { spawn } from 'child_process'; import { EventEmitter } from 'events'; class ChessEngine extends EventEmitter { constructor(enginePath) { super(); this.enginePath = enginePath; this.engine = null; this.isReady = false; this.currentFen = null; } start() { return new Promise((resolve, reject) => { try { this.engine = spawn(this.enginePath); this.engine.stdout.on('data', (data) => { const messages = data.toString().trim().split('\n'); messages.forEach(msg => this.handleEngineMessage(msg)); }); this.engine.stderr.on('data', (data) => { console.error('Engine Error:', data.toString()); }); this.engine.on('close', (code) => { console.log('Engine process closed with code:', code); this.isReady = false; }); // Initialize engine this.sendCommand('uci'); this.waitforReady(resolve); } catch (error) { reject(error); } }); } waitforReady(resolve){ this.sendCommand('isready'); // Wait for readyok this.once('ready', () => { this.isReady = true; resolve(); }); } handleEngineMessage(message) { console.log('Engine:', message); if (message.includes('readyok')) { this.emit('ready'); } else if (message.startsWith('bestmove')) { const [, move] = message.split(' '); this.emit('bestmove', move); } else if (message.startsWith('info')) { this.emit('info', this.parseInfo(message)); } } parseInfo(info) { const parts = info.split(' '); const result = {}; for (let i = 1; i < parts.length; i++) { switch (parts[i]) { case 'depth': result.depth = parseInt(parts[++i]); break; case 'seldepth': result.seldepth = parseInt(parts[++i]); break; case 'score': result.score = { type: parts[++i], value: parseInt(parts[++i]) }; break; case 'nodes': result.nodes = parseInt(parts[++i]); break; case 'time': result.time = parseInt(parts[++i]); break; case 'pv': result.pv = []; while (++i < parts.length && !parts[i].includes('bmc')) { result.pv.push(parts[i]); } break; } } return result; } sendCommand(cmd) { if (this.engine && this.engine.stdin.writable) { console.log('Sending:', cmd); this.engine.stdin.write(cmd + '\n'); } } async setBoardPosition(fen) { if (!this.isReady) throw new Error('Engine not ready'); this.currentFen = fen; this.sendCommand(`position fen ${fen}`); // Ensure engine is ready after position set this.sendCommand('isready'); await new Promise(resolve => this.once('ready', resolve)); } async getBestMove(options = {}) { if (!this.isReady) throw new Error('Engine not ready'); if (!this.currentFen) throw new Error('Position not set'); const { depth = 15, movetime = 1000, nodes = null, } = options; return new Promise((resolve) => { // Construct go command let goCmd = 'go'; if (depth) goCmd += ` depth ${depth}`; if (movetime) goCmd += ` movetime ${movetime}`; if (nodes) goCmd += ` nodes ${nodes}`; this.sendCommand(goCmd); // Wait for bestmove response this.once('bestmove', (move) => { resolve(move); }); }); } async getAnalysis(fen, options = {}) { await this.setBoardPosition(fen); const bestMove = await this.getBestMove(options); return { bestMove }; } quit() { if (this.engine) { this.sendCommand('quit'); this.engine = null; this.isReady = false; } } } export default ChessEngine;