Chess.com Bot/Cheat (by Admin0)

Chess.com Bot/Cheat that finds the best move!

As of 2025-04-26. See the latest version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// 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.1
// @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://greatest.deepsurf.us/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', () => { window.confirm('Report an issue? (External link)') ? document.location = 'mailto:[email protected]' : console.log('Issue report canceled.'); });
             $panel.find('#updateScriptButton').on('click', () => {
                 const scriptPageURL = 'https://greatest.deepsurf.us/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();
});