Triple model automated cheat engine (SF 17.1, SF 16.1, SF 10.0.2) with fully customizable UI and configuration settings.
// ==UserScript==
// @name BEST Cheat for Chess.com (Stockfish 17.1, 16.1 & 10.0.2, No Anti-Ban)
// @namespace http://tampermonkey.net/
// @version 6.9
// @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
// @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
// ==/UserScript==
(function () {
"use strict";
const CONFIG = { BOARD_SEL: "chess-board, wc-chess-board", LOOP_MS: 50, API: { MAX_DEPTH: 18, MAX_TIME: 2000 } };
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,
analysisStartTime: 0,
h: 180,
s: 100,
l: 50,
newGameObserver: null,
queueTimeout: null,
};
const DEFAULT_SETTINGS = {
engineMode: "cloud",
depth: 12,
maxThinkingTime: 2000,
contempt: 100,
searchMoves: "",
autoRun: !0,
autoMove: !1,
autoQueue: !1,
minDelay: 0,
maxDelay: 0,
highlightColor: "#00eeff",
innerOpacity: 0.6,
outerOpacity: 0.2,
gradientBias: 0,
debugLogs: !1,
};
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 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 = "<span style='color:red; font-weight:bold;'>HIGH USAGE: Cooldown ~30-60m.</span>";
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 =
"<span style='color:red; font-weight:bold;'>HIGH USAGE: Cooldown ~30-60m.</span>";
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;
}
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] : "";
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}: <span style="font-weight:bold; color:var(--bot-primary);">${best}</span>`;
}
state.lastLiveResult = `
<div style="display:flex; justify-content:space-between; align-items:center; font-weight:bold;">
<span style="color:var(--bot-primary); font-size:1.1em;">${scoreTxt} <span style="font-size:0.7em; color:#aaa; font-weight:normal;">(${duration}s)</span></span>
<span style="font-size:0.8em; color:#aaa;">(Local Depth ${depth})</span>
</div>
<div style="margin-top:5px; font-size:0.85em; color:#bbb; width:100%; max-width:100%; box-sizing:border-box; word-wrap:break-word; overflow-wrap:anywhere; white-space:normal;">
<span style="color:#888;">PV:</span> ${pv.split(" ").slice(0, 5).join(" ")}...
</div>
`;
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, undefined, undefined, undefined, undefined, duration);
} else state.lastMoveResult = "⚠️ No move found";
updateUI();
}
}
function processBestMove(bestMove, evalScore, mate, continuationArr, winChance, duration) {
state.currentBestMove = 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
? `<span style="font-size:0.7em; color:#aaa; font-weight:normal;">(${duration}s)</span>`
: "";
state.lastMoveResult = `✅ Best: <span style="font-weight:bold; color:var(--bot-primary);">${bestMove}</span>`;
let wcHtml = "";
if (winChance) wcHtml = `<span style="color:#aaa; font-size:0.8em;">(${Math.round(winChance)}%)</span>`;
state.lastLiveResult = `
<div style="display:flex; justify-content:space-between; align-items:center; font-weight:bold;">
<span style="color:var(--bot-primary); font-size:1.1em;">${scoreTxt} ${durHtml}</span>
<span>${wcHtml}</span>
</div>
<div style="margin-top:5px; font-size:0.85em; color:#bbb; width:100%; max-width:100%; box-sizing:border-box; word-wrap:break-word; overflow-wrap:anywhere; white-space:normal;">
<span style="color:#888;">PV:</span> ${pvStr}
</div>
`;
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;
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);
});
}
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;
}, 500);
}
}
});
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();
Object.keys(DEFAULT_SETTINGS).forEach((k) => {
const saved = GM_getValue(`bot_${k}`);
if (saved !== undefined) settings[k] = saved;
});
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; }
.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 { 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 { background:var(--bot-bg); padding:20px; border-radius:8px; width:350px; border:1px solid var(--bot-b); }
#modal * { color:var(--bot-t); }
#modal 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; }
`;
const html = `
<style>${style}</style>
<div id="enginePanel" class="${isMini ? "minified" : ""}">
<div id="panelHeader">
<div style="display:flex; align-items:center; gap:5px;">
<span>Engine Controls</span>
<span id="minBtn" style="cursor:pointer; padding:0 5px;">▼</span>
</div>
<button id="btnReset" style="padding:2px 8px; font-size:0.8em; background:#0002; color:#000; cursor:pointer;">Reset Defaults</button>
</div>
<div id="panelContent">
<div id="statusBox">${state.lastLiveResult}</div>
<div id="moveResult" style="background:#333; padding:5px; border-radius:4px; text-align:center;">${state.lastMoveResult}</div>
<div class="sect">
<div class="sect-title">Engine Config</div>
<div class="row">
<label>Model</label>
<select id="selMode" style="width:240px;">
<option value="cloud">SF 17.1.0(cloud 0.25-0.48s)</option>
<option value="sfonline">SF 16.1.0(cloud 0.15-11.0s)</option>
<option value="local">SF 10.0.2(local 0.00-75.0s)</option>
</select>
</div>
<div class="row"><label>Depth (Max <span id="lblMaxDepth">18</span>)</label><input type="number" id="inpDepth" min="1" max="18" value="${settings.depth}"></div>
<div class="row show-cloud"><label>Max Time (ms)</label><input type="number" id="inpTime" value="${settings.maxThinkingTime}"></div>
<div class="row show-local"><label>Contempt (-100→100)</label><input type="number" id="inpContempt" min="-100" max="100" value="${settings.contempt}"></div>
<div class="row show-cloud"><label>Search</label><input type="text" id="inpSearch" value="${settings.searchMoves}"></div>
</div>
<div class="sect">
<div class="sect-title">Automation</div>
<div class="row">
<label><input type="checkbox" id="chkRun" ${settings.autoRun ? "checked" : ""}> Auto-Analyze</label>
<label><input type="checkbox" id="chkMove" ${settings.autoMove ? "checked" : ""}> Auto-Move</label>
<label><input type="checkbox" id="chkQueue" ${settings.autoQueue ? "checked" : ""}> Auto-Queue</label>
</div>
<div class="row"><label>Randomized Delay (s)</label><div style="display:flex; gap:5px;"><input type="number" id="inpMin" style="width:50px" value="${settings.minDelay}"><span>-</span><input type="number" id="inpMax" style="width:50px" value="${settings.maxDelay}"></div></div>
<div style="font-size:0.7em; color:#888; text-align:right;" id="delayDisplay">Next: N/A</div>
</div>
<button id="btnAnalyze">Analyze Position</button>
<button id="custBtn">🎨 Visual Customization</button>
<div class="sect">
<div class="row"><label style="cursor:pointer"><input type="checkbox" id="chkDebug" ${settings.debugLogs ? "checked" : ""}> Show Debug Logs</label></div>
<div id="debugArea" style="display:${settings.debugLogs ? "block" : "none"}">
<div class="log-box" id="sentCommandOutput"></div>
<div class="log-box" id="receivedMessageOutput"></div>
</div>
</div>
</div>
</div>
<div id="modalOv">
<div id="modal">
<div class="row" style="border-bottom:1px solid #444; padding-bottom:10px; margin-bottom:10px;">
<h3 style="margin:0; color:var(--bot-p);">Visual Settings</h3>
<button id="modalClose" style="padding:2px 8px; font-weight:bold; cursor:pointer;">×</button>
</div>
<div class="sect" style="border:none; padding:0;">
<div class="row"><label>Inner Opacity</label><input type="range" id="visInnerOp" min="0" max="1" step="0.1" value="${settings.innerOpacity}"></div>
<div class="row"><label>Outer Opacity</label><input type="range" id="visOuterOp" min="0" max="1" step="0.1" value="${settings.outerOpacity}"></div>
<div class="row" style="padding-bottom:10px;"><label>Gradient Bias</label><input type="range" id="visBias" min="0" max="100" step="5" value="${settings.gradientBias}"></div>
</div>
<div class="sect">
<div class="sect-title" style="margin-bottom:10px;">Color Editor</div>
<div style="display:flex; flex-direction:column; gap:10px;">
<div class="row" style="width:100%;">
<div id="colorPreview" style="width:30px; height:30px; border-radius:50%; border:2px solid #555; background:${settings.highlightColor};"></div>
<div class="rgb-inputs">
<input type="number" id="inpR" min="0" max="255" placeholder="R">
<input type="number" id="inpG" min="0" max="255" placeholder="G">
<input type="number" id="inpB" min="0" max="255" placeholder="B">
</div>
</div>
<div class="row"><label>Hue</label><input type="range" id="sliderH" min="0" max="360" value="${state.h}"></div>
<div class="row"><label>Saturation</label><input type="range" id="sliderS" min="0" max="100" value="${state.s}"></div>
<div class="row"><label>Brightness</label><input type="range" id="sliderL" min="0" max="100" value="${state.l}"></div>
<div class="row" style="width:100%; margin-top:5px;"><label>Hex</label><input type="text" id="inpHex" style="text-transform:uppercase; text-align:center;"></div>
</div>
</div>
</div>
</div>
`;
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"),
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"),
modal: document.getElementById("modalOv"),
modalClose: document.getElementById("modalClose"),
visInnerOp: document.getElementById("visInnerOp"),
visOuterOp: document.getElementById("visOuterOp"),
visBias: document.getElementById("visBias"),
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"),
};
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"].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.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();
};
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.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");
toggleAutoQueue();
updateUI();
}
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.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 && 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);
}
}
}
updateUI();
}
setInterval(mainLoop, CONFIG.LOOP_MS);
})();