Chess.com Stockfish AutoMove Bot with UI

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

À partir de 2025-07-19. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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

})();