Chess.com Stockfish AutoMove Bot with UI

Auto move bot using Stockfish on Chess.com with start/stop UI

Ekde 2025/07/19. Vidu La ĝisdata versio.

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!)

// ==UserScript==
// @name         Chess.com Stockfish AutoMove Bot with UI
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Auto move bot using Stockfish on Chess.com with start/stop UI
// @author       Nimmmuary123456
// @match        https://www.chess.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Stockfish wasm build URL (you can self-host for reliability)
    const STOCKFISH_URL = 'https://cdn.jsdelivr.net/gh/niklasf/stockfish.js/stockfish.wasm.js';

    let stockfish;
    let isRunning = false;
    let analysisRunning = false;

    // Create UI panel
    const panel = document.createElement('div');
    panel.style.position = 'fixed';
    panel.style.top = '10px';
    panel.style.right = '10px';
    panel.style.backgroundColor = '#222';
    panel.style.color = 'white';
    panel.style.padding = '10px';
    panel.style.borderRadius = '8px';
    panel.style.zIndex = 99999;
    panel.style.fontFamily = 'Arial, sans-serif';
    panel.style.width = '200px';
    panel.style.boxShadow = '0 0 10px black';

    const title = document.createElement('div');
    title.textContent = 'Stockfish AutoMove Bot';
    title.style.fontWeight = 'bold';
    title.style.marginBottom = '8px';
    title.style.textAlign = 'center';
    panel.appendChild(title);

    const status = document.createElement('div');
    status.textContent = 'Status: Stopped';
    status.style.marginBottom = '8px';
    panel.appendChild(status);

    const btnStartStop = document.createElement('button');
    btnStartStop.textContent = 'Start';
    btnStartStop.style.width = '100%';
    btnStartStop.style.padding = '8px';
    btnStartStop.style.fontSize = '16px';
    btnStartStop.style.cursor = 'pointer';
    panel.appendChild(btnStartStop);

    document.body.appendChild(panel);

    // Load Stockfish engine as Web Worker
    function loadStockfish() {
        return new Promise((resolve, reject) => {
            stockfish = new Worker(STOCKFISH_URL);

            stockfish.onmessage = (event) => {
                const line = event.data;
                if (line === 'uciok') {
                    resolve();
                }
            };

            stockfish.onerror = (e) => {
                reject(e);
            };

            stockfish.postMessage('uci');
        });
    }

    // Send command to stockfish
    function sendCommand(cmd) {
        //console.log('Sending to Stockfish:', cmd);
        stockfish.postMessage(cmd);
    }

    // Get FEN from Chess.com board
    function getFEN() {
        // Chess.com stores the FEN string in window.__board?.fen() or window.Chessboard
        try {
            if (window.__board && typeof window.__board.fen === 'function') {
                return window.__board.fen();
            }
            // Alternative: read from Chess.com's internal chess object
            if (window.Chess && window.Chess.fen) {
                return window.Chess.fen();
            }
            // Or from Chess.com analysis panel - DOM fallback (last move in PGN)
            // Try to read FEN from page URL or from chessboard data attribute
            const fenInput = document.querySelector('input#fen-input');
            if (fenInput) return fenInput.value;
        } catch (e) {}
        return null;
    }

    // Find squares on board and simulate clicks to play move
    function playMove(move) {
        if (!move || move.length < 4) return;

        const from = move.slice(0, 2);
        const to = move.slice(2, 4);

        // Chess.com board squares have classes like "square-63" but it's complicated;
        // Let's try to use data-coordinates attributes (Chess.com uses algebraic notation)
        // The chessboard div uses data-square="e2", etc.
        const boardSquares = document.querySelectorAll('[data-square]');

        let fromEl, toEl;
        boardSquares.forEach(sq => {
            if (sq.getAttribute('data-square') === from) fromEl = sq;
            if (sq.getAttribute('data-square') === to) toEl = sq;
        });

        if (fromEl && toEl) {
            // Simulate click sequence
            fromEl.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
            fromEl.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
            toEl.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
            toEl.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));

            console.log(`Moved from ${from} to ${to}`);
            status.textContent = `Moved: ${move}`;
        } else {
            console.log('Could not find squares to move:', from, to);
            status.textContent = 'Error: Could not find squares';
        }
    }

    // Analyze position and get best move from Stockfish
    function analyzePosition(fen) {
        return new Promise((resolve) => {
            let bestMove = null;

            function onMessage(event) {
                const line = event.data;

                //console.log('Stockfish:', line);

                if (line.startsWith('bestmove')) {
                    bestMove = line.split(' ')[1];
                    stockfish.removeEventListener('message', onMessage);
                    resolve(bestMove);
                }
            }

            stockfish.addEventListener('message', onMessage);

            sendCommand('ucinewgame');
            sendCommand(`position fen ${fen}`);
            sendCommand('go depth 15');
        });
    }

    // Main bot loop
    async function botLoop() {
        if (!isRunning) return;

        if (analysisRunning) return; // prevent overlapping calls
        analysisRunning = true;

        const fen = getFEN();

        if (!fen) {
            console.log('FEN not found, retrying...');
            status.textContent = 'Waiting for board...';
            analysisRunning = false;
            setTimeout(botLoop, 3000);
            return;
        }

        console.log('Current FEN:', fen);
        status.textContent = 'Analyzing...';

        const bestMove = await analyzePosition(fen);

        if (bestMove && isRunning) {
            playMove(bestMove);
        }

        analysisRunning = false;

        if (isRunning) {
            setTimeout(botLoop, 4000); // wait 4 seconds before next analysis
        }
    }

    // Start the bot
    async function startBot() {
        if (isRunning) return;

        status.textContent = 'Loading Stockfish...';
        await loadStockfish();

        isRunning = true;
        status.textContent = 'Running...';
        btnStartStop.textContent = 'Stop';

        botLoop();
    }

    // Stop the bot
    function stopBot() {
        if (!isRunning) return;

        isRunning = false;
        status.textContent = 'Stopped';
        btnStartStop.textContent = 'Start';

        if (stockfish) {
            stockfish.terminate();
            stockfish = null;
        }
    }

    btnStartStop.addEventListener('click', () => {
        if (isRunning) {
            stopBot();
        } else {
            startBot();
        }
    });

})();