Chess.com Bot/Cheat (by Admin0)

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

// ==UserScript==
// @name         Chess.com Bot/Cheat (by Admin0)
// @namespace    Admin0
// @version      1.1.0
// @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==

// CLEAN REWRITE - CHESS.COM THEMED VERSION 3.0

const currentVersion = '3.0';

function main() {
    // Core variables setup
    var stockfishObjectURL;
    var engine = document.engine = {};
    var myVars = document.myVars = {};
    myVars.autoMove = GM_getValue('autoMove', false); // Load saved autoMove
    myVars.suggestMove = GM_getValue('suggestMove', false); // Load saved suggestMove
    myVars.autoMatch = GM_getValue('autoMatch', false); // Load saved autoMatch
    myVars.customDepth = GM_getValue('customDepth', 11); // Load saved depth, default to 11
    myVars.bestMoveHighlightColor = GM_getValue('bestMoveHighlightColor', '#7fa650'); // Chess.com green
    var myFunctions = document.myFunctions = {};
    var lastValue = GM_getValue('customDepth', 11); // Also initialize lastValue with saved/default depth

    // Core chess engine logic from Script3 (the working one)
    myFunctions.color = function(dat) {
        console.log("[Move] Processing:", dat);
        console.log("[Highlight] Attempting highlight on board element:", board ? board.nodeName : 'Board not found!'); // Diagnostic log
        let response = dat;
        let res1 = response.substring(0, 2); // From square
        let res2 = response.substring(2, 4); // To square

        // Execute move if auto-move is enabled
        if (myVars.autoMove === true) {
            myFunctions.movePiece(res1, res2);
        }

        // Reset thinking state
        isThinking = false;

        // Convert chess notation to Chess.com's grid system
        res1 = res1.replace(/^a/, "1")
            .replace(/^b/, "2")
            .replace(/^c/, "3")
            .replace(/^d/, "4")
            .replace(/^e/, "5")
            .replace(/^f/, "6")
            .replace(/^g/, "7")
            .replace(/^h/, "8");
        res2 = res2.replace(/^a/, "1")
            .replace(/^b/, "2")
            .replace(/^c/, "3")
            .replace(/^d/, "4")
            .replace(/^e/, "5")
            .replace(/^f/, "6")
            .replace(/^g/, "7")
            .replace(/^h/, "8");

        // Highlight destination square
        $(board.nodeName)
            .prepend('<div class="highlight square-' + res2 + ' bro" style="background-color: ' + myVars.bestMoveHighlightColor + '; opacity: 0.7;" data-test-element="highlight"></div>')
            .children(':first')
            .delay(1800)
            .queue(function() {
                $(this).remove();
            });

        // Highlight origin square
        $(board.nodeName)
            .prepend('<div class="highlight square-' + res1 + ' bro" style="background-color: ' + myVars.bestMoveHighlightColor + '; opacity: 0.7;" data-test-element="highlight"></div>')
            .children(':first')
            .delay(1800)
            .queue(function() {
                $(this).remove();
            });
    };

    // Move execution function from Script3
    myFunctions.movePiece = function(from, to) {
        console.log("[Move] Executing move from", from, "to", to);
        try {
            // Get legal moves from the game
            let legalMoves = board.game.getLegalMoves();

            // Find our move in legal moves
            for (let each = 0; each < legalMoves.length; each++) {
                if (legalMoves[each].from === from && legalMoves[each].to === to) {
                    let move = legalMoves[each];

                    // Check for promotion (pawn to last rank)
                let promotion = undefined;
                    let piece = board.game.getPiece(from);
                if (piece && piece.type === 'p' && (to[1] === '8' || to[1] === '1')) {
                        promotion = 'q'; // Default promote to queen
                        console.log("[Move] Promoting pawn to queen");
                }

                    // Execute the move
                    board.game.move({
                        from: move.from,
                        to: move.to,
                        promotion: promotion,
                        animate: false,
                        userGenerated: true
                    });
                    console.log("[Move] Executed successfully");
                    return;
                }
            }
            console.warn("[Move] No legal move found:", from, to);
        } catch (error) {
            console.error("[Move] Error executing move:", error);
        }
    };

    // Parser from Script3
    function parser(e) {
        console.log("[Engine] Message:", e.data);
        if (e.data.includes('bestmove')) {
            console.log("[Engine] Found best move:", e.data);
            myFunctions.color(e.data.split(' ')[1]);
            isThinking = false;
        }
    }

    // Engine load function from Script3
    myFunctions.loadChessEngine = function() {
        console.log("[Engine] Loading Stockfish...");
        try {
            if (!stockfishObjectURL) {
            stockfishObjectURL = URL.createObjectURL(new Blob([GM_getResourceText('stockfish.js')], {type: 'application/javascript'}));
        }

            engine.engine = new Worker(stockfishObjectURL);

            engine.engine.onmessage = e => {
                parser(e);
            };

            engine.engine.onerror = e => {
                console.error("[Engine] Error:", e);
                isThinking = false;
            };

            engine.engine.postMessage('ucinewgame');
            console.log("[Engine] Loaded successfully");
        } catch (error) {
            console.error("[Engine] Load failed:", error);
        }
    };

    // Engine reload function
    myFunctions.reloadChessEngine = function() {
        console.log("[Engine] Reloading...");
        try {
            if (engine.engine) {
                engine.engine.terminate();
            }
            isThinking = false;
            stockfishObjectURL = null; // Force recreation
            setTimeout(() => {
                myFunctions.loadChessEngine();
                console.log("[Engine] Reloaded successfully");
            }, 100);
        } catch (error) {
            console.error("[Engine] Reload failed:", error);
        }
    };

    // Run engine at specified depth
    myFunctions.runChessEngine = function(depth) {
        console.log("[Engine] Running at depth", depth);
        let fen = board.game.getFEN();
        console.log("[Engine] Position:", fen);

        engine.engine.postMessage(`position fen ${fen}`);
        isThinking = true;
        engine.engine.postMessage(`go depth ${depth}`);
        lastValue = depth;
    };

    // Auto run function
    myFunctions.autoRun = function(depth) {
        if (board.game.getTurn() == board.game.getPlayingAs()) {
            myFunctions.runChessEngine(depth || myVars.customDepth);
        }
    };

    // Function to handle delayed auto-run
    function other(delay) {
        let endTime = Date.now() + delay;
        let timer = setInterval(() => {
            if (Date.now() >= endTime) {
                myFunctions.autoRun(myVars.customDepth);
                canGo = true;
                clearInterval(timer);
            }
        }, 10);
    }

    // Auto start new game
    myFunctions.startNewGame = function() {
        console.log("[Match] Starting new game...");

        // Find New Game button in different places
        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("[Match] Clicked New Game from modal");
            myVars.hasAutoMatched = true;
            myVars.gameEnded = false;
            return;
        }

        const newGameButton = $('.game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])');
        if (newGameButton.length) {
            newGameButton[0].click();
            console.log("[Match] Clicked New Game button");
            myVars.hasAutoMatched = true;
            myVars.gameEnded = false;
            return;
        }

        console.log("[Match] No New Game button found");
    };

    // Function to handle spinner visibility
    myFunctions.spinner = function() {
        if (loaded && $('#overlay').length) {
            $('#overlay').css('display', isThinking ? 'block' : 'none');
        }
    };

    // Handle keyboard shortcuts
    document.onkeydown = function(e) {
        const depthKeys = {
            81: 1, 87: 2, 69: 3, 82: 4, 84: 5, 89: 6, 85: 7, 73: 8, 79: 9, 80: 10,
            65: 11, 83: 12, 68: 13, 70: 14, 71: 15, 72: 16, 74: 17, 75: 18, 76: 19,
            90: 20, 88: 21, 67: 22, 86: 23, 66: 24, 78: 25, 77: 26, 187: 100
        };

        if (depthKeys[e.keyCode]) {
            myVars.customDepth = depthKeys[e.keyCode];
            if (loaded) {
                $('#depthValue').text(myVars.customDepth);
            }
            GM_setValue('customDepth', myVars.customDepth); // Save the new depth
            location.reload(); // Reload the page
        }

        // Toggle UI with ESC
        if (e.keyCode === 27 && loaded) {
            $('#chessBot').toggle();
        }
    };

    // UI Creation function - CHESS.COM THEMED
    var loaded = false;
    myFunctions.loadEx = function() {
        if (loaded) return;

        try {
            console.log("[UI] Creating Chess.com themed interface...");
            board = $('chess-board')[0] || $('wc-chess-board')[0];
             if (!board) {
                console.warn("[UI] Board not found yet");
                return;
             }

            myVars.board = board;

            // Create main container with Chess.com styling
            const panel = document.createElement('div');
            panel.id = 'chessBot';
            panel.style = `
    position: fixed;
                right: 20px;
    top: 50%;
    transform: translateY(-50%);
                width: 280px;
                background-color: #312e2b;
                color: #bababa;
                font-family: "Segoe UI", Arial, sans-serif;
    z-index: 9999;
                padding: 15px;
                border-radius: 5px;
                box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
                font-size: 14px;
            `;

            // Create header
            const header = document.createElement('div');
            header.style = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 15px;
                border-bottom: 1px solid #464442;
                padding-bottom: 10px;
            `;

            const title = document.createElement('h2');
            title.innerText = 'Chess.com Bot';
            title.style = `
                margin: 0;
                font-size: 18px;
                font-weight: 600;
                color: #bababa;
            `;

            const version = document.createElement('span');
            version.innerText = 'v3.0';
            version.style = `
                font-size: 12px;
                opacity: 0.8;
                color: #bababa;
            `;

            header.appendChild(title);
            header.appendChild(version);
            panel.appendChild(header);

            // Create spinner overlay
            const overlay = document.createElement('div');
            overlay.id = 'overlay';
            overlay.style = `
    position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: rgba(49, 46, 43, 0.85);
    z-index: 10000;
    display: none;
                border-radius: 5px;
            `;

            const spinner = document.createElement('div');
            spinner.style = `
    position: absolute;
                top: 50%;
                left: 50%;
                width: 40px;
                height: 40px;
                margin-top: -20px;
                margin-left: -20px;
                border: 3px solid #bababa;
                border-top-color: #7fa650;
    border-radius: 50%;
                animation: spin 1s infinite linear;
            `;

            const spinStyle = document.createElement('style');
            spinStyle.textContent = `
                @keyframes spin {
                    0% { transform: rotate(0deg); }
                    100% { transform: rotate(360deg); }
                }
            `;

            document.head.appendChild(spinStyle);
            overlay.appendChild(spinner);
            panel.appendChild(overlay);

            // Create content sections
            const createSection = (title) => {
                const section = document.createElement('div');
                section.style = `
                    margin-bottom: 15px;
                `;

                const sectionTitle = document.createElement('h3');
                sectionTitle.innerText = title;
                sectionTitle.style = `
                    margin: 0 0 10px 0;
                    font-size: 16px;
                    color: #bababa;
                    border-bottom: 1px solid #464442;
                    padding-bottom: 5px;
                `;

                section.appendChild(sectionTitle);
                return section;
            };

            // Create depth section
            const depthSection = createSection('Engine Depth');

            const depthDisplay = document.createElement('div');
            depthDisplay.style = `
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 10px;
                background-color: #3a3634;
                padding: 8px 12px;
                border-radius: 4px;
            `;

            const depthLabel = document.createElement('span');
            depthLabel.innerText = 'Current Depth:';

            const depthValue = document.createElement('span');
            depthValue.id = 'depthValue';
            depthValue.innerText = myVars.customDepth;
            depthValue.style = `
                font-weight: bold;
                color: #7fa650;
            `;

            depthDisplay.appendChild(depthLabel);
            depthDisplay.appendChild(depthValue);

            const depthInfo = document.createElement('p');
            depthInfo.innerText = 'Press A-Z keys to change depth (Q=1, W=2, etc.)';
            depthInfo.style = `
                margin: 5px 0;
                font-size: 12px;
                opacity: 0.7;
            `;

            const depthInput = document.createElement('div');
            depthInput.style = `
                display: flex;
                align-items: center;
                margin-top: 10px;
            `;

            const depthInputLabel = document.createElement('label');
            depthInputLabel.innerText = 'Set Depth:';
            depthInputLabel.style = 'margin-right: 10px;';

            const depthInputField = document.createElement('input');
            depthInputField.type = 'number';
            depthInputField.id = 'customDepthInput';
            depthInputField.min = '1';
            depthInputField.max = '100';
            depthInputField.value = myVars.customDepth;
            depthInputField.style = `
                background-color: #3a3634;
                border: 1px solid #464442;
                color: #bababa;
                padding: 5px;
                border-radius: 3px;
                width: 60px;
            `;

            depthInputField.addEventListener('change', function() {
                const value = parseInt(this.value);
                if (!isNaN(value) && value >= 1 && value <= 100) {
                    myVars.customDepth = value;
                    depthValue.innerText = value;
                    GM_setValue('customDepth', myVars.customDepth); // Save the new depth
                    location.reload(); // Reload the page
                } else {
                    this.value = GM_getValue('customDepth', 11); // Reset to saved value if input is invalid
                }
            });

            depthInput.appendChild(depthInputLabel);
            depthInput.appendChild(depthInputField);

            depthSection.appendChild(depthDisplay);
            depthSection.appendChild(depthInfo);
            depthSection.appendChild(depthInput);
            panel.appendChild(depthSection);

            // Create game options section
            const optionsSection = createSection('Game Options');

            const createCheckbox = (id, label) => {
                const container = document.createElement('div');
                container.style = `
                    display: flex;
                    align-items: center;
                    margin-bottom: 10px;
                    cursor: pointer;
                `;

                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.id = id;
                checkbox.style = `
                    margin-right: 10px;
                    cursor: pointer;
                `;

                const checkLabel = document.createElement('label');
                checkLabel.htmlFor = id;
                checkLabel.innerText = label;
                checkLabel.style = 'cursor: pointer;';

                container.appendChild(checkbox);
                container.appendChild(checkLabel);
                return container;
            };

            const autoRunCheck = createCheckbox('suggestMove', 'Enable Suggested Move');
            const autoMoveCheck = createCheckbox('autoMove', 'Enable auto move');
            const autoMatchCheck = createCheckbox('autoMatch', 'Enable auto match');

            optionsSection.appendChild(autoRunCheck);
            optionsSection.appendChild(autoMoveCheck);
            optionsSection.appendChild(autoMatchCheck);
            panel.appendChild(optionsSection);

            // Set initial state from loaded vars
            autoRunCheck.querySelector('input').checked = myVars.suggestMove;
            autoMoveCheck.querySelector('input').checked = myVars.autoMove;
            autoMatchCheck.querySelector('input').checked = myVars.autoMatch;

            // Create delay section
            const delaySection = createSection('Suggestion Delay');

            const createDelayInput = (id, label, defaultValue) => {
                const container = document.createElement('div');
                container.style = `
                    display: flex;
                    align-items: center;
                    margin-bottom: 10px;
                `;

                const inputLabel = document.createElement('label');
                inputLabel.htmlFor = id;
                inputLabel.innerText = label;
                inputLabel.style = `
                    flex: 1;
                `;

                const input = document.createElement('input');
                input.type = 'number';
                input.id = id;
                input.min = '0.1';
                input.step = '0.1';
                input.value = defaultValue;
                input.style = `
                    background-color: #3a3634;
                    border: 1px solid #464442;
                    color: #bababa;
                    padding: 5px;
                    border-radius: 3px;
                    width: 60px;
                `;

                container.appendChild(inputLabel);
                container.appendChild(input);
                return container;
            };

            const minDelayInput = createDelayInput('timeDelayMin', 'Min Delay (s):', '0.1');
            const maxDelayInput = createDelayInput('timeDelayMax', 'Max Delay (s):', '1.0');

            delaySection.appendChild(minDelayInput);
            delaySection.appendChild(maxDelayInput);
            panel.appendChild(delaySection);

            // Create display section
            const displaySection = createSection('Display');

            const colorContainer = document.createElement('div');
            colorContainer.style = `
                display: flex;
                align-items: center;
            `;

            const colorLabel = document.createElement('label');
            colorLabel.htmlFor = 'highlightColorInput';
            colorLabel.innerText = 'Highlight Color:';
            colorLabel.style = 'flex: 1;';

            const colorInput = document.createElement('input');
            colorInput.type = 'color';
            colorInput.id = 'highlightColorInput';
            colorInput.value = myVars.bestMoveHighlightColor;
            colorInput.style = `
                width: 60px;
                height: 30px;
                padding: 0;
                border: none;
                background: none;
            `;

            colorInput.addEventListener('change', function() {
                myVars.bestMoveHighlightColor = this.value;
                GM_setValue('bestMoveHighlightColor', this.value);
            });

            colorContainer.appendChild(colorLabel);
            colorContainer.appendChild(colorInput);
            displaySection.appendChild(colorContainer);
            panel.appendChild(displaySection);

            // Create buttons section
            const actionsSection = createSection('Actions');

            const createButton = (text, onClick, primary = false) => {
                const button = document.createElement('button');
                button.innerText = text;
                button.addEventListener('click', onClick);
                button.style = `
                    background-color: ${primary ? '#7fa650' : '#5d5955'};
                    color: #fff;
                    border: none;
                    padding: 10px;
                    margin-bottom: 10px;
                    border-radius: 3px;
                    width: 100%;
                    cursor: pointer;
                    font-weight: ${primary ? 'bold' : 'normal'};
                    transition: background-color 0.2s;
                `;

                button.addEventListener('mouseover', function() {
                    this.style.backgroundColor = primary ? '#8fb761' : '#6e6a66';
                });

                button.addEventListener('mouseout', function() {
                    this.style.backgroundColor = primary ? '#7fa650' : '#5d5955';
                });

                return button;
            };

            const reloadButton = createButton('Reload Chess Engine', () => myFunctions.reloadChessEngine(), true);
            const issueButton = createButton('Report an Issue', () => {
                window.open('https://greasyfork.org/en/scripts/534105-chess-com-bot-cheat-by-admin0/feedback', '_blank');
            });

            const updateButton = createButton('Check for Updates', () => {
                window.open('https://greasyfork.org/en/scripts/534105-chess-com-bot-cheat-by-admin0', '_blank');
            });

            actionsSection.appendChild(reloadButton);
            actionsSection.appendChild(issueButton);
            actionsSection.appendChild(updateButton);
            panel.appendChild(actionsSection);

            // Add drag handle
            const dragHandle = document.createElement('div');
            dragHandle.style = `
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                height: 15px;
                cursor: move;
                background-color: rgba(255, 255, 255, 0.05);
                border-top-left-radius: 5px;
                border-top-right-radius: 5px;
            `;

            // Make panel draggable
            let isDragging = false;
            let offsetX, offsetY;

            dragHandle.addEventListener('mousedown', function(e) {
                isDragging = true;
                offsetX = e.clientX - panel.getBoundingClientRect().left;
                offsetY = e.clientY - panel.getBoundingClientRect().top;
            });

            document.addEventListener('mousemove', function(e) {
                if (isDragging) {
                    panel.style.right = 'auto';
                    panel.style.top = (e.clientY - offsetY) + 'px';
                    panel.style.left = (e.clientX - offsetX) + 'px';
                    panel.style.transform = 'none';
                }
            });

            document.addEventListener('mouseup', function() {
                isDragging = false;
            });

            panel.appendChild(dragHandle);

            // Add footer
            const footer = document.createElement('div');
            footer.style = `
                margin-top: 15px;
                padding-top: 10px;
                border-top: 1px solid #464442;
                font-size: 12px;
                text-align: center;
                opacity: 0.7;
            `;

            footer.innerText = 'Press ESC to toggle interface';
            panel.appendChild(footer);

            // Append panel to body
            document.body.appendChild(panel);

            loaded = true;
            console.log("[UI] Chess.com themed interface created successfully");
        } catch (error) {
            console.error("[UI] Error creating interface:", error);
        }
    };

    // Main interval loop
    const waitForChessBoard = setInterval(() => {
        if (loaded) {
            board = $('chess-board')[0] || $('wc-chess-board')[0];

            // Read checkbox states directly from DOM
            try {
                myVars.suggestMove = document.getElementById('suggestMove').checked;
                myVars.autoMove = document.getElementById('autoMove').checked;
                myVars.autoMatch = document.getElementById('autoMatch').checked;

                // Save the current state
                GM_setValue('suggestMove', myVars.suggestMove);
                GM_setValue('autoMove', myVars.autoMove);
                GM_setValue('autoMatch', myVars.autoMatch);

                // Read delay values
                let minDelay = parseFloat(document.getElementById('timeDelayMin').value) || 0.1;
                let maxDelay = parseFloat(document.getElementById('timeDelayMax').value) || 1.0;
                myVars.delay = Math.random() * (maxDelay - minDelay) + minDelay;
            } catch (e) {
                console.warn("[UI] Error reading UI state:", e);
            }

            // Update spinner
            myVars.isThinking = isThinking;
            myFunctions.spinner();

            // Check for game over
            const gameOverModal = $('.game-over-modal-content');
            if (gameOverModal.length > 0 && !myVars.gameEnded) {
                console.log("[Game] Game over detected");
                myVars.gameEnded = true;
                myVars.hasAutoMatched = false;
            }

            // Check turn
            try {
                if (!myVars.gameEnded && board && board.game) {
                    myTurn = (board.game.getTurn() == board.game.getPlayingAs());
                } else {
                    myTurn = false;
                }
            } catch (e) {
                myTurn = false;
            }

            // Log state (for debugging)
            console.log(`[State] SuggestMove:${myVars.suggestMove} AutoMove:${myVars.autoMove} AutoMatch:${myVars.autoMatch} MyTurn:${myTurn} Thinking:${isThinking} CanGo:${canGo}`);

            // Make sure engine is loaded
            if (!engine.engine) {
                myFunctions.loadChessEngine();
            }

            // Auto Run Logic (Now Suggested Move Logic)
            if (myVars.suggestMove && canGo && !isThinking && myTurn && !myVars.gameEnded) {
                console.log("[Auto] Triggering suggested move analysis...");
                canGo = false;
                const currentDelay = myVars.delay * 1000;
                other(currentDelay);
            }

            // Auto Match Logic
            if (myVars.autoMatch && myVars.gameEnded && !myVars.hasAutoMatched) {
                console.log("[Auto] Triggering auto match...");
                myFunctions.startNewGame();
            }
        } else if ($('chess-board, wc-chess-board').length > 0) {
            // Try to load UI if not loaded yet
            myFunctions.loadEx();
        }
    }, 100);
}

// Global variables
var isThinking = false;
var canGo = true;
var myTurn = false;
var board;

// Start the script
window.addEventListener("load", (event) => {
    console.log("[Script] Chess.com Bot v3.0 starting...");
    main();
});