// additional copyright/license info:
//© All Rights Reserved
//
//Chess.com Cheat/Bot © 2023 by Admin0
//
// ==UserScript==
// @name Chess.com Bot/Cheat (by Admin0)
// @namespace Admin0
// @version 2.2
// @description Chess.com Bot/Cheat that finds the best move!
// @author Admin0
// @license Chess.com Bot/Cheat © 2024 by Admin0, © 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
// @resource stockfish.js https://cdnjs.cloudflare.com/ajax/libs/stockfish.js/9.0.0/stockfish.js
// @require https://greasyfork.org/scripts/445697/code/index.js
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @run-at document-start
// ==/UserScript==
// SCRIPT // DO NOT CHANGE //
const currentVersion = '1'; // Sets the current version
function main() {
var stockfishObjectURL;
var engine = document.engine = {};
var myVars = document.myVars = {};
myVars.autoMovePiece = false;
myVars.autoRun = false;
myVars.autoMove = false;
myVars.autoMatch = false;
myVars.delay = 0.1;
myVars.customDepth = 11;
myVars.hasAutoMatched = false;
myVars.gameEnded = false;
myVars.isAttemptingAutoMatch = false;
// Load saved highlight color or set default
myVars.bestMoveHighlightColor = GM_getValue('bestMoveHighlightColor', '#EB6150'); // Default: rgb(235, 97, 80)
// Threat Highlighting vars - REMOVED
// myVars.enableThreatHighlighting = GM_getValue('enableThreatHighlighting', false);
// myVars.threatHighlightColor = GM_getValue('threatHighlightColor', '#FFDB58');
// myVars.lastOpponentMove = null;
var myFunctions = document.myFunctions = {};
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("[Color Fn Input]:", dat);
let bestmoveUCI = dat;
if (typeof dat === 'string' && dat.startsWith('bestmove ')) {
bestmoveUCI = dat.split(' ')[1];
}
console.log("[Color Fn Extracted UCI]:", bestmoveUCI);
// Call highlight FIRST
myFunctions.highlightMove(bestmoveUCI);
// If autoMove is enabled, move the piece after a short delay
if(myVars.autoMove == true){
console.log("[Auto Move] Scheduling move in 50ms...");
setTimeout(() => {
myFunctions.movePiece(bestmoveUCI.substring(0, 2), bestmoveUCI.substring(2, 4));
}, 50); // 50ms delay - adjust if needed
}
isThinking = false;
}
// Simplified movePiece function based on Script 2
myFunctions.movePiece = function(from, to){
const uciMove = from + to;
console.log("[Auto Move] Attempting move:", uciMove);
if (!board || !board.game) {
console.error("[Auto Move] Failed: Board or board.game not initialized!");
return;
}
try {
const legalMoves = board.game.getLegalMoves();
const foundMove = legalMoves.find(move => move.from === from && move.to === to);
if (foundMove) {
console.log("[Auto Move] Found legal move object:", foundMove);
// Determine promotion piece (usually queen 'q')
let promotion = undefined;
const piece = board.game.getPiece(from);
if (piece && piece.type === 'p' && (to[1] === '8' || to[1] === '1')) {
promotion = 'q'; // Default promotion to Queen
console.log("[Auto Move] Pawn promotion to Queen detected.");
}
board.game.move({
from: foundMove.from,
to: foundMove.to,
promotion: promotion, // Add promotion piece if needed
animate: false, // Set to false for faster moves, true for visual effect
userGenerated: true // Important to mimic user action
});
console.log("[Auto Move] Move executed via board.game.move:", uciMove);
} else {
console.warn("[Auto Move] Failed: No legal move found for UCI:", uciMove, "Available moves:", legalMoves);
// Fallback to highlighting if move fails
myFunctions.highlightMove(uciMove);
}
} catch (error) {
console.error("[Auto Move] Error during move execution:", error);
// Fallback to highlighting on error
myFunctions.highlightMove(uciMove);
}
}
function parser(e){
console.log("[Engine Raw]:", e.data); // Log raw engine output
if(e.data.includes('bestmove')){
console.log("[Engine Parsed Bestmove Line]:", e.data);
myFunctions.color(e.data.split(' ')[1]);
isThinking = false;
}
}
myFunctions.reloadChessEngine = function() {
console.log(`Reloading the chess engine!`);
engine.engine.terminate();
isThinking = false;
myFunctions.loadChessEngine();
}
myFunctions.loadChessEngine = function() {
if(!stockfishObjectURL) {
stockfishObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish.js')], {type: 'application/javascript'}));
}
console.log(stockfishObjectURL);
if(stockfishObjectURL) {
engine.engine = new Worker(stockfishObjectURL);
engine.engine.onmessage = e => {
parser(e);
};
engine.engine.onerror = e => {
console.log("Worker Error: "+e);
};
engine.engine.postMessage('ucinewgame');
}
console.log('loaded chess engine');
}
var lastValue = 11;
myFunctions.runChessEngine = function(depth){
var fen = board.game.getFEN();
console.log(`[Engine Send] Position FEN: ${fen}`); // Log FEN being sent
console.log(`[Engine Send] Go Depth: ${depth}`); // Log depth
engine.engine.postMessage(`position fen ${fen}`);
console.log('updated: ' + `position fen ${fen}`);
isThinking = true;
engine.engine.postMessage(`go depth ${depth}`);
lastValue = depth;
}
myFunctions.autoRun = function(){
if(board.game.getTurn() == board.game.getPlayingAs()){
myFunctions.runChessEngine(myVars.customDepth);
}
}
document.onkeydown = function(e) {
let depthToRun = -1;
switch (e.keyCode) {
case 81: depthToRun = 1; break;
case 87: depthToRun = 2; break;
case 69: depthToRun = 3; break;
case 82: depthToRun = 4; break;
case 84: depthToRun = 5; break;
case 89: depthToRun = 6; break;
case 85: depthToRun = 7; break;
case 73: depthToRun = 8; break;
case 79: depthToRun = 9; break;
case 80: depthToRun = 10; break;
case 65: depthToRun = 11; break;
case 83: depthToRun = 12; break;
case 68: depthToRun = 13; break;
case 70: depthToRun = 14; break;
case 71: depthToRun = 15; break;
case 72: depthToRun = 16; break;
case 74: depthToRun = 17; break;
case 75: depthToRun = 18; break;
case 76: depthToRun = 19; break;
case 90: depthToRun = 20; break;
case 88: depthToRun = 21; break;
case 67: depthToRun = 22; break;
case 86: depthToRun = 23; break;
case 66: depthToRun = 24; break;
case 78: depthToRun = 25; break;
case 77: depthToRun = 26; break;
case 187: depthToRun = 100; break; // '+' key
}
// Handle depth setting via keys Q-M and +
if (depthToRun !== -1) {
myVars.customDepth = depthToRun; // Update the stored depth
if (loaded) {
$('#customDepthInput').val(myVars.customDepth);
$('#depthText')[0].innerHTML = "Current Depth: <strong>" + myVars.customDepth + "</strong>";
}
myFunctions.runChessEngine(myVars.customDepth);
}
// Handle UI Toggle via ESC key
if (e.keyCode === 27) { // ESC key
if (loaded) {
const panel = $('#settingsContainer');
if (panel.length > 0) {
panel.toggle(); // Toggle visibility
console.log("Toggled UI panel visibility.");
}
}
}
};
myFunctions.spinner = function() {
if (loaded && $('#overlay').length > 0) {
$('#overlay').css('display', isThinking ? 'block' : 'none');
} else if (loaded) {
console.warn("Spinner overlay #overlay not found.");
}
}
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);
}
myFunctions.replaceAd = function(){
// Removed ad replacement logic
}
var loaded = false;
myFunctions.loadEx = function(){
if (loaded) return; // Prevent re-loading
try{
console.log("Loading UI panel (Step 1)... ");
board = $('chess-board')[0] || $('wc-chess-board')[0];
if (!board) {
console.warn("Cannot load UI - board element not found yet.");
return; // Don't proceed if board isn't there
}
myVars.board = board;
// --- Inject Styles ---
myFunctions.injectStyles();
// --- Create Panel ---
// Remove any old panel first (if reloading during testing)
$('#settingsContainer').remove();
// Create the main panel div
const $panel = $('<div>').attr('id', 'settingsContainer');
// --- Spinner ---
const $spinnerOverlay = $('<div>').attr('id', 'overlay').hide(); // Start hidden
const $spinner = $('<div>'); // The actual spinner div (styled by CSS)
$spinnerOverlay.append($spinner);
$panel.append($spinnerOverlay);
// --- Depth Section ---
$panel.append('<h3>Engine Depth</h3>');
const $depthDiv = $('<div>');
$depthDiv.append(
$('<span>').attr('id', 'depthText').html(`Current: <strong>${myVars.customDepth}</strong>`)
);
$depthDiv.append('<br>'); // Line break
$depthDiv.append($('<label>').attr('for', 'customDepthInput').text('Set Depth:'));
const $depthInput = $('<input>').attr({
type: 'number', id: 'customDepthInput', name: 'customDepthInput',
min: '1', max: '100', value: myVars.customDepth
});
$depthDiv.append($depthInput);
$panel.append($depthDiv);
// --- Game Options Section ---
$panel.append('<h3>Game Options</h3>');
const $optionsDiv = $('<div>');
// Auto Run
$optionsDiv.append(
$('<div>').addClass('options-line').append(
$('<input>').attr({ type: 'checkbox', id: 'autoRun', name: 'autoRun', checked: myVars.autoRun }),
$('<label>').attr('for', 'autoRun').addClass('inline').text('Enable auto run')
)
);
// Auto Move
$optionsDiv.append(
$('<div>').addClass('options-line').append(
$('<input>').attr({ type: 'checkbox', id: 'autoMove', name: 'autoMove', checked: myVars.autoMove }),
$('<label>').attr('for', 'autoMove').addClass('inline').text('Enable auto move')
)
);
// Auto Match
$optionsDiv.append(
$('<div>').addClass('options-line').append(
$('<input>').attr({ type: 'checkbox', id: 'autoMatch', name: 'autoMatch', checked: myVars.autoMatch }),
$('<label>').attr('for', 'autoMatch').addClass('inline').text('Enable auto match')
)
);
$panel.append($optionsDiv);
// --- Delay Section ---
$panel.append('<h3>Auto Run Delay</h3>');
const $delayDiv = $('<div>');
$delayDiv.append(
$('<div>').addClass('options-line').append( // Wrap in div for spacing
$('<label>').attr('for', 'timeDelayMin').text('Min (s):'),
$('<input>').attr({ type: 'number', id: 'timeDelayMin', name: 'timeDelayMin', min: '0.1', step: '0.1', value: GM_getValue('delayMin', 0.1) })
)
);
$delayDiv.append(
$('<div>').addClass('options-line').append(
$('<label>').attr('for', 'timeDelayMax').text('Max (s):'),
$('<input>').attr({ type: 'number', id: 'timeDelayMax', name: 'timeDelayMax', min: '0.1', step: '0.1', value: GM_getValue('delayMax', 1.0) })
)
);
$panel.append($delayDiv);
// --- Display Section ---
$panel.append('<h3>Display</h3>');
const $displayDiv = $('<div>').addClass('options-line'); // Use options-line for consistency
$displayDiv.append(
$('<label>').attr('for', 'highlightColorInput').text('Highlight Color:'),
$('<input>').attr({ type: 'color', id: 'highlightColorInput', name: 'highlightColorInput', value: myVars.bestMoveHighlightColor })
);
$panel.append($displayDiv);
// --- Buttons Section ---
$panel.append('<h3>Actions</h3>');
// Reload Button
const $reloadButton = $('<button>').attr({ type: 'button', name: 'reloadEngine', id: 'relEngBut' })
.text('Reload Chess Engine');
$panel.append($reloadButton);
// Issue Button
const $issueButton = $('<button>').attr({ type: 'button', name: 'isBut', id: 'isBut' })
.text('Report an issue?');
$panel.append($issueButton);
// Update Button
const $updateButton = $('<button>').attr({ type: 'button', id: 'updateScriptButton' })
.text('Check for Updates');
$panel.append($updateButton);
// --- Append Panel to Body ---
$('body').append($panel); // Append to body for fixed positioning
// --- Attach Depth Event Listener ---
$panel.find('#customDepthInput').on('change', function() {
const newDepth = parseInt($(this).val(), 10);
if (!isNaN(newDepth) && newDepth >= 1 && newDepth <= 100) {
myVars.customDepth = newDepth;
$panel.find('#depthText').html(`Current: <strong>${myVars.customDepth}</strong>`);
} else {
$(this).val(myVars.customDepth); // Reset invalid input
}
});
// --- Attach Checkbox Event Listeners ---
$panel.find('#autoRun').on('change', function() { myVars.autoRun = $(this).is(':checked'); console.log(`[Event] autoRun changed to: ${myVars.autoRun}`); });
$panel.find('#autoMove').on('change', function() { myVars.autoMove = $(this).is(':checked'); console.log(`[Event] autoMove changed to: ${myVars.autoMove}`); });
$panel.find('#autoMatch').on('change', function() { myVars.autoMatch = $(this).is(':checked'); console.log(`[Event] autoMatch changed to: ${myVars.autoMatch}`); });
// --- Attach Delay Input Event Listeners ---
$panel.find('#timeDelayMin').on('change', function() {
let minVal = parseFloat($(this).val());
let maxVal = parseFloat($panel.find('#timeDelayMax').val());
if (isNaN(minVal) || minVal < 0.1) minVal = 0.1;
if (minVal > maxVal) minVal = maxVal; // Prevent min > max
$(this).val(minVal.toFixed(1)); // Update potentially corrected value
GM_setValue('delayMin', minVal);
myVars.delayMin = minVal; // Update runtime var
console.log(`[Event] Delay Min changed to: ${minVal}`);
});
$panel.find('#timeDelayMax').on('change', function() {
let maxVal = parseFloat($(this).val());
let minVal = parseFloat($panel.find('#timeDelayMin').val());
if (isNaN(maxVal) || maxVal < 0.1) maxVal = 0.1;
if (maxVal < minVal) maxVal = minVal; // Prevent max < min
$(this).val(maxVal.toFixed(1)); // Update potentially corrected value
GM_setValue('delayMax', maxVal);
myVars.delayMax = maxVal; // Update runtime var
console.log(`[Event] Delay Max changed to: ${maxVal}`);
});
// --- Attach Color Picker Event Listener ---
$panel.find('#highlightColorInput').on('change', function() {
myVars.bestMoveHighlightColor = $(this).val();
GM_setValue('bestMoveHighlightColor', myVars.bestMoveHighlightColor);
console.log(`[Event] Highlight color changed to: ${myVars.bestMoveHighlightColor}`);
});
// --- Attach Button Event Listeners ---
$panel.find('#relEngBut').on('click', myFunctions.reloadChessEngine);
$panel.find('#isBut').on('click', () => {
const feedbackURL = 'https://greasyfork.org/en/scripts/534105-chess-com-bot-cheat-by-admin0/feedback';
window.open(feedbackURL, '_blank');
console.log('Opened script feedback page:', feedbackURL);
});
$panel.find('#updateScriptButton').on('click', () => {
const scriptPageURL = 'https://greasyfork.org/en/scripts/534105-chess-com-bot-cheat-by-admin0'; // Your script's page
window.open(scriptPageURL, '_blank');
console.log('Opened script page for update check:', scriptPageURL);
});
// Initialize runtime delay vars from saved values
myVars.delayMin = parseFloat($panel.find('#timeDelayMin').val());
myVars.delayMax = parseFloat($panel.find('#timeDelayMax').val());
loaded = true;
console.log("UI Panel Created Successfully (Step 1).");
} catch (error) {
console.error("Error loading UI (Step 1):", error);
loaded = false; // Ensure it tries again if error occurs
}
}
// Function to inject CSS styles for the UI panel
myFunctions.injectStyles = function() {
// Basic panel styles - positioning and colors
const css = `
#settingsContainer {
position: fixed;
right: 10px;
top: 50%;
transform: translateY(-50%);
width: 260px; /* Back to standard width */
max-height: 85vh;
overflow-y: auto;
background-color: #312e2b; /* Standard Chess.com dark bg */
color: #cccccc; /* Standard light text */
border: 1px solid #444; /* Standard border */
border-radius: 6px; /* Standard radius */
padding: 10px 15px; /* Standard padding */
font-family: "Noto Sans", sans-serif;
font-size: 14px;
z-index: 9999;
box-shadow: 0 4px 12px rgba(0,0,0,0.5); /* Standard shadow */
user-select: none;
transition: box-shadow 0.3s ease;
}
#settingsContainer:hover {
box-shadow: 0 6px 18px rgba(0,0,0,0.6); /* Standard subtle hover */
/* Remove scale effect */
}
/* Spinner adjustments */
#overlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-color: rgba(49, 46, 43, 0.75); /* Match bg */
z-index: 10000;
display: none;
border-radius: 6px; /* Match panel */
}
#overlay > div {
position: absolute;
top: 50%; left: 50%;
height: 40px; width: 40px;
margin-top: -20px; margin-left: -20px;
border: 4px solid #888; /* Standard grey spinner */
border-right-color: transparent;
border-radius: 50%;
animation: rotate 0.8s infinite linear;
}
/* Section Headings */
#settingsContainer h3 {
color: #ededed; /* Standard heading color */
margin-top: 15px;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid #444; /* Standard separator */
font-size: 16px;
font-weight: bold;
letter-spacing: normal; /* Standard spacing */
}
#settingsContainer h3:first-of-type {
margin-top: 0;
}
/* Input Styling */
#settingsContainer label {
display: block;
margin-bottom: 3px;
color: #cccccc; /* Standard label color */
cursor: default;
}
#settingsContainer input[type="number"],
#settingsContainer input[type="color"] {
background-color: #444; /* Standard input bg */
border: 1px solid #555; /* Standard input border */
color: #ccc;
padding: 5px;
border-radius: 3px;
margin-left: 5px;
vertical-align: middle;
}
#settingsContainer input[type="number"] {
width: 55px;
}
#settingsContainer input[type="color"] {
width: 40px;
height: 25px;
padding: 1px;
cursor: pointer;
border-color: #555; /* Standard border */
}
/* Checkbox/Inline Label Styling */
#settingsContainer label.inline {
display: inline-block;
margin-left: 5px;
margin-bottom: 0;
vertical-align: middle;
font-weight: normal;
color: #cccccc; /* Standard text color */
cursor: pointer;
}
#settingsContainer input[type="checkbox"] {
margin-right: 5px;
vertical-align: middle;
cursor: pointer;
/* Revert to default checkbox appearance for better theme consistency */
appearance: checkbox;
background-color: initial;
border: initial;
width: auto;
height: auto;
border-radius: initial;
position: relative;
top: 0; /* Reset alignment */
}
/* Remove custom checked styles */
/* #settingsContainer input[type="checkbox"]:checked { ... } */
/* #settingsContainer input[type="checkbox"]:checked::after { ... } */
#settingsContainer div.options-line {
margin-bottom: 8px;
padding: 4px 6px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
#settingsContainer div.options-line:hover {
background-color: #3c3936; /* Subtle hover */
cursor: default;
}
/* Button Styling */
#settingsContainer button {
display: block;
width: 100%;
box-sizing: border-box;
text-align: center;
background-color: #81b64c; /* Adjusted Chess.com green */
color: white;
border: none;
border-radius: 4px;
padding: 8px 10px;
margin-top: 10px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
text-shadow: none;
transition: background-color 0.2s ease;
}
#settingsContainer button:hover {
background-color: #93cc5b; /* Lighter green for hover */
box-shadow: none;
transform: none;
}
#settingsContainer button:active {
transform: none;
box-shadow: none;
background-color: #70a140; /* Darker green for active */
}
#settingsContainer button#isBut { /* Secondary Button */
background-color: #555;
margin-top: 5px;
}
#settingsContainer button#isBut:hover {
background-color: #666;
box-shadow: none;
transform: none;
}
#settingsContainer button#isBut:active {
transform: none;
box-shadow: none;
background-color: #4a4a4a;
}
`;
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = css;
document.head.appendChild(styleSheet);
console.log("Injected basic bot UI styles.");
}
function other(delay){
var endTime = Date.now() + delay;
var timer = setInterval(()=>{
if(Date.now() >= endTime){
myFunctions.autoRun();
canGo = true;
clearInterval(timer);
}
},10);
}
async function getVersion(){
var GF = new GreasyFork; // set upping api
var code = await GF.get().script().code(460208); // Get code
var version = GF.parseScriptCodeMeta(code).filter(e => e.meta === '@version')[0].value; // filtering array and getting value of @version
if(currentVersion !== version){
while(true){
alert('UPDATE THIS SCRIPT IN ORDER TO PROCEED!');
}
}
}
//Removed due to script being reported. I tried to make it so people can know when bug fixes come out. Clearly people don't like that.
//getVersion();
const waitForChessBoard = setInterval(() => {
if(loaded) {
board = $('chess-board')[0] || $('wc-chess-board')[0];
// Read delay values from myVars (set by event listeners or initial load)
let minDel = myVars.delayMin || 0.1;
let maxDel = myVars.delayMax || 1.0;
// No need to check min > max here, listener handles it
// Recalculate random delay for this interval check
myVars.delay = Math.random() * (maxDel - minDel) + minDel;
myVars.isThinking = isThinking;
myFunctions.spinner();
// Check if game has ended by looking for the game over modal
const gameOverModal = $('.game-over-modal-content');
if (gameOverModal.length > 0 && !myVars.gameEnded) {
console.log("Game over detected.");
myVars.gameEnded = true;
myVars.hasAutoMatched = false; // Reset auto-match flag for the new game opportunity
}
// Determine whose turn it is, ONLY if the game hasn't ended
if (!myVars.gameEnded && board && board.game) { // Check !myVars.gameEnded here
try {
myTurn = (board.game.getTurn() == board.game.getPlayingAs());
} catch (e) {
console.warn("[Turn Check] Error getting turn/playingAs:", e);
myTurn = false;
}
} else {
myTurn = false; // Not my turn if game ended or board not ready
}
} else if ($('chess-board, wc-chess-board').length > 0 && !$('#settingsContainer').length) { // Try to load if board exists and panel doesn't
myFunctions.loadEx(); // Try loading UI if not loaded
}
// --- Logic Execution Section ---
// Log current state before checks
if (loaded && board && board.game) { // Only log if things are loaded
console.log(`[State Check] AutoRun:${myVars.autoRun} AutoMove:${myVars.autoMove} AutoMatch:${myVars.autoMatch} MyTurn:${myTurn} GameEnded:${myVars.gameEnded} Thinking:${isThinking} CanGo:${canGo} HasAutoMatched:${myVars.hasAutoMatched}`);
try {
// Also log board.game state if possible
console.log(`[Board Check] Turn: ${board.game.getTurn()}, PlayingAs: ${board.game.getPlayingAs()}, FEN: ${board.game.getFEN()}`);
// Add specific turn/playingAs log
console.log(`[Board Check] getPlayingAs(): ${board.game.getPlayingAs()}, getTurn(): ${board.game.getTurn()}`);
} catch (e) {
console.warn("[Board Check] Error getting board.game details:", e);
}
}
// Auto Run Logic
if(myVars.autoRun == true && canGo == true && isThinking == false && myTurn && !myVars.gameEnded){
console.log("[Action] Triggering Auto Run..."); // Log trigger
canGo = false;
var currentDelay = myVars.delay != undefined ? myVars.delay * 1000 : 10;
other(currentDelay); // Call other without depth parameter
}
// Auto Match Logic
if (myVars.autoMatch && myVars.gameEnded && !myVars.hasAutoMatched && !myVars.isAttemptingAutoMatch) {
console.log("[Action] Triggering Auto Match Sequence..."); // Updated log
myFunctions.startNewGame();
// Note: startNewGame now sets flags and manages its own state
}
}, 100); // Interval runs every 100ms
myFunctions.startNewGame = function() {
console.log("Starting new game..."); // Log from Script 2 version
const modalNewGameButton = $('.game-over-modal-content .game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])');
if (modalNewGameButton.length) {
modalNewGameButton[0].click();
console.log("Clicked New <x> min button from game-over modal.");
myVars.hasAutoMatched = true;
myVars.gameEnded = false; // Reset flag
myVars.isAttemptingAutoMatch = false; // Reset flag
return;
}
const newGameButton = $('.game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])');
if (newGameButton.length) {
newGameButton[0].click();
console.log("Clicked New <x> min button from game-over.");
myVars.hasAutoMatched = true;
myVars.gameEnded = false; // Reset flag
myVars.isAttemptingAutoMatch = false; // Reset flag
return;
}
const guestButton = $('#guest-button.authentication-intro-guest');
if (guestButton.length) {
guestButton[0].click();
console.log("Clicked Play as Guest.");
setTimeout(() => {
const playButton = $('.cc-button-component.cc-button-primary.cc-button-xx-large.cc-button-full');
if (playButton.length) {
playButton[0].click();
console.log("Clicked Play button after guest prompt.");
myVars.hasAutoMatched = true;
myVars.gameEnded = false; // Reset flag
myVars.isAttemptingAutoMatch = false; // Reset flag
} else {
console.error("Play button not found after guest prompt!");
myVars.isAttemptingAutoMatch = false; // Reset flag even on failure
}
}, 500);
return; // Return after starting timeout
}
const newGameTab = $('[data-tab="newGame"]');
if (newGameTab.length) {
newGameTab[0].click();
console.log("Clicked New Game tab.");
setTimeout(() => {
const playButton = $('.cc-button-component.cc-button-primary.cc-button-xx-large.cc-button-full');
if (playButton.length) {
playButton[0].click();
console.log("Clicked Play button.");
myVars.hasAutoMatched = true;
myVars.gameEnded = false; // Reset flag
myVars.isAttemptingAutoMatch = false; // Reset flag
} else {
console.error("Play button not found after clicking New Game tab!");
myVars.isAttemptingAutoMatch = false; // Reset flag even on failure
}
}, 500);
return; // Return after starting timeout
}
// If no buttons were found and clicked
console.error("No suitable New Game button/tab found by Script 2 logic.");
myVars.isAttemptingAutoMatch = false; // Reset flag if nothing found
};
myFunctions.highlightMove = function(bestmoveUCI) {
// Remove previous highlights first
$('.highlight.bro').remove();
console.log(`[Highlight] Received UCI: ${bestmoveUCI}, Length: ${bestmoveUCI?.length}`); // Log input
if (!bestmoveUCI || bestmoveUCI.length < 4) {
console.error("[Highlight] Invalid UCI string received:", bestmoveUCI);
return; // Don't proceed if UCI is bad
}
let res1 = bestmoveUCI.substring(0, 2);
let res2 = bestmoveUCI.substring(2, 4);
const highlightColor = myVars.bestMoveHighlightColor || '#EB6150';
// --- Add coordinate translation --- START
function translateCoords(uciSquare) {
return uciSquare
.replace(/^a/, "1")
.replace(/^b/, "2")
.replace(/^c/, "3")
.replace(/^d/, "4")
.replace(/^e/, "5")
.replace(/^f/, "6")
.replace(/^g/, "7")
.replace(/^h/, "8");
}
const translatedSq1 = translateCoords(res1);
const translatedSq2 = translateCoords(res2);
console.log(`[Highlight] Translated: ${res1} -> ${translatedSq1}, ${res2} -> ${translatedSq2}`);
// --- Add coordinate translation --- END
try {
console.log(`[Highlight] Highlighting TO square: ${translatedSq2}`);
$(board.nodeName) // Use board.nodeName to be safe
.prepend(`<div class="highlight square-${translatedSq2} bro" style="background-color: ${highlightColor}; opacity: 0.71; pointer-events: none;" data-test-element="highlight"></div>`)
.children(':first')
.delay(1800)
.queue(function() { $(this).remove(); });
} catch (e) {
console.error(`[Highlight] Error highlighting TO square ${translatedSq2}:`, e);
}
try {
console.log(`[Highlight] Highlighting FROM square: ${translatedSq1}`);
$(board.nodeName)
.prepend(`<div class="highlight square-${translatedSq1} bro" style="background-color: ${highlightColor}; opacity: 0.71; pointer-events: none;" data-test-element="highlight"></div>`)
.children(':first')
.delay(1800)
.queue(function() { $(this).remove(); });
} catch (e) {
console.error(`[Highlight] Error highlighting FROM square ${translatedSq1}:`, e);
}
console.log("[Highlight] Finished highlighting.");
}
myFunctions.highlightThreats = function() {
// Placeholder for the removed highlightThreats function
};
}
//Touching below may break the script
var isThinking = false
var canGo = true;
var myTurn = false;
var board;
window.addEventListener("load", (event) => {
main();
});