commit cdb7709affe1991534953e357d57be54eeb140c3 Author: Zakaria Date: Thu Apr 9 15:09:44 2026 -0400 first c diff --git a/engine.js b/engine.js new file mode 100644 index 0000000..60d6aed --- /dev/null +++ b/engine.js @@ -0,0 +1,94 @@ +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()); +} diff --git a/evalbar.js b/evalbar.js new file mode 100644 index 0000000..758e608 --- /dev/null +++ b/evalbar.js @@ -0,0 +1,42 @@ +var evalLabel = document.createElement("span"); +evalLabel.style = `font-size:12px;font-weight:bold;color:#94a3b8;min-width:38px;text-align:right;font-family:monospace;`; +evalLabel.textContent = "0.0"; + +var evalTrack = document.createElement("div"); +evalTrack.style = `flex:1;height:6px;border-radius:4px;background:#0f172a;overflow:hidden;`; + +var evalFill = document.createElement("div"); +evalFill.style = `height:100%;width:50%;background:#e2e8f0;transition:width 0.4s ease;border-radius:4px;`; +evalTrack.appendChild(evalFill); + +var evalPercent = document.createElement("span"); +evalPercent.style = `font-size:11px;color:#64748b;min-width:34px;font-family:monospace;`; +evalPercent.textContent = "50%"; + +function cpToWinPercent(cp) { + return 50 + 50 * (2 / (1 + Math.exp(-0.00368208 * cp)) - 1); +} + +function updateEvalBar(cp, isMate, sideToMove) { + let whiteCP = sideToMove === "w" ? cp : -cp; + let whitePercent, labelText; + + if (isMate) { + if (cp === 0) { + // Position IS checkmate — the side to move has already been mated + whitePercent = sideToMove === "b" ? 98 : 2; + labelText = sideToMove === "b" ? "M0" : "-M0"; + } else { + whitePercent = whiteCP > 0 ? 98 : 2; + labelText = whiteCP > 0 ? `M${Math.abs(cp)}` : `-M${Math.abs(cp)}`; + } + } else { + whitePercent = cpToWinPercent(whiteCP); + let v = (whiteCP / 100).toFixed(1); + labelText = whiteCP >= 0 ? `+${v}` : `${v}`; + } + + evalFill.style.width = `${whitePercent}%`; + evalLabel.textContent = labelText; + evalPercent.textContent = `${Math.round(whitePercent)}%`; +} diff --git a/fen.js b/fen.js new file mode 100644 index 0000000..e3c81fc --- /dev/null +++ b/fen.js @@ -0,0 +1,33 @@ +function getBoardFen() { + let fen = ""; + for (let i = 8; i >= 1; i--) { + for (let j = 1; j <= 8; j++) { + if (j === 1 && i !== 8) fen += "/"; + + let classes = document.querySelectorAll(`.piece.square-${j}${i}`)[0]?.classList ?? null; + let piece = null; + if (classes) { + for (let cls of classes.values()) { + if (cls.length === 2) piece = cls; + } + } + + if (piece === null) { + let last = fen.slice(-1); + fen = (!isNaN(Number(last)) && last !== "") + ? fen.slice(0, -1) + (Number(last) + 1) + : fen + "1"; + } else if (piece[0] === "b") { + fen += piece[1]; + } else { + fen += piece[1].toUpperCase(); + } + } + } + return fen; +} + +function getPlayerColour() { + const board = document.querySelector("wc-chess-board"); + return board?.classList.contains("flipped") ? "b" : "w"; +} diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..68a3863 Binary files /dev/null and b/icon.png differ diff --git a/main.js b/main.js new file mode 100644 index 0000000..b31818e --- /dev/null +++ b/main.js @@ -0,0 +1,2 @@ +const mainBody = document.querySelector(".board-layout-main"); +if (mainBody) mainBody.prepend(panel); diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..e58558e --- /dev/null +++ b/manifest.json @@ -0,0 +1,24 @@ +{ + "manifest_version": 2, + "name": "Chesscheat", + "version": "1.3", + "icons": { + "16": "icon.png", + "48": "icon.png", + "128": "icon.png" + }, + "browser_specific_settings": { + "gecko": { + "id": "chesscheatextension@gmail.com", + "strict_min_version": "57.0" + } + }, + "description": "Always see the optimal move in any chess.com game. Guarantees wins almost 100% of the time", + "content_scripts": [ + { + "js": ["fen.js", "evalbar.js", "engine.js", "panel.js", "main.js"], + "matches": [ + ] + } + ] +} diff --git a/panel.js b/panel.js new file mode 100644 index 0000000..2ffc339 --- /dev/null +++ b/panel.js @@ -0,0 +1,117 @@ +var hackRunning = false; + +// --- Panel container --- +var panel = document.createElement("div"); +panel.style = ` + background: #1e293b; + border: 1px solid #334155; + border-radius: 8px; + padding: 6px 10px; + margin: 4px 0; + font-family: 'Segoe UI', sans-serif; + display: flex; + flex-direction: column; + gap: 5px; + box-shadow: 0 2px 8px rgba(0,0,0,0.4); +`; + +// Row 1: title + depth slider + value + button +var controlRow = document.createElement("div"); +controlRow.style = `display:flex;align-items:center;gap:8px;`; + +var panelTitle = document.createElement("span"); +panelTitle.textContent = "Chess Assist"; +panelTitle.style = `font-size:11px;font-weight:600;color:#64748b;white-space:nowrap;`; + +var depthSlider = document.createElement("input"); +depthSlider.type = "range"; +depthSlider.min = "5"; depthSlider.max = "30"; depthSlider.value = globalDepth; +depthSlider.style = `flex:1;accent-color:#3b82f6;cursor:pointer;height:4px;`; + +var depthValue = document.createElement("span"); +depthValue.textContent = globalDepth; +depthValue.style = `font-size:11px;color:#64748b;min-width:16px;font-family:monospace;`; + +depthSlider.addEventListener("input", function() { + globalDepth = parseInt(this.value); + depthValue.textContent = this.value; +}); + +var actionBtn = document.createElement("button"); +actionBtn.id = "hack_button"; +actionBtn.textContent = "Start"; +actionBtn.style = ` + padding: 4px 12px; + border: none; + border-radius: 5px; + font-size: 11px; + font-weight: 600; + cursor: pointer; + background: #22c55e; + color: #fff; + white-space: nowrap; + transition: opacity 0.2s; +`; +actionBtn.onmouseenter = () => { if (!actionBtn.disabled) actionBtn.style.opacity = "0.8"; }; +actionBtn.onmouseleave = () => { actionBtn.style.opacity = "1"; }; + +controlRow.appendChild(panelTitle); +controlRow.appendChild(depthSlider); +controlRow.appendChild(depthValue); +controlRow.appendChild(actionBtn); +panel.appendChild(controlRow); + +// Row 2: best move + eval bar (hidden until running) +var infoRow = document.createElement("div"); +infoRow.style = `display:none;align-items:center;gap:8px;`; + +var bestMoveDisplay = document.createElement("span"); +bestMoveDisplay.id = "best-move"; +bestMoveDisplay.style = `font-size:11px;color:#94a3b8;font-family:monospace;white-space:nowrap;`; +bestMoveDisplay.textContent = "Calculating..."; + +infoRow.appendChild(bestMoveDisplay); +infoRow.appendChild(evalLabel); +infoRow.appendChild(evalTrack); +infoRow.appendChild(evalPercent); +panel.appendChild(infoRow); + +// --- State machine --- +function setIdleState() { + hackRunning = false; + actionBtn.textContent = "Start"; + actionBtn.style.background = "#22c55e"; + actionBtn.disabled = false; + infoRow.style.display = "none"; + depthSlider.disabled = false; + depthSlider.style.opacity = "1"; +} + +function setRunningState() { + actionBtn.textContent = "Stop"; + actionBtn.style.background = "#ef4444"; + actionBtn.disabled = false; + infoRow.style.display = "flex"; + depthSlider.disabled = true; + depthSlider.style.opacity = "0.4"; +} + +function startHack() { + if (hackRunning) return; + hackRunning = true; + actionBtn.textContent = "..."; + actionBtn.style.background = "#475569"; + actionBtn.disabled = true; + startEngines(getPlayerColour()); + setRunningState(); +} + +function stopHack() { + stopEngines(); + setIdleState(); +} + +actionBtn.onclick = () => { + if (!hackRunning) startHack(); + else stopHack(); +};