// ==UserScript==
// @name Chess.com Bot/Cheat - Stockfish API Debug 3 - Ad Removed - FIXED - COMPLETE SCRIPT
// @namespace BottleOrg Scripts
// @version 1.6.2-Official
// @description Chess.com Bot/Cheat using Stockfish Online API. Ad Removed. - FIXED loadEx and UI update errors - DEBUGGING VERSION 3 - IMPROVED MOVE HANDLING - COMPLETE SCRIPT
// @author MrAuzzie (Original), Modified by BottleOrg && Gemini 2.0 to become better.
// @license Chess.com Bot/Cheat © 2023 by MrAuzzie#998142, © All Rights Reserved
// @match https://www.chess.com/play/*
// @match https://www.chess.com/game/*
// @match https://www.chess.com/puzzles/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_registerMenuCommand
// @require https://gf.zukizuki.org/scripts/445697/code/index.js
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @run-at document-start
// ==/UserScript==
// Changelogs
// Made this Debug V3 script into official script due to it is working correctly
// Working Stockfish V16+ using their API
// Debugging V2 script Found out engine is not responding due to not finding a legal move
// Debugging V1 script engine responded but did not move
// Using stockfish V10.0.2 as it is working
// Removed CDN stockfish V16 engines and engine fallbacks because it doesn't work
// Added engine fallbacks if an engine fails
// added error handling and alerts
// Upgraded from Stockfish V9 to Stockfish V10.0.2
// Using Stockfish 9 currently
const currentVersion = '1.6.2-OFFICIAL';
function main() {
var engine = document.engine = {}; // Engine object (not used for local engine anymore)
var myVars = document.myVars = {};
myVars.autoMovePiece = false;
myVars.autoRun = false;
myVars.delay = 0.1;
var myFunctions = document.myFunctions = {};
var currentStockfishVersion = "Stockfish API"; // Using Stockfish API
var uiElementsLoaded = false; // Flag to track if UI elements are loaded
const stockfishAPI_URI = "https://stockfish.online/api/s/v2.php"; // Stockfish API URI
stop_b = stop_w = 0;
s_br = s_br2 = s_wr = s_wr2 = 0;
obs = "";
myFunctions.rescan = function(lev) {
var ari = $("chess-board")
.find(".piece")
.map(function() {
return this.className;
})
.get();
jack = ari.map(f => f.substring(f.indexOf(' ') + 1));
function removeWord(arr, word) {
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i].replace(word, '');
}
}
removeWord(ari, 'square-');
jack = ari.map(f => f.substring(f.indexOf(' ') + 1));
for (var i = 0; i < jack.length; i++) {
jack[i] = jack[i].replace('br', 'r')
.replace('bn', 'n')
.replace('bb', 'b')
.replace('bq', 'q')
.replace('bk', 'k')
.replace('bb', 'b')
.replace('bn', 'n')
.replace('br', 'r')
.replace('bp', 'p')
.replace('wp', 'P')
.replace('wr', 'R')
.replace('wn', 'N')
.replace('wb', 'B')
.replace('br', 'R')
.replace('wn', 'N')
.replace('wb', 'B')
.replace('wq', 'Q')
.replace('wk', 'K')
.replace('wb', 'B')
}
str2 = "";
var count = 0,
str = "";
for (var j = 8; j > 0; j--) {
for (var i = 1; i < 9; i++) {
(str = (jack.find(el => el.includes([i] + [j])))) ? str = str.replace(/[^a-zA-Z]+/g, ''): str = "";
if (str == "") {
count++;
str = count.toString();
if (!isNaN(str2.charAt(str2.length - 1))) str2 = str2.slice(0, -1);
else {
count = 1;
str = count.toString()
}
}
str2 += str;
if (i == 8) {
count = 0;
str2 += "/";
}
}
}
str2 = str2.slice(0, -1);
//str2=str2+" KQkq - 0"
color = "";
wk = wq = bk = bq = "0";
const move = $('vertical-move-list')
.children();
if (move.length < 2) {
stop_b = stop_w = s_br = s_br2 = s_wr = s_wr2 = 0;
}
if (stop_b != 1) {
if (move.find(".black.node:contains('K')")
.length) {
bk = "";
bq = "";
stop_b = 1;
console.log('debug secb');
}
} else {
bq = "";
bk = "";
}
if (stop_b != 1)(bk = (move.find(".black.node:contains('O-O'):not(:contains('O-O-O'))")
.length) ? "" : "k") ? (bq = (move.find(".black.node:contains('O-O-O')")
.length) ? bk = "" : "q") : bq = "";
if (s_br != 1) {
if (move.find(".black.node:contains('R')")
.text()
.match('[abcd]+')) {
bq = "";
s_br = 1
}
} else bq = "";
if (s_br2 != 1) {
if (move.find(".black.node:contains('R')")
.text()
.match('[hgf]+')) {
bk = "";
s_br2 = 1
}
} else bk = "";
if (stop_b == 0) {
if (s_br == 0)
if (move.find(".white.node:contains('xa8')")
.length > 0) {
bq = "";
s_br = 1;
console.log('debug b castle_r');
}
if (s_br2 == 0)
if (move.find(".white.node:contains('xh8')")
.length > 0) {
bk = "";
s_br2 = 1;
console.log('debug b castle_l');
}
}
if (stop_w != 1) {
if (move.find(".white.node:contains('K')")
.length) {
wk = "";
wq = "";
stop_w = 1;
console.log('debug secw');
}
} else {
wq = "";
wk = "";
}
if (stop_w != 1)(wk = (move.find(".white.node:contains('O-O'):not(:contains('O-O-O'))")
.length) ? "" : "K") ? (wq = (move.find(".white.node:contains('O-O-O')")
.length) ? wk = "" : "Q") : wq = "";
if (s_wr != 1) {
if (move.find(".white.node:contains('R')")
.text()
.match('[abcd]+')) {
wq = "";
s_wr = 1
}
} else wq = "";
if (s_wr2 != 1) {
if (move.find(".white.node:contains('R')")
.text()
.match('[hgf]+')) {
wk = "";
s_wr2 = 1
}
} else wk = "";
if (stop_w == 0) {
if (s_wr == 0)
if (move.find(".black.node:contains('xa1')")
.length > 0) {
wq = "";
s_wr = 1;
console.log('debug w castle_l');
}
if (s_wr2 == 0)
if (move.find(".black.node:contains('xh1')")
.length > 0) {
wk = "";
s_wr2 = 1;
console.log('debug w castle_r');
}
}
if ($('.coordinates')
.children()
.first()
.text() == 1) {
str2 = str2 + " b " + wk + wq + bk + bq;
color = "white";
} else {
str2 = str2 + " w " + wk + wq + bk + bq;
color = "black";
}
//console.log(str2);
return str2;
}
myFunctions.color = function(dat){
console.log("myFunctions.color CALLED with dat:", dat); // Added logging at start
response = dat;
const bestmoveUCI = response.split(' ')[1]; // Extract UCI move directly (e.g., "e2e4")
console.log("Extracted bestmove UCI from API response:", bestmoveUCI);
if(myVars.autoMove == true){
console.log("Auto-move is enabled, calling movePiece with UCI move:", bestmoveUCI); // Log before movePiece
myFunctions.movePiece(bestmoveUCI); // Pass UCI move directly to movePiece
} else {
console.log("Auto-move is disabled, only highlighting UCI move:", bestmoveUCI); // Log if auto-move off
myFunctions.highlightMove(bestmoveUCI); // Call highlight function with UCI move
}
isThinking = false;
console.log("myFunctions.color COMPLETED"); // Log at the end of color
}
myFunctions.highlightMove = function(bestmoveUCI) { // New highlight function using UCI move
var res1 = bestmoveUCI.substring(0, 2);
var res2 = bestmoveUCI.substring(2, 4);
$(board.nodeName)
.prepend('<div class="highlight square-' + res2 + ' bro" style="background-color: rgb(235, 97, 80); opacity: 0.71;" data-test-element="highlight"></div>')
.children(':first')
.delay(1800)
.queue(function() {
$(this)
.remove();
});
$(board.nodeName)
.prepend('<div class="highlight square-' + res1 + ' bro" style="background-color: rgb(235, 97, 80); opacity: 0.71;" data-test-element="highlight"></div>')
.children(':first')
.delay(1800)
.queue(function() {
$(this)
.remove();
});
console.log("myFunctions.highlightMove COMPLETED - move highlighted:", bestmoveUCI);
}
myFunctions.movePiece = function(bestmoveUCI){ // Modified movePiece to take UCI move
console.log("myFunctions.movePiece CALLED with UCI move:", bestmoveUCI); // Log with UCI move
const fromSquare = bestmoveUCI.substring(0, 2);
const toSquare = bestmoveUCI.substring(2, 4);
console.log("Parsed fromSquare:", fromSquare, "toSquare:", toSquare);
const legalMoves = board.game.getLegalMoves();
let foundMove = null;
for (const move of legalMoves) {
if (move.from === fromSquare && move.to === toSquare) {
foundMove = move;
break;
}
}
if (foundMove) {
console.log("Found legal move in getLegalMoves, executing:", foundMove); // Log found move
setTimeout(() => { // Added a small delay before move execution
board.game.move({
...foundMove,
promotion: 'false',
animate: false,
userGenerated: true
});
console.log("myFunctions.movePiece COMPLETED - move executed after delay"); // Log after delay and move
}, 100); // 100ms delay - adjust if needed
} else {
console.warn("myFunctions.movePiece - LEGAL MOVE NOT FOUND in getLegalMoves for UCI move:", bestmoveUCI); // Warn with UCI move
}
}
myFunctions.reloadChessEngine = function() {
console.log(`Reloading the chess engine (Stockfish API - no reload needed).`);
alert("Reloading engine for Stockfish API is not needed. Re-analyzing will use the API again.");
}
myFunctions.loadChessEngine = function() {
console.log(`Using Stockfish Online API. No local engine loading.`);
if (uiElementsLoaded) {
$('#engineVersionText')[0].innerHTML = "Chess Engine: <strong>Stockfish API Loaded</strong>";
}
}
myFunctions.fetchBestMoveFromAPI = function(fen, depth) {
const apiURL = `${stockfishAPI_URI}?fen=${encodeURIComponent(fen)}&depth=${depth}`;
console.log(`Fetching best move from API: ${apiURL}`);
GM_xmlhttpRequest({
method: "GET",
url: apiURL,
onload: function(response) {
if (response.status === 200) {
try {
const jsonResponse = JSON.parse(response.responseText);
if (jsonResponse.success === true) {
const bestmove = jsonResponse.bestmove;
console.log(`API Response SUCCESS: Bestmove - ${bestmove}, Evaluation - ${jsonResponse.evaluation}, Mate - ${jsonResponse.mate}`); // Success log
console.log("Calling myFunctions.color with bestmove:", bestmove); // Log before calling color
myFunctions.color(bestmove);
} else {
console.error("API request was successful, but 'success' is false in response:", jsonResponse);
alert("Stockfish API returned an error. Please check console for details.");
isThinking = false; // Stop thinking state
if (uiElementsLoaded) {
$('#engineVersionText')[0].innerHTML = "<span style='color:red;'>Chess Engine: <strong>Stockfish API Error</strong></span>";
}
}
} catch (e) {
console.error("Error parsing API response JSON:", e);
alert("Error parsing Stockfish API response. Please check console for details.");
isThinking = false; // Stop thinking state
if (uiElementsLoaded) {
$('#engineVersionText')[0].innerHTML = "<span style='color:red;'>Chess Engine: <strong>API Response Error</strong></span>";
}
}
} else {
console.error("Stockfish API request failed with status:", response.status, response.statusText);
alert(`Stockfish API request failed. Status: ${response.status} ${response.statusText}. Please check console and internet connection.`);
isThinking = false; // Stop thinking state
if (uiElementsLoaded) {
$('#engineVersionText')[0].innerHTML = "<span style='color:red;'>Chess Engine: <strong>API Request Failed</strong></span>";
}
}
},
onerror: function(error) {
console.error("GM_xmlhttpRequest error:", error);
alert("Error making Stockfish API request. Please check console and internet connection.");
isThinking = false; // Stop thinking state
if (uiElementsLoaded) {
$('#engineVersionText')[0].innerHTML = "<span style='color:red;'>Chess Engine: <strong>API Request Error</strong></span>";
}
}
});
};
var lastValue = 11;
myFunctions.runChessEngine = function(depth){
if (currentStockfishVersion === "Failed" || currentStockfishVersion === "None") {
console.warn("Chess engine is not available (failed to load). Cannot run engine.");
return;
}
var fen = board.game.getFEN();
console.log(`[Stockfish API] Requesting analysis for FEN: ${fen}, Depth: ${depth}`);
isThinking = true;
myFunctions.fetchBestMoveFromAPI(fen, 7); // Call API function with REDUCED DEPTH (7) for bullet
lastValue = depth; // Keep lastValue for UI display, but API uses fixed depth 7 now for testing
}
myFunctions.autoRun = function(lstValue){
if (currentStockfishVersion === "Failed" || currentStockfishVersion === "None") return;
if(board.game.getTurn() == board.game.getPlayingAs()){
myFunctions.runChessEngine(lstValue);
}
}
document.onkeydown = function(e) {
if (currentStockfishVersion === "Failed" || currentStockfishVersion === "None") return;
switch (e.keyCode) {
case 81:
myFunctions.runChessEngine(1);
break;
case 87:
myFunctions.runChessEngine(2);
break;
case 69:
myFunctions.runChessEngine(3);
break;
case 82:
myFunctions.runChessEngine(4);
break;
case 84:
myFunctions.runChessEngine(5);
break;
case 89:
myFunctions.runChessEngine(6);
break;
case 85:
myFunctions.runChessEngine(7);
break;
case 73:
myFunctions.runChessEngine(8);
break;
case 79:
myFunctions.runChessEngine(9);
break;
case 80:
myFunctions.runChessEngine(10);
break;
case 65:
myFunctions.runChessEngine(11);
break;
case 83:
myFunctions.runChessEngine(12);
break;
case 68:
myFunctions.runChessEngine(13);
break;
case 70:
myFunctions.runChessEngine(14);
break;
case 71:
myFunctions.runChessEngine(15);
break;
case 72:
myFunctions.runChessEngine(16);
break;
case 74:
myFunctions.runChessEngine(17);
break;
case 75:
myFunctions.runChessEngine(18);
case 76:
myFunctions.runChessEngine(19);
break;
case 90:
myFunctions.runChessEngine(20);
break;
case 88:
myFunctions.runChessEngine(21);
break;
case 67:
myFunctions.runChessEngine(22);
break;
case 86:
myFunctions.runChessEngine(23);
break;
case 66:
myFunctions.runChessEngine(24);
break;
case 78:
myFunctions.runChessEngine(25);
break;
case 77:
myFunctions.runChessEngine(26);
break;
case 187:
myFunctions.runChessEngine(100);
break;
}
};
myFunctions.spinner = function() {
if(isThinking == true){
$('#overlay')[0].style.display = 'block';
}
if(isThinking == false) {
$('#overlay')[0].style.display = 'none';
}
}
let dynamicStyles = null;
function addAnimation(body) {
if (!dynamicStyles) {
dynamicStyles = document.createElement('style');
dynamicStyles.type = 'text/css';
document.head.appendChild(dynamicStyles);
}
dynamicStyles.sheet.insertRule(body, dynamicStyles.length);
}
var loaded = false;
myFunctions.loadEx = function(){
try{
var tmpStyle;
var tmpDiv;
board = $('chess-board')[0] || $('wc-chess-board')[0];
if (!board) { // Check if board element is found
console.warn("Chessboard element not found yet. Retrying...");
return; // Exit and retry in the next interval
}
myVars.board = board;
var div = document.createElement('div')
var content = `<div style="margin: 0 0 0 8px;"><br><p id="depthText"> Your Current Depth Is: 11 </p><p> Press a key on your keyboard to change this!</p><p id="engineVersionText">Chess Engine: Stockfish API</p><br><input type="checkbox" id="autoRun" name="autoRun" value="false">
<label for="autoRun"> Enable auto run</label><br>
<input type="checkbox" id="autoMove" name="autoMove" value="false">
<label for="autoMove"> Enable auto move</label><br>
<input type="number" id="timeDelayMin" name="timeDelayMin" min="0.1" value=0.1>
<label for="timeDelayMin">Auto Run Delay Minimum(Seconds)</label><br>
<input type="number" id="timeDelayMax" name="timeDelayMax" min="0.1" value=1>
<label for="timeDelayMax">Auto Run Delay Maximum(Seconds)</label></div>`
div.innerHTML = content;
div.setAttribute('style','background-color:white; height:auto;');
div.setAttribute('id','settingsContainer');
board.parentElement.parentElement.appendChild(div); //parentElement might be null, but board is checked above
//spinnerContainer
var spinCont = document.createElement('div');
spinCont.setAttribute('style','display:none;');
spinCont.setAttribute('id','overlay');
div.prepend(spinCont);
//spinner
var spinr = document.createElement('div')
spinr.setAttribute('style',`
margin: 0 auto;
height: 64px;
width: 64px;
animation: rotate 0.8s infinite linear;
border: 5px solid firebrick;
border-right-color: transparent;
border-radius: 50%;
`);
spinCont.appendChild(spinr);
addAnimation(`@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}`);
//Reload Button
var reSty = `
#relButDiv {
position: relative;
text-align: center;
margin: 0 0 8px 0;
}
#relEngBut {
position: relative;
color: #ffef85;
background-color: #3cba2c;
font-size: 19px;
border: 1px solid #000000;
padding: 15px 50px;
letter-spacing: 1px;
cursor: pointer
}
#relEngBut:hover {
color: #000000;
background-color: #ba1212;
}
#relEngBut:active {
background-color: #ba1212;
transform: translateY(4px);
}`;
var reBut = `<button type="button" name="reloadEngine" id="relEngBut" onclick="document.myFunctions.reloadChessEngine()">Reload Chess Engine</button>`;
tmpDiv = document.createElement('div');
var relButDiv = document.createElement('div');
relButDiv.id = 'relButDiv';
tmpDiv.innerHTML = reBut;
reBut = tmpDiv.firstChild;
tmpStyle = document.createElement('style');
tmpStyle.innerHTML = reSty;
document.head.append(tmpStyle);
relButDiv.append(reBut);
div.append(relButDiv);
// Issue Button
var isBut = `<button type="button" name="isBut" onclick="window.confirm('Do you wish to go to my issue page?') ? document.location = 'https://forms.gle/UbcnhTutTX4aCrs48' : console.log('cancled')">Got An Issue/Bug?</button>`;
tmpDiv = document.createElement('div');
var isButDiv = document.createElement('div');
isButDiv.style = `
position: relative;
text-align: center;
margin: 0 0 8px 0;
`;
tmpDiv.innerHTML = isBut;
isBut = tmpDiv.firstChild;
isBut.id = 'isBut';
isBut.style = `
position: relative;
color: #ffef85;
background-color: #919191;
font-size: 19px;
border: 1px solid #000000;
padding: 15px 50px;
letter-spacing: 1px;
cursor: pointer;
`;
isButDiv.append(isBut);
div.append(isButDiv);
loaded = true;
uiElementsLoaded = true; // Set flag after UI elements are created
myFunctions.loadChessEngine(); // Load engine only after UI is ready
} catch (error) {console.log(error)}
}
function other(delay){
var endTime = Date.now() + delay;
var timer = setInterval(()=>{
if(Date.now() >= endTime){
myFunctions.autoRun(lastValue);
canGo = true;
clearInterval(timer);
}
},10);
}
async function getVersion(){
var GF = new GreasyFork;
var code = await GF.get().script().code(460208);
var version = GF.parseScriptCodeMeta(code).filter(e => e.meta === '@version')[0].value;
if(currentVersion !== version){
while(true){
alert('UPDATE THIS SCRIPT IN ORDER TO PROCEED!');
}
}
}
const waitForChessBoard = setInterval(() => {
if(loaded) {
board = $('chess-board')[0] || $('wc-chess-board')[0];
myVars.autoRun = $('#autoRun')[0].checked;
myVars.autoMove = $('#autoMove')[0].checked;
let minDel = parseInt($('#timeDelayMin')[0].value);
let maxDel = parseInt($('#timeDelayMax')[0].value);
myVars.delay = Math.random() * (maxDel - minDel) + minDel;
myVars.isThinking = isThinking;
myFunctions.spinner();
if(board.game.getTurn() == board.game.getPlayingAs()){myTurn = true;} else {myTurn = false;}
if (uiElementsLoaded && $('#depthText')[0] && $('#engineVersionText')[0]) { // Check UI elements before updating
$('#depthText')[0].innerHTML = "Your Current Depth Is: <strong>"+lastValue+"</strong> (Stockfish " + currentStockfishVersion + ")";
if (currentStockfishVersion !== "None" && currentStockfishVersion !== "Failed") {
$('#engineVersionText')[0].innerHTML = "Chess Engine: <strong>" + currentStockfishVersion + " Loaded</strong>";
} else if (currentStockfishVersion === "Failed") {
$('#engineVersionText')[0].innerHTML = "<span style='color:red;'>Chess Engine: <strong>Failed to Load</strong></span>";
} else {
$('#engineVersionText')[0].innerHTML = "Chess Engine: <strong>Loading...</strong>";
}
}
} else {
myFunctions.loadEx();
}
if(!engine.engine && currentStockfishVersion !== "Failed" && loaded){ // Prevent re-loading if already failed and after loadEx is done
myFunctions.loadChessEngine();
}
if(myVars.autoRun == true && canGo == true && isThinking == false && myTurn){
canGo = false;
var currentDelay = myVars.delay != undefined ? myVars.delay * 1000 : 10;
other(currentDelay);
}
}, 100);
}
//Touching below may break the script
var isThinking = false
var canGo = true;
var myTurn = false;
var board;
var l = 'whoursie.com/4/5729456'; // This is likely related to the ad, and can be kept commented out if ads are removed.
window.addEventListener("load", (event) => {
let currentTime = Date.now();
main();
});