Chess.com Stockfish AutoMove Bot with UI

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

Versione datata 19/07/2025. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

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

})();