// ==UserScript== // @name BEST Cheat for Chess.com (Stockfish 17.1, 16.1 & 10.0.2, No Anti-Ban) // @namespace http://tampermonkey.net/ // @version 7.9.3 // @description Triple model automated cheat engine (SF 17.1, SF 16.1, SF 10.0.2) with fully customizable UI and configuration settings. // @author Ech0 // @copyright 2025, Ech0 // @license MIT // @match https://www.chess.com/play/* // @match https://www.chess.com/game/* // @match https://www.chess.com/puzzles/* // @match https://www.chess.com/daily // @connect chess-api.com // @connect stockfish.online // @grant GM_getResourceText // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @resource stockfish.js https://cdnjs.cloudflare.com/ajax/libs/stockfish.js/10.0.2/stockfish.js // @run-at document-idle // @downloadURL none // ==/UserScript== (function () { "use strict"; const CONFIG = { BOARD_SEL: "chess-board, wc-chess-board", LOOP_MS: 50, API: { MAX_DEPTH: 18, MAX_TIME: 2000 } }; const PIECE_IMGS = { p: "https://upload.wikimedia.org/wikipedia/commons/c/c7/Chess_pdt45.svg", r: "https://upload.wikimedia.org/wikipedia/commons/f/ff/Chess_rdt45.svg", n: "https://upload.wikimedia.org/wikipedia/commons/e/ef/Chess_ndt45.svg", b: "https://upload.wikimedia.org/wikipedia/commons/9/98/Chess_bdt45.svg", q: "https://upload.wikimedia.org/wikipedia/commons/4/47/Chess_qdt45.svg", k: "https://upload.wikimedia.org/wikipedia/commons/f/f0/Chess_kdt45.svg", P: "https://upload.wikimedia.org/wikipedia/commons/4/45/Chess_plt45.svg", R: "https://upload.wikimedia.org/wikipedia/commons/7/72/Chess_rlt45.svg", N: "https://upload.wikimedia.org/wikipedia/commons/7/70/Chess_nlt45.svg", B: "https://upload.wikimedia.org/wikipedia/commons/b/b1/Chess_blt45.svg", Q: "https://upload.wikimedia.org/wikipedia/commons/1/15/Chess_qlt45.svg", K: "https://upload.wikimedia.org/wikipedia/commons/4/42/Chess_klt45.svg", }; const state = { board: null, isThinking: !1, ui: {}, lastRawFEN: "N/A", lastSentFEN: "", lastSanitizedBoardFEN: "", lastMoveResult: "Waiting for analysis...", lastLiveResult: "Depth | Evaluation: Best move will appear here.", lastPayload: "N/A", lastResponse: "N/A", moveTargetTime: 0, calculatedDelay: 0, localEngine: null, localConfigSent: !1, currentCloudRequest: null, currentBestMove: null, currentPV: [], analysisStartTime: 0, h: 180, s: 100, l: 50, newGameObserver: null, queueTimeout: null, localEval: null, localMate: null, localPV: null, localDepth: null, history: [], hasSavedCurrentGameResult: !1, playingAs: null, }; const DEFAULT_SETTINGS = { engineMode: "cloud", depth: 12, maxThinkingTime: 2000, contempt: 100, searchMoves: "", autoRun: !0, autoMove: !1, autoQueue: !1, showPVArrows: !1, pvDepth: 5, pvShowNumbers: !1, pvCustomGradient: !1, pvStartColor: "#FFFF00", pvEndColor: "#FF0000", minDelay: 0, maxDelay: 0, highlightColor: "#00eeff", visualType: "boxes", arrowOpacity: 0.8, arrowWidth: 15, innerOpacity: 0.6, outerOpacity: 0.2, gradientBias: 0, debugLogs: !1, enableHistory: !0, }; const settings = { ...DEFAULT_SETTINGS }; const hexToRgb = (hex) => { const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return r ? { r: parseInt(r[1], 16), g: parseInt(r[2], 16), b: parseInt(r[3], 16) } : { r: 0, g: 0, b: 0 }; }; const rgbToHex = (r, g, b) => "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); const rgbToHsl = (r, g, b) => { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b), min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) h = s = 0; else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return { h: h * 360, s: s * 100, l: l * 100 }; }; const hslToRgb = (h, s, l) => { let r, g, b; h /= 360; s /= 100; l /= 100; if (s === 0) r = g = b = l; else { const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) }; }; function interpolateColor(color1, color2, factor) { const r1 = parseInt(color1.slice(1, 3), 16); const g1 = parseInt(color1.slice(3, 5), 16); const b1 = parseInt(color1.slice(5, 7), 16); const r2 = parseInt(color2.slice(1, 3), 16); const g2 = parseInt(color2.slice(3, 5), 16); const b2 = parseInt(color2.slice(5, 7), 16); const r = Math.round(r1 + factor * (r2 - r1)); const g = Math.round(g1 + factor * (g2 - g1)); const b = Math.round(b1 + factor * (b2 - b1)); return `rgb(${r}, ${g}, ${b})`; } function getRawBoardFEN() { if (!state.board?.game) return null; try { if (typeof state.board.game.getFEN === "function") return state.board.game.getFEN(); if (typeof state.board.game.fen === "string") return state.board.game.fen; if (state.board.game.getPosition) return state.board.game.getPosition(); } catch (e) {} return null; } function sanitizeFEN(rawFEN) { if (!rawFEN) return ""; let parts = rawFEN.replace(/\s+/g, " ").trim().split(" "); if (parts.length < 6) { const def = ["w", "-", "-", "0", "1"]; for (let i = parts.length; i < 6; i++) parts.push(def[i - 1]); } if (parts[3] && parts[3] !== "-") parts[3] = parts[3].toLowerCase(); return parts.join(" "); } function loadLocalEngine() { if (state.localEngine) return; try { const scriptContent = GM_getResourceText("stockfish.js"); if (!scriptContent) throw new Error("Stockfish resource not found."); const blob = new Blob([scriptContent], { type: "application/javascript" }); state.localEngine = new Worker(URL.createObjectURL(blob)); state.localEngine.onmessage = handleLocalMessage; state.localEngine.onerror = (e) => handleError("Local Engine Error", e); [ "ucinewgame", "isready", "setoption name MultiPV value 1", `setoption name Contempt value ${settings.contempt}`, ].forEach((c) => state.localEngine.postMessage(c)); console.log("Stockfish 10 Local Loaded."); } catch (e) { handleError("Engine Load Fail", e); } } function analyze(depth = settings.depth, fenOverride = null, isRetry = !1) { if (state.isThinking && !fenOverride && !isRetry) return; let finalFEN = fenOverride || sanitizeFEN(getRawBoardFEN()); if (!finalFEN) return; state.lastRawFEN = finalFEN; state.lastSentFEN = finalFEN; if (!fenOverride) state.lastSanitizedBoardFEN = finalFEN; state.isThinking = !0; state.analysisStartTime = performance.now(); const minMs = settings.minDelay * 1000; const maxMs = settings.maxDelay * 1000; const delay = Math.random() * (maxMs - minMs) + minMs; state.moveTargetTime = performance.now() + delay; state.calculatedDelay = (delay / 1000).toFixed(2); updateUI(); if (settings.engineMode === "cloud") { analyzeCloud(finalFEN, depth, isRetry); } else if (settings.engineMode === "sfonline") { analyzeSF16(finalFEN, depth); } else { analyzeLocal(finalFEN, depth); } } function analyzeCloud(finalFEN, depth, isRetry) { const actualDepth = Math.min(depth, 18); const payload = { fen: finalFEN, depth: actualDepth, maxThinkingTime: Math.min(settings.maxThinkingTime, CONFIG.API.MAX_TIME), taskId: Math.random().toString(36).substring(7), }; if (settings.searchMoves.trim()) payload.searchmoves = settings.searchMoves.trim(); state.lastPayload = `POST https://chess-api.com/v1\n${JSON.stringify(payload, null, 2)}`; if (state.ui.liveOutput) state.ui.liveOutput.innerHTML = isRetry ? "♻️ Retrying Safe FEN..." : "☁️ SF17 Analysis..."; updateUI(); state.currentCloudRequest = GM_xmlhttpRequest({ method: "POST", url: "https://chess-api.com/v1", headers: { "Content-Type": "application/json" }, data: JSON.stringify(payload), timeout: 15000, onload: (res) => handleCloudResponse(res, finalFEN, actualDepth, isRetry), onerror: (err) => handleError("Network Error", err), ontimeout: () => handleError("Timeout (15s)"), }); } function analyzeSF16(finalFEN, depth) { const actualDepth = Math.min(depth, 15); const encodedFEN = encodeURIComponent(finalFEN); const url = `https://stockfish.online/api/s/v2.php?fen=${encodedFEN}&depth=${actualDepth}&mode=bestmove`; state.lastPayload = `GET ${url}`; if (state.ui.liveOutput) state.ui.liveOutput.innerHTML = "☁️ SF16.1 Analysis..."; updateUI(); state.currentCloudRequest = GM_xmlhttpRequest({ method: "GET", url: url, timeout: 20000, onload: (res) => handleSF16Response(res), onerror: (err) => handleError("Network Error (SF16)", err), ontimeout: () => handleError("Timeout (SF16 20s)"), }); } function handleSF16Response(response) { state.isThinking = !1; state.lastResponse = response.responseText; try { if (response.status !== 200) throw new Error(`HTTP ${response.status}`); let data; try { data = JSON.parse(response.responseText); } catch (e) { throw new Error("Invalid JSON from SF16"); } if (!data.success || !data.bestmove) throw new Error(data.data || "Unknown SF16 Error"); const bestMove = data.bestmove.split(" ")[1] || data.bestmove; const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2); let evalScore = data.evaluation; let mate = data.mate; processBestMove( bestMove, evalScore, mate, data.continuation ? data.continuation.split(" ") : null, null, duration ); } catch (e) { handleError("SF16 API Error", e); } updateUI(); } function handleCloudResponse(response, sentFEN, depth, isRetry) { state.isThinking = !1; state.lastResponse = response.responseText; if (response.responseText.includes("HIGH_USAGE") || response.status === 429) { state.lastMoveResult = "⚠️ API COOLDOWN"; state.lastLiveResult = "HIGH USAGE: Cooldown ~30-60m."; updateUI(); return; } try { if (response.status !== 200) throw new Error(`HTTP ${response.status}`); let rawData; try { rawData = JSON.parse(response.responseText); } catch (e) { throw new Error("Invalid JSON"); } const result = Array.isArray(rawData) ? rawData[0] : rawData; if (!result || result.error || result.status === "error") { const errText = result?.error || result?.message || "Unknown Error"; if (errText.includes("HIGH_USAGE")) { state.lastMoveResult = "⚠️ API COOLDOWN"; state.lastLiveResult = "HIGH USAGE: Cooldown ~30-60m."; updateUI(); return; } if ((errText.includes("FEN") || errText.includes("VALIDATION")) && !isRetry) { const parts = sentFEN.split(" "); if (parts.length >= 4 && parts[3] !== "-") { parts[3] = "-"; analyze(depth, parts.join(" "), !0); return; } } throw new Error(errText); } if (result.move || result.bestmove) { const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2); processBestMove( result.move || result.bestmove, result.eval, result.mate, result.continuationArr, result.winChance, duration ); } else { state.lastMoveResult = "⚠️ No move returned."; } } catch (e) { handleError("API Error", e); } updateUI(); } function analyzeLocal(fen, depth) { if (!state.localEngine) loadLocalEngine(); if (!state.localEngine) return; if (!state.localConfigSent) { state.localEngine.postMessage(`setoption name Contempt value ${settings.contempt}`); state.localConfigSent = !0; } state.localEval = null; state.localMate = null; state.localPV = null; state.localDepth = null; const actualDepth = Math.min(depth, 23); const cmds = [`position fen ${fen}`, `go depth ${actualDepth}`]; state.lastPayload = `Worker CMDs:\n${cmds.join("\n")}`; state.ui.liveOutput.innerHTML = "⚡ Local Analysis..."; updateUI(); cmds.forEach((cmd) => state.localEngine.postMessage(cmd)); } function handleLocalMessage(e) { const msg = e.data; if (typeof msg !== "string") return; state.lastResponse = (state.lastResponse.length > 500 ? "..." + state.lastResponse.slice(-500) : state.lastResponse) + "\n" + msg; if (msg.startsWith("info") && msg.includes("depth") && msg.includes("score")) { const depthMatch = msg.match(/depth (\d+)/); const scoreMatch = msg.match(/score (cp|mate) (-?\d+)/); const pvMatch = msg.match(/ pv (.*)/); if (depthMatch && scoreMatch) { const depth = depthMatch[1]; let val = parseInt(scoreMatch[2]); const type = scoreMatch[1]; const fenParts = state.lastSentFEN ? state.lastSentFEN.split(" ") : []; const sideToMove = fenParts.length > 1 ? fenParts[1] : "w"; if (sideToMove === "b") val = -val; const pv = pvMatch ? pvMatch[1] : ""; if (type === "mate") { state.localMate = val; state.localEval = null; } else { state.localMate = null; state.localEval = (val / 100).toFixed(2); } state.localPV = pv; state.localDepth = depth; if (pv) state.currentPV = pv.split(" "); let scoreTxt; if (type === "mate") { scoreTxt = "M" + Math.abs(val); if (val < 0) scoreTxt = "-" + scoreTxt; } else { scoreTxt = (val > 0 ? "+" : "") + (val / 100).toFixed(2); } const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2); if (pv) { const best = pv.split(" ")[0]; highlightMove(best); state.lastMoveResult = `⏳ D${depth}: ${best}`; } state.lastLiveResult = `
${scoreTxt} (${duration}s) (Local Depth ${depth})
PV: ${pv.split(" ").slice(0, 5).join(" ")}...
`; updateUI(); } } if (msg.startsWith("bestmove")) { state.isThinking = !1; const parts = msg.split(" "); const bestMove = parts[1]; if (bestMove && bestMove !== "(none)") { const duration = ((performance.now() - state.analysisStartTime) / 1000).toFixed(2); processBestMove( bestMove, state.localEval, state.localMate, state.localPV ? state.localPV.split(" ") : null, null, duration, state.localDepth ); } else state.lastMoveResult = "⚠️ No move found"; updateUI(); } } function processBestMove(bestMove, evalScore, mate, continuationArr, winChance, duration, depth = null) { state.currentBestMove = bestMove; state.currentPV = continuationArr || (bestMove ? [bestMove] : []); highlightMove(bestMove); let scoreTxt = ""; let pvStr = "N/A"; if (evalScore !== undefined || mate !== undefined) { if (mate) { scoreTxt = `M${Math.abs(mate)}`; if (mate < 0) scoreTxt = "-" + scoreTxt; } else { const sc = parseFloat(evalScore); scoreTxt = (sc > 0 ? "+" : "") + sc; } if (continuationArr) pvStr = continuationArr.join(" "); } const durHtml = duration ? `(${duration}s)` : ""; state.lastMoveResult = `✅ Best: ${bestMove}`; let wcHtml = ""; if (winChance) wcHtml = `(${Math.round(winChance)}%)`; else if (depth) wcHtml = `(Local Depth ${depth})`; state.lastLiveResult = `
${scoreTxt} ${durHtml} ${wcHtml}
PV: ${pvStr}
`; if (settings.autoMove) triggerAutoMove(); } function triggerAutoMove() { if (!state.currentBestMove || !state.board?.game) return; const turn = state.board.game.getTurn(); const playingAs = state.board.game.getPlayingAs(); if (turn !== playingAs) return; const wait = Math.max(0, state.moveTargetTime - performance.now()); setTimeout(() => playMove(state.currentBestMove), wait); } function handleError(type, err) { state.isThinking = !1; console.error(type, err); state.lastResponse = `${type}: ${err.message || err}`; state.lastMoveResult = `❌ ${type}`; updateUI(); } function playMove(move) { if (!state.board?.game) return; const from = move.substring(0, 2); const to = move.substring(2, 4); const currentRaw = getRawBoardFEN(); if (currentRaw && sanitizeFEN(currentRaw).split(" ")[0] !== state.lastSentFEN.split(" ")[0]) return; for (const m of state.board.game.getLegalMoves()) { if (m.from === from && m.to === to) { const promotion = move.length > 4 ? move.substring(4, 5) : "q"; state.board.game.move({ ...m, promotion, animate: !0, userGenerated: !0 }); return; } } } function highlightMove(move) { document.querySelectorAll(".bot-highlight").forEach((el) => el.remove()); if (!move) return; if (settings.visualType === "arrow") { drawArrow(move); } else { const { highlightColor, innerOpacity, outerOpacity, gradientBias } = settings; const { r, g, b } = hexToRgb(highlightColor); const col = (a) => `rgba(${r}, ${g}, ${b}, ${a})`; const from = move.substring(0, 2); const to = move.substring(2, 4); [from, to].forEach((alg) => { const sqId = `${alg.charCodeAt(0) - 96}${alg.charAt(1)}`; const div = document.createElement("div"); div.className = `square-${sqId} bot-highlight`; const biasPct = gradientBias + "%"; div.style.cssText = `position: absolute; pointer-events: none; z-index: 200; width: 12.5%; height: 12.5%; box-sizing: border-box; background: radial-gradient(closest-side, ${col(innerOpacity)} ${biasPct}, ${col(outerOpacity)} 100%);`; state.board.appendChild(div); }); } if (settings.showPVArrows && state.currentPV && state.currentPV.length > 0) { // MODIFIED LOGIC: // If engineMode is "cloud" (SF17.1), start from index 0. // Otherwise, start from index 1 (skip first move). const isCloud = settings.engineMode === "cloud"; const start = isCloud ? 0 : 1; const end = isCloud ? settings.pvDepth : 1 + settings.pvDepth; const pvMoves = state.currentPV.slice(start, end); if (pvMoves.length > 0) { const total = pvMoves.length; pvMoves.forEach((pvMove, i) => { let color; if (settings.pvCustomGradient) { const factor = total > 1 ? i / (total - 1) : 0; color = interpolateColor(settings.pvStartColor, settings.pvEndColor, factor); } else { let g = 255; if (total > 1) { g = Math.round(255 * (1 - i / (total - 1))); } color = `rgb(255, ${g}, 0)`; } const text = settings.pvShowNumbers ? (i + 1).toString() : null; drawArrow(pvMove, color, settings.arrowOpacity, text); }); } } } function drawArrow(move, colorOverride = null, opacityOverride = null, text = null) { const color = colorOverride || settings.highlightColor; const opacity = opacityOverride !== null ? opacityOverride : settings.arrowOpacity; const width = settings.arrowWidth; let isFlipped = !1; if (state.board.classList.contains("flipped")) isFlipped = !0; else if (state.board.game && state.board.game.getPlayingAs && state.board.game.getPlayingAs() === "b") isFlipped = !0; const from = move.substring(0, 2); const to = move.substring(2, 4); const getCoords = (sq) => { const file = sq.charCodeAt(0) - 97; const rank = parseInt(sq[1]) - 1; let x, y; if (isFlipped) { x = (7 - file) * 12.5 + 6.25; y = rank * 12.5 + 6.25; } else { x = file * 12.5 + 6.25; y = (7 - rank) * 12.5 + 6.25; } return { x, y }; }; const start = getCoords(from); const end = getCoords(to); const dx = end.x - start.x; const dy = end.y - start.y; const len = Math.sqrt(dx * dx + dy * dy); const scale = width / 15; const headLen = 4 * scale; const headWidth = 3 * scale; const lineWidth = 1.2 * scale; if (len === 0) return; const ux = dx / len; const uy = dy / len; const endLineX = end.x - ux * headLen; const endLineY = end.y - uy * headLen; const px = -uy; const py = ux; const corner1X = endLineX + px * (headWidth / 2); const corner1Y = endLineY + py * (headWidth / 2); const corner2X = endLineX - px * (headWidth / 2); const corner2Y = endLineY - py * (headWidth / 2); const ns = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(ns, "svg"); svg.setAttribute("class", "bot-highlight"); svg.style.cssText = "position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:200;"; svg.setAttribute("viewBox", "0 0 100 100"); const line = document.createElementNS(ns, "line"); line.setAttribute("x1", start.x); line.setAttribute("y1", start.y); line.setAttribute("x2", endLineX); line.setAttribute("y2", endLineY); line.setAttribute("stroke", color); line.setAttribute("stroke-width", lineWidth); line.setAttribute("stroke-opacity", opacity); const polygon = document.createElementNS(ns, "polygon"); polygon.setAttribute("points", `${end.x},${end.y} ${corner1X},${corner1Y} ${corner2X},${corner2Y}`); polygon.setAttribute("fill", color); polygon.setAttribute("fill-opacity", opacity); svg.appendChild(line); svg.appendChild(polygon); if (text) { const midX = (start.x + end.x) / 2; const midY = (start.y + end.y) / 2; const txt = document.createElementNS(ns, "text"); txt.setAttribute("x", midX); txt.setAttribute("y", midY); txt.setAttribute("dominant-baseline", "middle"); txt.setAttribute("text-anchor", "middle"); txt.setAttribute("fill", "#000000"); txt.setAttribute("font-size", 4 * scale + "px"); txt.setAttribute("font-weight", "bold"); txt.textContent = text; svg.appendChild(txt); } state.board.appendChild(svg); } function toggleAutoQueue() { if (state.newGameObserver) { state.newGameObserver.disconnect(); state.newGameObserver = null; } if (state.queueTimeout) { clearTimeout(state.queueTimeout); state.queueTimeout = null; } if (settings.autoQueue) { state.newGameObserver = new MutationObserver((mutations) => { const btns = Array.from(document.querySelectorAll("button")); const newGameBtn = btns.find((b) => { const txt = b.innerText.toLowerCase(); return txt.includes("new") && !txt.includes("rematch") && b.offsetParent !== null; }); if (newGameBtn) { if (!state.queueTimeout) { state.queueTimeout = setTimeout(() => { newGameBtn.click(); state.queueTimeout = null; }, 100); } } }); state.newGameObserver.observe(document.body, { childList: !0, subtree: !0 }); } } function saveSetting(key, val) { GM_setValue(`bot_${key}`, val); } function resetSettings() { const currentModel = settings.engineMode; Object.assign(settings, DEFAULT_SETTINGS); settings.engineMode = currentModel; Object.keys(DEFAULT_SETTINGS).forEach((k) => { if (k !== "engineMode") saveSetting(k, DEFAULT_SETTINGS[k]); }); saveSetting("engineMode", currentModel); const hsl = rgbToHsl(...Object.values(hexToRgb(settings.highlightColor))); state.h = hsl.h; state.s = hsl.s; state.l = hsl.l; toggleAutoQueue(); createUI(); } function syncColor() { const rgb = hslToRgb(state.h, state.s, state.l); const hex = rgbToHex(rgb.r, rgb.g, rgb.b); settings.highlightColor = hex; saveSetting("highlightColor", hex); if (state.ui.inpR) { state.ui.inpR.value = rgb.r; state.ui.inpG.value = rgb.g; state.ui.inpB.value = rgb.b; state.ui.inpHex.value = hex; state.ui.colorPreview.style.background = hex; state.ui.sliderH.value = state.h; state.ui.sliderS.value = state.s; state.ui.sliderL.value = state.l; } highlightMove(state.currentBestMove); } function createUI() { if (document.getElementById("enginePanel")) document.getElementById("enginePanel").remove(); if (document.getElementById("modalOv")) document.getElementById("modalOv").remove(); if (document.getElementById("histModalOv")) document.getElementById("histModalOv").remove(); if (document.getElementById("fenTooltip")) document.getElementById("fenTooltip").remove(); Object.keys(DEFAULT_SETTINGS).forEach((k) => { const saved = GM_getValue(`bot_${k}`); if (saved !== undefined) settings[k] = saved; }); state.history = GM_getValue("bot_history", []); const initHsl = rgbToHsl(...Object.values(hexToRgb(settings.highlightColor))); state.h = initHsl.h; state.s = initHsl.s; state.l = initHsl.l; const savedW = GM_getValue("panelW", "33vw"); const savedH = GM_getValue("panelH", "100vh"); const savedX = GM_getValue("pX", "auto"); const savedY = GM_getValue("pY", "0"); const isMini = GM_getValue("isMini", !1); const style = ` :root { --bot-bg:#222; --bot-b:#444; --bot-p:#81b64c; --bot-t:#eee; --bot-inp:#333; } #enginePanel * { box-sizing: border-box; } #enginePanel { position:fixed; width:${savedW}; height:${savedH}; min-width:300px; min-height:300px; background:var(--bot-bg); border:1px solid var(--bot-b); color:var(--bot-t); z-index:9999; font-family:sans-serif; box-shadow:-4px 0 15px rgba(0,0,0,0.5); font-size:14px; display:flex; flex-direction:column; resize:both; overflow:hidden; } #enginePanel.minified #panelContent { display:none; } #enginePanel.minified { height:38px !important; resize:none; min-height:0 !important; overflow:hidden !important; border-bottom:0; } #panelHeader { background:var(--bot-p); color:#000; padding:10px; font-weight:bold; display:flex; justify-content:space-between; align-items:center; cursor:move; flex:none; user-select:none; height:38px; } #panelContent { padding:15px; display:flex; flex-direction:column; gap:10px; overflow-y:auto; flex:1; min-height: 0; } .sect { border-top:1px solid #333; padding-top:10px; display:flex; flex-direction:column; gap:8px; } .sect-title { font-size:0.85em; color:#aaa; font-weight:bold; text-transform:uppercase; margin-bottom:4px; } .row { display:flex; justify-content:space-between; align-items:center; gap: 10px; } input, select { background:var(--bot-inp); color:var(--bot-t); border:1px solid var(--bot-b); padding:4px; border-radius:4px; } input[type="number"] { width: 60px; } select { width: 120px; } input[type="text"] { flex:1; } button { background:var(--bot-p); border:none; padding:10px; color:#000; font-weight:bold; cursor:pointer; border-radius:4px; } button:disabled { opacity:0.6; cursor:not-allowed; } #custBtn { background:#00bcd4; margin-top:5px; } #histBtn { background:#8e44ad; margin-top:5px; color: white; } .log-box { background:#111; padding:8px; font-family:monospace; font-size:0.75em; border-radius:4px; overflow-y:auto; word-break:break-all; white-space:pre-wrap; border:1px solid #333; height:100px; resize:vertical; user-select: text !important; -webkit-user-select: text !important; cursor: text; } #statusBox { background:#003344; padding:8px; border:1px solid #00bcd4; border-radius:4px; font-size:0.9em; min-height:40px; width: 100%; flex-shrink: 0; display: flex; flex-direction: column; gap: 5px; } #modalOv, #histModalOv { position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:10000; display:none; justify-content:center; align-items:center; } #modal, #histModal { background:var(--bot-bg); padding:20px; border-radius:8px; width:350px; border:1px solid var(--bot-b); } #histModal { width: 600px; height: 600px; max-height: 90vh; display: flex; flex-direction: column; } #modal * { color:var(--bot-t); } #modal label, #histModal label { color: #ffffff !important; opacity: 1 !important; font-weight: 600; } #modal input[type="color"] { height: 24px; padding: 0; width: 40px; cursor:pointer; border: none; } #modal select { height: 24px; padding: 0 4px; font-size: 0.9em; } .show-cloud { display: none; } .show-local { display: none; } body.mode-cloud .show-cloud { display: flex; } body.mode-local .show-local { display: flex; } .rgb-inputs { display: flex; gap: 5px; flex: 1; justify-content: flex-end; } .rgb-inputs input { width: 45px; text-align: center; } #sliderH { background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); height: 10px; border-radius: 5px; appearance: none; } #sliderH::-webkit-slider-thumb { -webkit-appearance: none; width: 15px; height: 15px; border-radius: 50%; background: #fff; cursor: pointer; border: 1px solid #000; } #histTableContainer { flex: 1; overflow-y: auto; border: 1px solid #444; border-radius: 4px; margin-top: 10px; } #histTable { width:100%; border-collapse: collapse; font-size:0.85em; } #histTable th, #histTable td { border-bottom: 1px solid #444; padding: 6px; text-align: left; color: #eee; } #histTable th { background: #333; color: var(--bot-p); position: sticky; top: 0; z-index: 1; } #histTable tr:hover { background: #2a2a2a; } .hist-win { color: #81b64c; font-weight: bold; } .hist-loss { color: #ff5555; font-weight: bold; } .hist-draw { color: #aaaaaa; font-weight: bold; } .hist-fen { max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; color: #888; text-decoration: underline dotted; } .btn-del { background: #ff5555; color: white; padding: 2px 6px; border-radius: 3px; font-size: 0.7em; cursor: pointer; border: none; } .hist-controls { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; } .hist-controls label { color: #fff !important; font-size: 0.95em; opacity: 1; } #histEmpty { padding: 20px; text-align: center; color: #888; } #fenTooltip { position: fixed; border: 3px solid #333; background: #222; z-index: 10001; display: none; pointer-events: none; box-shadow: 0 4px 15px rgba(0,0,0,0.5); } .fen-board { display: grid; grid-template-columns: repeat(8, 1fr); width: 240px; height: 240px; border: 2px solid #555; } .fen-sq { width: 30px; height: 30px; display: flex; justify-content: center; align-items: center; background-size: 100%; background-repeat: no-repeat; } .fen-sq.light { background-color: #eeeed2; } .fen-sq.dark { background-color: #769656; } `; const html = `
Engine Controls
${state.lastLiveResult}
${state.lastMoveResult}
Engine Config
PV Display
Automation
-
Next: N/A

Game History

Max Capacity: 200 Games
Date Color Result Clock FEN
`; document.body.insertAdjacentHTML("beforeend", html); const panel = document.getElementById("enginePanel"); const computed = window.getComputedStyle(panel); panel.style.width = computed.width; if (!isMini) { panel.style.height = computed.height; } if (savedX === "auto") { panel.style.right = "0px"; panel.style.left = "auto"; } else { panel.style.left = savedX + "px"; } panel.style.top = savedY + "px"; state.ui = { panel: panel, header: document.getElementById("panelHeader"), minBtn: document.getElementById("minBtn"), moveResult: document.getElementById("moveResult"), liveOutput: document.getElementById("statusBox"), logSent: document.getElementById("sentCommandOutput"), logRec: document.getElementById("receivedMessageOutput"), delayDisplay: document.getElementById("delayDisplay"), btnAnalyze: document.getElementById("btnAnalyze"), selMode: document.getElementById("selMode"), inpDepth: document.getElementById("inpDepth"), inpTime: document.getElementById("inpTime"), inpContempt: document.getElementById("inpContempt"), inpSearch: document.getElementById("inpSearch"), chkRun: document.getElementById("chkRun"), chkMove: document.getElementById("chkMove"), chkQueue: document.getElementById("chkQueue"), chkPV: document.getElementById("chkPV"), inpPVDepth: document.getElementById("inpPVDepth"), chkPVNums: document.getElementById("chkPVNums"), chkPVGrad: document.getElementById("chkPVGrad"), inpPVStart: document.getElementById("inpPVStart"), inpPVEnd: document.getElementById("inpPVEnd"), pvSettings: document.getElementById("pvSettings"), pvGradSettings: document.getElementById("pvGradSettings"), inpMin: document.getElementById("inpMin"), inpMax: document.getElementById("inpMax"), chkDebug: document.getElementById("chkDebug"), debugArea: document.getElementById("debugArea"), btnReset: document.getElementById("btnReset"), lblMaxDepth: document.getElementById("lblMaxDepth"), custBtn: document.getElementById("custBtn"), histBtn: document.getElementById("histBtn"), modal: document.getElementById("modalOv"), modalClose: document.getElementById("modalClose"), histModal: document.getElementById("histModalOv"), histModalClose: document.getElementById("histModalClose"), histBody: document.getElementById("histBody"), btnClearHist: document.getElementById("btnClearHist"), chkHistory: document.getElementById("chkHistory"), visType: document.getElementById("visType"), visBoxSettings: document.getElementById("visBoxSettings"), visArrowSettings: document.getElementById("visArrowSettings"), visInnerOp: document.getElementById("visInnerOp"), visOuterOp: document.getElementById("visOuterOp"), visBias: document.getElementById("visBias"), visArrowOp: document.getElementById("visArrowOp"), visArrowWidth: document.getElementById("visArrowWidth"), sliderH: document.getElementById("sliderH"), sliderS: document.getElementById("sliderS"), sliderL: document.getElementById("sliderL"), colorPreview: document.getElementById("colorPreview"), inpR: document.getElementById("inpR"), inpG: document.getElementById("inpG"), inpB: document.getElementById("inpB"), inpHex: document.getElementById("inpHex"), fenTooltip: document.getElementById("fenTooltip"), }; state.ui.selMode.value = settings.engineMode; const bind = (el, key, type = "val") => { if (!el) return; el.addEventListener(type === "chk" ? "change" : "input", (e) => { const val = type === "chk" ? e.target.checked : type === "num" ? parseFloat(e.target.value) : e.target.value; settings[key] = val; saveSetting(key, val); if (key === "autoMove" && val === !0) triggerAutoMove(); if (key === "autoQueue") toggleAutoQueue(); if ( [ "innerOpacity", "outerOpacity", "gradientBias", "arrowOpacity", "arrowWidth", "showPVArrows", "pvDepth", "pvShowNumbers", "pvCustomGradient", "pvStartColor", "pvEndColor", ].includes(key) ) highlightMove(state.currentBestMove); updateUI(); }); }; state.ui.btnAnalyze.onclick = () => analyze(); state.ui.btnReset.onclick = resetSettings; state.ui.custBtn.onclick = () => (state.ui.modal.style.display = "flex"); state.ui.modalClose.onclick = () => (state.ui.modal.style.display = "none"); state.ui.histBtn.onclick = () => { renderHistory(); state.ui.histModal.style.display = "flex"; }; state.ui.histModalClose.onclick = () => (state.ui.histModal.style.display = "none"); state.ui.btnClearHist.onclick = () => { if (confirm("Delete all history?")) { state.history = []; GM_setValue("bot_history", []); renderHistory(); } }; state.ui.chkHistory.onchange = (e) => { settings.enableHistory = e.target.checked; saveSetting("enableHistory", e.target.checked); }; state.ui.minBtn.onclick = () => { const isMini = state.ui.panel.classList.toggle("minified"); GM_setValue("isMini", isMini); }; [state.ui.sliderH, state.ui.sliderS, state.ui.sliderL].forEach((el) => { el.oninput = () => { state.h = parseFloat(state.ui.sliderH.value); state.s = parseFloat(state.ui.sliderS.value); state.l = parseFloat(state.ui.sliderL.value); syncColor(); }; }); [state.ui.inpR, state.ui.inpG, state.ui.inpB].forEach((el) => { el.oninput = () => { const r = parseInt(state.ui.inpR.value) || 0, g = parseInt(state.ui.inpG.value) || 0, b = parseInt(state.ui.inpB.value) || 0; const hsl = rgbToHsl(r, g, b); state.h = hsl.h; state.s = hsl.s; state.l = hsl.l; syncColor(); }; }); state.ui.inpHex.onchange = (e) => { const hex = e.target.value; if (/^#[0-9A-F]{6}$/i.test(hex)) { const rgb = hexToRgb(hex); const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); state.h = hsl.h; state.s = hsl.s; state.l = hsl.l; syncColor(); } }; syncColor(); state.ui.header.onmousedown = (e) => { if (e.target.id === "minBtn" || e.target.id === "btnReset") return; e.preventDefault(); const startX = e.clientX - state.ui.panel.offsetLeft; const startY = e.clientY - state.ui.panel.offsetTop; const onMove = (mv) => { let x = mv.clientX - startX; let y = mv.clientY - startY; x = Math.max(0, Math.min(x, window.innerWidth - state.ui.panel.offsetWidth)); y = Math.max(0, Math.min(y, window.innerHeight - state.ui.panel.offsetHeight)); state.ui.panel.style.left = x + "px"; state.ui.panel.style.top = y + "px"; state.ui.panel.style.right = "auto"; GM_setValue("pX", x); GM_setValue("pY", y); }; document.addEventListener("mousemove", onMove); document.onmouseup = () => document.removeEventListener("mousemove", onMove); }; new ResizeObserver(() => { if (!state.ui.panel.classList.contains("minified")) { GM_setValue("panelW", state.ui.panel.style.width); GM_setValue("panelH", state.ui.panel.style.height); } }).observe(state.ui.panel); state.ui.selMode.onchange = (e) => { settings.engineMode = e.target.value; saveSetting("engineMode", e.target.value); state.isThinking = !1; if (settings.engineMode === "local") loadLocalEngine(); updateUI(); }; state.ui.chkDebug.onchange = (e) => { settings.debugLogs = e.target.checked; saveSetting("debugLogs", e.target.checked); updateUI(); }; state.ui.visType.onchange = (e) => { settings.visualType = e.target.value; saveSetting("visualType", e.target.value); toggleVisualInputs(); highlightMove(state.currentBestMove); }; function toggleVisualInputs() { if (settings.visualType === "arrow") { state.ui.visBoxSettings.style.display = "none"; state.ui.visArrowSettings.style.display = "block"; } else { state.ui.visBoxSettings.style.display = "block"; state.ui.visArrowSettings.style.display = "none"; } } state.ui.visType.value = settings.visualType; toggleVisualInputs(); bind(state.ui.inpDepth, "depth", "num"); bind(state.ui.inpTime, "maxThinkingTime", "num"); bind(state.ui.inpContempt, "contempt", "num"); bind(state.ui.inpSearch, "searchMoves"); bind(state.ui.chkRun, "autoRun", "chk"); bind(state.ui.chkMove, "autoMove", "chk"); bind(state.ui.chkQueue, "autoQueue", "chk"); bind(state.ui.chkPV, "showPVArrows", "chk"); bind(state.ui.inpPVDepth, "pvDepth", "num"); bind(state.ui.chkPVNums, "pvShowNumbers", "chk"); bind(state.ui.chkPVGrad, "pvCustomGradient", "chk"); bind(state.ui.inpPVStart, "pvStartColor"); bind(state.ui.inpPVEnd, "pvEndColor"); bind(state.ui.inpMin, "minDelay", "num"); bind(state.ui.inpMax, "maxDelay", "num"); bind(state.ui.visInnerOp, "innerOpacity", "num"); bind(state.ui.visOuterOp, "outerOpacity", "num"); bind(state.ui.visBias, "gradientBias", "num"); bind(state.ui.visArrowOp, "arrowOpacity", "num"); bind(state.ui.visArrowWidth, "arrowWidth", "num"); toggleAutoQueue(); updateUI(); } function drawFenBoard(fen) { let rows = fen.split(" ")[0].split("/"); let board = []; for (let r of rows) { let rowArr = []; for (let char of r) { if (!isNaN(char)) { let empties = parseInt(char); for (let k = 0; k < empties; k++) rowArr.push(""); } else { rowArr.push(char); } } board.push(rowArr); } let html = '
'; for (let r = 0; r < 8; r++) { for (let c = 0; c < 8; c++) { const piece = board[r][c]; const isDark = (r + c) % 2 === 1; const bg = piece ? `style="background-image: url('${PIECE_IMGS[piece]}');"` : ""; html += `
`; } } html += "
"; return html; } function renderHistory() { if (!state.ui.histBody) return; state.ui.histBody.innerHTML = ""; if (state.history.length === 0) { state.ui.histBody.innerHTML = 'No history yet.'; return; } const sorted = [...state.history].reverse(); sorted.forEach((item, index) => { const tr = document.createElement("tr"); let resClass = "hist-draw"; if (item.result === "Win") resClass = "hist-win"; else if (item.result === "Loss") resClass = "hist-loss"; tr.innerHTML = ` ${item.date} ${item.color || "N/A"} ${item.result} ${item.myTime} / ${item.oppTime} ${item.fen} `; state.ui.histBody.appendChild(tr); }); document.querySelectorAll(".btn-del").forEach((btn) => { btn.onclick = (e) => { const idx = parseInt(e.target.dataset.idx); state.history.splice(idx, 1); GM_setValue("bot_history", state.history); renderHistory(); }; }); document.querySelectorAll(".hist-fen").forEach((el) => { el.onmouseenter = (e) => { const fen = e.target.getAttribute("data-fen"); if (fen && state.ui.fenTooltip) { state.ui.fenTooltip.innerHTML = drawFenBoard(fen); state.ui.fenTooltip.style.display = "block"; const rect = e.target.getBoundingClientRect(); let left = rect.left + 20; let top = rect.bottom + 5; if (left + 250 > window.innerWidth) left = window.innerWidth - 260; if (top + 250 > window.innerHeight) top = rect.top - 260; state.ui.fenTooltip.style.left = left + "px"; state.ui.fenTooltip.style.top = top + "px"; } }; el.onmouseleave = () => { if (state.ui.fenTooltip) state.ui.fenTooltip.style.display = "none"; }; }); } function checkForGameOver() { if (!settings.enableHistory) return; const resultEl = document.querySelector( ".game-result-component, .game-over-modal-content, .daily-game-footer-game-over" ); if (resultEl) { if (state.hasSavedCurrentGameResult) return; let fen = sanitizeFEN(getRawBoardFEN()); let playingAsCode = state.playingAs; if (!playingAsCode && state.board?.game?.getPlayingAs) { try { playingAsCode = state.board.game.getPlayingAs(); } catch (e) {} } if (playingAsCode !== 1 && playingAsCode !== 2) playingAsCode = 0; const playerColor = playingAsCode === 2 ? "Black" : "White"; if (playingAsCode === 2) { let parts = fen.split(" "); if (parts.length > 0) { parts[0] = parts[0] .split("/") .reverse() .map((row) => { return row.split("").reverse().join(""); }) .join("/"); fen = parts.join(" "); } } let myTime = "N/A"; let oppTime = "N/A"; const clockBot = document.querySelector(".clock-bottom .clock-time-monospace, .clock-bottom"); const clockTop = document.querySelector(".clock-top .clock-time-monospace, .clock-top"); if (clockBot) myTime = clockBot.innerText; if (clockTop) oppTime = clockTop.innerText; let resultTxt = "Ended"; let simpleRes = "Draw"; const mainMsg = resultEl.querySelector(".game-result-main-message, .game-over-header-title"); if (mainMsg) resultTxt = mainMsg.innerText.trim(); else resultTxt = resultEl.innerText.split("\n")[0].trim(); const subMsgEl = resultEl.querySelector(".game-result-sub-message, .game-over-header-subtitle"); let subMsg = subMsgEl ? subMsgEl.innerText.trim() : ""; const fullText = (resultTxt + " " + subMsg).toLowerCase(); if (resultEl.classList.contains("game-result-win")) { simpleRes = "Win"; } else if (resultEl.classList.contains("game-result-loss")) { simpleRes = "Loss"; } else if (resultEl.classList.contains("game-result-draw")) { simpleRes = "Draw"; } else if (fullText.includes("you won")) { simpleRes = "Win"; } else if (fullText.includes("you lost")) { simpleRes = "Loss"; } else if (playingAsCode === 1 && fullText.includes("white won")) { simpleRes = "Win"; } else if (playingAsCode === 1 && fullText.includes("black won")) { simpleRes = "Loss"; } else if (playingAsCode === 2 && fullText.includes("black won")) { simpleRes = "Win"; } else if (playingAsCode === 2 && fullText.includes("white won")) { simpleRes = "Loss"; } const gameObj = { date: new Date().toLocaleString(), color: playerColor, result: simpleRes, fen: fen, myTime: myTime, oppTime: oppTime, id: Date.now(), }; state.history.push(gameObj); if (state.history.length > 200) state.history.shift(); GM_setValue("bot_history", state.history); state.hasSavedCurrentGameResult = !0; if (state.ui.histModal && state.ui.histModal.style.display !== "none") renderHistory(); } else { state.hasSavedCurrentGameResult = !1; } } function enforceBounds() { if (state.ui.panel) { const rect = state.ui.panel.getBoundingClientRect(); const winW = window.innerWidth; const winH = window.innerHeight; if (rect.right > winW) state.ui.panel.style.width = winW - rect.left + "px"; if (rect.bottom > winH) state.ui.panel.style.height = winH - rect.top + "px"; if (rect.left < 0) state.ui.panel.style.left = "0px"; if (rect.top < 0) state.ui.panel.style.top = "0px"; } requestAnimationFrame(enforceBounds); } requestAnimationFrame(enforceBounds); function updateUI() { if (!state.ui.panel) return; document.body.classList.remove("mode-cloud", "mode-local", "mode-sfonline"); document.body.classList.add(`mode-${settings.engineMode}`); if (state.ui.debugArea) state.ui.debugArea.style.display = settings.debugLogs ? "block" : "none"; let maxD = 18; if (settings.engineMode === "local") maxD = 23; else if (settings.engineMode === "sfonline") maxD = 15; if (state.ui.lblMaxDepth) state.ui.lblMaxDepth.innerText = maxD; if (state.ui.inpDepth) state.ui.inpDepth.max = maxD; if (state.ui.inpPVDepth) { state.ui.inpPVDepth.max = 45; } if (state.ui.pvSettings) { state.ui.pvSettings.style.display = settings.showPVArrows ? "block" : "none"; } if (state.ui.pvGradSettings) { state.ui.pvGradSettings.style.display = settings.pvCustomGradient ? "block" : "none"; } if (state.ui.btnAnalyze) state.ui.btnAnalyze.disabled = state.isThinking; if (state.ui.moveResult) state.ui.moveResult.innerHTML = state.lastMoveResult; if (state.ui.liveOutput) state.ui.liveOutput.innerHTML = state.lastLiveResult; if (state.ui.delayDisplay) state.ui.delayDisplay.innerText = `Randomized Delay: ${state.calculatedDelay}s`; if (state.ui.logSent) state.ui.logSent.innerText = state.lastPayload; if (state.ui.logRec) state.ui.logRec.innerText = state.lastResponse; if (document.activeElement !== state.ui.inpDepth) state.ui.inpDepth.value = settings.depth; } function mainLoop() { state.board = document.querySelector(CONFIG.BOARD_SEL); if (!state.ui.panel) createUI(); if (state.board?.game?.getPlayingAs) { try { const pa = state.board.game.getPlayingAs(); if (pa === 1 || pa === 2) state.playingAs = pa; } catch (e) {} } if (state.board?.game && settings.autoRun) { const raw = getRawBoardFEN(); if (raw) { const clean = sanitizeFEN(raw); const isTurn = state.board.game.getTurn() === state.board.game.getPlayingAs(); if (isTurn && clean !== state.lastSanitizedBoardFEN) { analyze(settings.depth); } } } checkForGameOver(); updateUI(); } setInterval(mainLoop, CONFIG.LOOP_MS); })();