Chess.com Bot/Cheat (by Admin0)

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

Fra 27.04.2025. Se den seneste versjonen.

// 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();
});