chessExt/engine.js
2026-04-09 15:09:44 -04:00

95 lines
3.4 KiB
JavaScript

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());
}