var globalDepth = 15; var _hackEngine = null; var _evalEngine = null; var _movePoller = null; const STOCKFISH_PATH = "/bundles/app/js/vendor/jschessengine/stockfish.asm.1abfa10c.js"; const CHAR_MAP = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8 }; function startEngines(playerColour) { let baseFen = getBoardFen(); let evalSideToMove = "w"; let activeSide = "w"; // --- Hack engine --- _hackEngine = new Worker(STOCKFISH_PATH); _hackEngine.postMessage(`position fen ${baseFen} ${playerColour}`); _hackEngine.postMessage("go wtime 300000 btime 300000 winc 2000 binc 2000"); _hackEngine.postMessage(`go depth ${globalDepth}`); _hackEngine.onmessage = function(event) { if (!event.data.startsWith("bestmove")) return; const bestMove = event.data.split(" ")[1]; const display = document.getElementById("best-move"); if (display) display.textContent = `Best move: ${bestMove} (depth ${globalDepth})`; document.querySelectorAll(".cheat-highlight").forEach(el => el.remove()); const m = bestMove.split(""); const from = `${CHAR_MAP[m[0]]}${m[1]}`; const to = `${CHAR_MAP[m[2]]}${m[3]}`; const board = document.querySelector("wc-chess-board"); if (!board) return; [from, to].forEach(sq => { const hl = document.createElement("div"); hl.className = `highlight cheat-highlight square-${sq}`; hl.style = "background:red;opacity:0.5"; board.appendChild(hl); }); }; // --- Eval engine --- _evalEngine = new Worker(STOCKFISH_PATH); let evalGeneration = 0; function startEvalSearch() { activeSide = evalSideToMove; const gen = ++evalGeneration; const side = activeSide; _evalEngine.postMessage("stop"); _evalEngine.postMessage(`position fen ${baseFen} ${evalSideToMove}`); _evalEngine.postMessage("go depth 10"); let pending = null; _evalEngine.onmessage = function(event) { if (gen !== evalGeneration) return; // discard stale results from old search if (event.data.startsWith("info")) { const scoreCP = event.data.match(/score cp (-?\d+)/); const scoreMate = event.data.match(/score mate (-?\d+)/); if (scoreMate) pending = { val: parseInt(scoreMate[1]), isMate: true }; else if (scoreCP) pending = { val: parseInt(scoreCP[1]), isMate: false }; } else if (event.data.startsWith("bestmove") && pending) { // Only update the bar once the search is fully done — avoids wild swings // from shallow-depth lines (e.g. seeing a capture but not the recapture) updateEvalBar(pending.val, pending.isMate, side); pending = null; } }; } startEvalSearch(); // --- Move poller --- _movePoller = setInterval(() => { const newFen = getBoardFen(); if (newFen === baseFen) return; baseFen = newFen; evalSideToMove = evalSideToMove === "w" ? "b" : "w"; startEvalSearch(); _hackEngine.postMessage(`position fen ${baseFen} ${playerColour}`); _hackEngine.postMessage("go wtime 300000 btime 300000 winc 2000 binc 2000"); _hackEngine.postMessage(`go depth ${globalDepth}`); }, 500); } function stopEngines() { if (_movePoller) { clearInterval(_movePoller); _movePoller = null; } if (_evalEngine) { _evalEngine.terminate(); _evalEngine = null; } if (_hackEngine) { _hackEngine.terminate(); _hackEngine = null; } document.querySelectorAll(".cheat-highlight").forEach(el => el.remove()); }