Chess.com Cheat Engine

Chess.com cheat engine

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Chess.com Cheat Engine
// @license      MIT
// @namespace    http://tampermonkey.net/
// @version      9.0
// @description  Chess.com cheat engine
// @author       rexxx
// @match        https://www.chess.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      unpkg.com
// @connect      cdn.jsdelivr.net
// @connect      cdnjs.cloudflare.com
// @connect      lichess.org
// @connect      stockfish.online
// @connect      tablebase.lichess.ovh
// @connect      tablebase.lichess.ovh
// @run-at       document-idle
// @grant        unsafeWindow
// ==/UserScript==

(function () {
    'use strict';

    const CONFIG = {
        engineDepth: 16,
        pollInterval: 1000,
        minThinkTime: 300,
        maxThinkTime: 1200,
        blunderChance: 0.05,
        occasionalSlowMove: 0.05,
        slowMoveDelay: 2000,
        behaviorVariability: true,
        auto: {
            enabled: false,
            minDelay: 500,
            maxDelay: 2000,
            fastPlayChance: 0.25,
            fatigueEnabled: true,
            breakChance: 0.02,
            autoQueue: true
        },
        arrowOpacity: 0.8,
        showPanel: true,
        showThreats: true,
        stealthMode: false,
        useBook: true,
        personality: 'master',
    };

    const SELECTORS = {
        board: ['wc-chess-board', 'chess-board'],
        chat: '.chat-scroll-area-component',
        moves: 'vertical-move-list, wc-move-list, .move-list-component',
        clocks: '.clock-component',
        gameOver: [
            '.game-over-modal-container',
            '.modal-game-over-component',
            '[data-cy="game-over-modal"]',
            '.game-result-header'
        ],
        drawOffer: '.draw-offer-component',
        promotion: {
            dialog: '.promotion-window, .promotion-piece',
            items: '.promotion-piece'
        }
    };

    const State = {
        engineFound: false,
        isThinking: false,
        lastFen: null,
        playerColor: null,
        gameId: null,
        moveCount: 0,
        boredomLevel: 0,
        personality: null,
        ui: {
            overlay: null,
            panel: null,
            statusDot: null,
            autoIndicator: null
        },
        workers: {
            stockfish: null
        },
        cache: {
            fen: new Map(),
            board: null
        }
    };

    const Utils = {
        sleep: (ms) => new Promise(r => setTimeout(r, ms)),

        log: (msg, type = 'info') => {
            const colors = {
                info: '#3eff3e',
                warn: '#ffcc00',
                error: '#ff4444',
                debug: '#aaaaff'
            };
            console.log(`%c[BA] ${msg}`, `color: ${colors[type]}; font-weight: bold;`);
        },

        randomRange: (min, max) => Math.random() * (max - min) + min,

        isTabActive: () => !document.hidden,

        query: (selector, root = document) => {
            if (Array.isArray(selector)) {
                for (const s of selector) {
                    const el = root.querySelector(s);
                    if (el) return el;
                }
                return null;
            }
            return root.querySelector(selector);
        }
    };

    const UI = {
        injectStyles: () => {
            GM_addStyle(`
                @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;700&display=swap');

                .ba-overlay { pointer-events: none; z-index: 10000; position: absolute; top: 0; left: 0; transition: opacity 0.2s; }
                .ba-stealth { opacity: 0 !important; pointer-events: none !important; }

                .ba-panel {
                    position: fixed; top: 50px; left: 50px; z-index: 10001;
                    width: 300px;
                    background: rgba(10, 10, 12, 0.95);
                    color: #e0e0e0;
                    border: 1px solid #333;
                    border-left: 2px solid #4caf50;
                    border-radius: 8px;
                    font-family: 'Inter', sans-serif;
                    box-shadow: 0 10px 40px rgba(0,0,0,0.6);
                    overflow: hidden;
                    display: flex; flex-direction: column;
                }

                .ba-header {
                    padding: 12px 16px;
                    background: linear-gradient(90deg, rgba(76,175,80,0.1), transparent);
                    border-bottom: 1px solid rgba(255,255,255,0.05);
                    display: flex; justify-content: space-between; align-items: center;
                    cursor: grab; user-select: none;
                }
                .ba-logo { font-weight: 800; font-size: 14px; letter-spacing: 1px; color: #4caf50; }
                .ba-logo span { color: #fff; opacity: 0.7; font-weight: 400; }
                .ba-minimize { cursor: pointer; opacity: 0.5; transition: 0.2s; }
                .ba-minimize:hover { opacity: 1; color: #fff; }

                .ba-tabs { display: flex; background: rgba(0,0,0,0.2); }
                .ba-tab {
                    flex: 1; text-align: center; padding: 10px 0;
                    font-size: 11px; font-weight: 600; color: #666;
                    cursor: pointer; transition: 0.2s;
                    border-bottom: 2px solid transparent;
                }
                .ba-tab:hover { color: #aaa; background: rgba(255,255,255,0.02); }
                .ba-tab.active { color: #e0e0e0; border-bottom: 2px solid #4caf50; background: rgba(76,175,80,0.05); }

                .ba-content { padding: 16px; min-height: 150px; max-height: 400px; overflow-y: auto; }
                .ba-page { display: none; }
                .ba-page.active { display: block; animation: fadeIn 0.2s; }

                .ba-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
                .ba-label { font-size: 12px; color: #aaa; }
                .ba-value { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: #4caf50; }

                .ba-slider-container { margin-bottom: 14px; }
                .ba-slider-header { display: flex; justify-content: space-between; margin-bottom: 6px; }
                .ba-slider {
                    -webkit-appearance: none; width: 100%; height: 4px;
                    background: #333; border-radius: 2px; outline: none;
                }
                .ba-slider::-webkit-slider-thumb {
                    -webkit-appearance: none; width: 12px; height: 12px;
                    background: #4caf50; border-radius: 50%; cursor: pointer;
                    box-shadow: 0 0 10px rgba(76,175,80,0.4);
                }

                .ba-checkbox {
                    width: 16px; height: 16px; border: 1px solid #444;
                    background: #111; border-radius: 3px; cursor: pointer;
                    display: flex; align-items: center; justify-content: center;
                }
                .ba-checkbox.checked { background: #4caf50; border-color: #4caf50; }
                .ba-checkbox.checked::after { content: '✓'; font-size: 10px; color: #000; font-weight: bold; }

                .ba-status-box {
                    background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.05);
                    border-radius: 6px; padding: 12px; margin-bottom: 16px; text-align: center;
                }
                .ba-eval-large { font-family: 'JetBrains Mono'; font-size: 24px; font-weight: 700; color: #fff; margin-bottom: 4px; display: block;}
                .ba-best-move-large { font-family: 'JetBrains Mono'; font-size: 14px; color: #4caf50; background: rgba(76,175,80,0.1); padding: 4px 8px; border-radius: 4px; display: inline-block; }

                .ba-footer {
                    padding: 8px 16px; font-size: 10px; color: #555;
                    border-top: 1px solid rgba(255,255,255,0.05);
                    display: flex; gap: 12px;
                }
                .ba-key { color: #888; background: #222; padding: 1px 4px; border-radius: 3px; font-family: monospace; border: 1px solid #333; }

                @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
                .ba-arrow { stroke-linecap: round; opacity: ${CONFIG.arrowOpacity}; filter: drop-shadow(0 0 4px rgba(0,0,0,0.5)); }
            `);
        },

        createInterface: () => {
            if (State.ui.panel) return;

            const panel = document.createElement('div');
            panel.className = 'ba-panel';
            panel.innerHTML = `
                <div class="ba-header">
                    <div class="ba-logo">REXXX<span>.MENU</span></div>
                    <div class="ba-minimize">_</div>
                </div>
                <div class="ba-tabs">
                    <div class="ba-tab active" data-tab="main">MAIN</div>
                    <div class="ba-tab" data-tab="timings">TIMINGS</div>
                    <div class="ba-tab" data-tab="visuals">VISUALS</div>
                </div>
                <div class="ba-content">
                    <div id="tab-main" class="ba-page active">
                        <div class="ba-status-box">
                            <span class="ba-eval-large">0.00</span>
                            <span class="ba-best-move-large">Waiting...</span>
                        </div>

                        <div class="ba-row">
                            <span class="ba-label">Auto-Play</span>
                            <div class="ba-checkbox ${CONFIG.auto.enabled ? 'checked' : ''}" id="toggle-auto"></div>
                        </div>
                        <div class="ba-row">
                            <span class="ba-label">Opening Book</span>
                            <div class="ba-checkbox ${CONFIG.useBook ? 'checked' : ''}" id="toggle-book"></div>
                        </div>
                    </div>

                    <div id="tab-timings" class="ba-page">
                        <div class="ba-slider-container">
                            <div class="ba-slider-header">
                                <span class="ba-label">Min Delay (ms)</span>
                                <span class="ba-value" id="val-min">${CONFIG.auto.minDelay}</span>
                            </div>
                            <input type="range" class="ba-slider" min="0" max="2000" value="${CONFIG.auto.minDelay}" id="slide-min">
                        </div>
                        <div class="ba-slider-container">
                            <div class="ba-slider-header">
                                <span class="ba-label">Max Delay (ms)</span>
                                <span class="ba-value" id="val-max">${CONFIG.auto.maxDelay}</span>
                            </div>
                            <input type="range" class="ba-slider" min="500" max="5000" value="${CONFIG.auto.maxDelay}" id="slide-max">
                        </div>
                         <div class="ba-slider-container">
                            <div class="ba-slider-header">
                                <span class="ba-label">Blunder Chance %</span>
                                <span class="ba-value" id="val-blunder">${CONFIG.blunderChance * 100}</span>
                            </div>
                            <input type="range" class="ba-slider" min="0" max="20" value="${CONFIG.blunderChance * 100}" id="slide-blunder">
                        </div>
                    </div>
                    <div id="tab-visuals" class="ba-page">
                        <div class="ba-row">
                            <span class="ba-label">Arrow Opacity</span>
                            <div class="ba-slider" style="width: 100px;"></div>
                        </div>
                         <div class="ba-row">
                            <span class="ba-label">Show Threats</span>
                            <div class="ba-checkbox ${CONFIG.showThreats ? 'checked' : ''}" id="toggle-threats"></div>
                        </div>
                    </div>
                </div>
                <div class="ba-footer">
                    <span><span class="ba-key">A</span> Toggle Auto</span>
                    <span><span class="ba-key">X</span> Stealth</span>
                </div>
            `;

            document.body.appendChild(panel);
            State.ui.panel = panel;

            UI.makeDraggable(panel);
            UI.initListeners(panel);
        },

        initListeners: (panel) => {
            panel.querySelectorAll('.ba-tab').forEach(t => {
                t.addEventListener('click', (e) => {
                    panel.querySelectorAll('.ba-tab').forEach(x => x.classList.remove('active'));
                    panel.querySelectorAll('.ba-page').forEach(x => x.classList.remove('active'));

                    e.target.classList.add('active');
                    panel.querySelector(`#tab-${e.target.dataset.tab}`).classList.add('active');
                });
            });

            const toggle = (id, configPath, callback) => {
                panel.querySelector(`#${id}`).addEventListener('click', (e) => {
                    const keys = configPath.split('.');
                    if (keys.length === 2) CONFIG[keys[0]][keys[1]] = !CONFIG[keys[0]][keys[1]];
                    else CONFIG[configPath] = !CONFIG[configPath];

                    e.target.classList.toggle('checked');
                    if (callback) callback();
                });
            };

            toggle('toggle-auto', 'auto.enabled');
            toggle('toggle-book', 'useBook');
            toggle('toggle-threats', 'showThreats');

            const slider = (id, valId, configPath) => {
                const el = panel.querySelector(`#${id}`);
                const display = panel.querySelector(`#${valId}`);
                el.addEventListener('input', (e) => {
                    const val = parseInt(e.target.value);
                    const keys = configPath.split('.');
                    if (keys.length === 2) CONFIG[keys[0]][keys[1]] = val;
                    else CONFIG[configPath] = val;
                    display.textContent = val;
                });
            };

            slider('slide-min', 'val-min', 'auto.minDelay');
            slider('slide-max', 'val-max', 'auto.maxDelay');
        },

        makeDraggable: (el) => {
            const header = el.querySelector('.ba-header');
            let isDragging = false;
            let startX, startY, initialLeft, initialTop;

            header.addEventListener('mousedown', (e) => {
                isDragging = true;
                startX = e.clientX;
                startY = e.clientY;
                initialLeft = el.offsetLeft;
                initialTop = el.offsetTop;
                header.style.cursor = 'grabbing';
            });

            document.addEventListener('mousemove', (e) => {
                if (!isDragging) return;
                const dx = e.clientX - startX;
                const dy = e.clientY - startY;
                el.style.left = `${initialLeft + dx}px`;
                el.style.top = `${initialTop + dy}px`;
            });

            document.addEventListener('mouseup', () => {
                isDragging = false;
                header.style.cursor = 'grab';
            });
        },

        toggleStealth: () => {
            CONFIG.stealthMode = !CONFIG.stealthMode;
            const p = State.ui.panel;
            if (p) p.style.opacity = CONFIG.stealthMode ? '0' : '1';
            document.querySelectorAll('.ba-overlay').forEach(el => {
                el.classList.toggle('ba-stealth', CONFIG.stealthMode);
            });
        },

        updatePanel: (evalData, bestMove) => {
            if (!State.ui.panel) return;

            const evalBox = State.ui.panel.querySelector('.ba-eval-large');
            const moveBox = State.ui.panel.querySelector('.ba-best-move-large');

            if (evalData) {
                evalBox.textContent = evalData.type === 'mate' ? `M${evalData.value}` : evalData.value.toFixed(2);
                evalBox.style.color = evalData.value > 0.5 ? '#4caf50' : (evalData.value < -0.5 ? '#ff5252' : '#e0e0e0');
            }
            if (bestMove) {
                moveBox.textContent = bestMove.move || '...';
            }

            State.ui.panel.querySelector('#toggle-auto').classList.toggle('checked', CONFIG.auto.enabled);
        },

        updateStatus: (color) => {
            if (!State.ui.panel) return;
            State.ui.panel.style.borderLeftColor = color;
            const logo = State.ui.panel.querySelector('.ba-logo');
            if (logo) logo.style.color = color;

            if (color === 'red') {
                const moveBox = State.ui.panel.querySelector('.ba-best-move-large');
                if (moveBox) moveBox.textContent = 'Engine Failed';
            }
        },

        clearOverlay: () => {
            document.querySelectorAll('.ba-overlay').forEach(e => e.remove());
        },

        drawMove: (move, color = '#4caf50', secondary = false) => {
            if (CONFIG.stealthMode) return;
            if (!secondary) UI.clearOverlay();

            const board = Game.getBoard();
            if (!board) return;

            const rect = board.getBoundingClientRect();
            let overlay = secondary ? document.querySelector('.ba-overlay') : null;

            if (!overlay) {
                overlay = document.createElement('div');
                overlay.className = 'ba-overlay';
                if (CONFIG.stealthMode) overlay.classList.add('ba-stealth');
                overlay.style.width = rect.width + 'px';
                overlay.style.height = rect.height + 'px';
                overlay.style.left = rect.left + window.scrollX + 'px';
                overlay.style.top = rect.top + window.scrollY + 'px';
                document.body.appendChild(overlay);
            }

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

            const file = (c) => c.charCodeAt(0) - 97;
            const rank = (c) => parseInt(c) - 1;

            const isFlipped = State.playerColor === 'b';
            const sqSize = rect.width / 8;

            const getPos = (sq) => {
                const f = file(sq[0]);
                const r = rank(sq[1]);
                return {
                    x: (isFlipped ? 7 - f : f) * sqSize + sqSize / 2,
                    y: (isFlipped ? r : 7 - r) * sqSize + sqSize / 2
                };
            };

            const p1 = getPos(from);
            const p2 = getPos(to);

            let svg = overlay.querySelector('svg');
            if (!svg) {
                svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
                svg.style.width = '100%';
                svg.style.height = '100%';
                overlay.appendChild(svg);
            }

            const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            line.setAttribute('x1', p1.x);
            line.setAttribute('y1', p1.y);
            line.setAttribute('x2', p2.x);
            line.setAttribute('y2', p2.y);
            line.setAttribute('stroke', color);
            line.setAttribute('stroke-width', sqSize * (secondary ? 0.1 : 0.18));
            line.setAttribute('class', 'ba-arrow');
            if (secondary) line.setAttribute('stroke-dasharray', '5,5');

            const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
            circle.setAttribute('cx', p2.x);
            circle.setAttribute('cy', p2.y);
            circle.setAttribute('r', sqSize * (secondary ? 0.1 : 0.18));
            circle.setAttribute('fill', color);
            circle.setAttribute('opacity', secondary ? 0.5 : 0.8);

            svg.appendChild(line);
            svg.appendChild(circle);
        }
    };

    const Game = {
        getBoard: () => {
            if (State.cache.board && State.cache.board.isConnected) return State.cache.board;
            State.cache.board = Utils.query(SELECTORS.board);
            return State.cache.board;
        },

        getElementAtSquare: (sq) => {
            const board = Game.getBoard();
            if (!board) return null;

            const piece = board.querySelector(`.piece.square-${Game.squareToCoords(sq)}`) ||
                board.querySelector(`.piece.${sq}`);

            if (piece) return piece;

            if (board.shadowRoot) {
                const shadowPiece = board.shadowRoot.querySelector(`.piece.square-${Game.squareToCoords(sq)}`);
                if (shadowPiece) return shadowPiece;
            }

            return board;
        },

        squareToCoords: (sq) => {
            const file = sq.charCodeAt(0) - 96;
            const rank = sq[1];
            return `${file}${rank}`;
        },

        detectColor: () => {
            const board = Game.getBoard();
            if (!board) return null;

            const isFlipped = board.classList.contains('flipped') || board.getAttribute('flipped') === 'true';
            return isFlipped ? 'b' : 'w';
        },

        isValidFen: (fen) => {
            if (!fen || typeof fen !== 'string') return false;
            return fen.split(' ').length >= 4;
        },

        getFen: () => {
            const board = Game.getBoard();
            if (!board) return null;

            if (board.game && board.game.getFEN) return board.game.getFEN();

            const keys = Object.keys(board);
            const reactKey = keys.find(k => k.startsWith('__reactFiber') || k.startsWith('__reactInternal'));
            if (reactKey) {
                let curr = board[reactKey];
                while (curr) {
                    if (curr.memoizedProps?.game?.fen) return curr.memoizedProps.game.fen;
                    if (typeof curr.memoizedProps?.fen === 'string') return curr.memoizedProps.fen;
                    curr = curr.return;
                }
            }

            return null;
        },

        isMyTurn: (fen) => {
            if (!fen || !State.playerColor) return false;
            const turn = fen.split(' ')[1];
            return turn === State.playerColor;
        },

        isCapture: (move) => {
            const board = Game.getBoard();
            if (!board) return false;
            const to = move.substring(2, 4);
            const coords = Game.squareToCoords(to);
            return !!board.querySelector(`.piece.square-${coords}`);
        }
    };

    const OpeningBook = {
        fetchMove: (fen) => {
            if (!CONFIG.useBook) return null;

            return new Promise((resolve) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://explorer.lichess.ovh/masters?fen=${fen}`,
                    onload: (response) => {
                        try {
                            const data = JSON.parse(response.responseText);
                            if (data.moves && data.moves.length > 0) {
                                const topMoves = data.moves.slice(0, 3);
                                const totalGames = topMoves.reduce((sum, m) => sum + m.white + m.draw + m.black, 0);

                                let r = Math.random() * totalGames;
                                for (const move of topMoves) {
                                    const games = move.white + move.draw + move.black;
                                    if (r < games) {
                                        resolve(move.uci);
                                        return;
                                    }
                                    r -= games;
                                }
                                resolve(topMoves[0].uci);
                            } else {
                                resolve(null);
                            }
                        } catch (e) {
                            resolve(null);
                        }
                    },
                    onerror: () => resolve(null)
                });
            });
        }
    };

    const Engine = {
        init: async () => {
            if (State.workers.stockfish) return;

            Utils.log('Initializing Stockfish...');
            UI.updateStatus('orange'); // Connecting

            try {
                // Use GM_xmlhttpRequest to bypass CSP
                const scriptContent = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: 'https://unpkg.com/[email protected]/stockfish.js',
                        onload: (res) => resolve(res.responseText),
                        onerror: (err) => reject(err)
                    });
                });

                const blob = new Blob([scriptContent], { type: 'application/javascript' });
                State.workers.stockfish = new Worker(URL.createObjectURL(blob));

                State.workers.stockfish.onmessage = (e) => {
                    const msg = e.data;
                    if (msg === 'uciok') {
                        State.engineFound = true;
                        UI.updateStatus('#4caf50'); // Green
                        Utils.log('Stockfish Ready');
                    }
                    if (msg.startsWith('bestmove')) {
                        const move = msg.split(' ')[1];
                        Engine.handleBestMove(move);
                    }
                    if (msg.startsWith('info') && msg.includes('score')) {
                        Engine.parseScore(msg);
                    }
                };

                State.workers.stockfish.postMessage('uci');
                State.workers.stockfish.postMessage('isready');
                State.workers.stockfish.postMessage(`setoption name MultiPV value 1`);

            } catch (e) {
                Utils.log('Stockfish Init Failed: ' + e, 'error');
                UI.updateStatus('red');
            }
        },

        analyze: async (fen) => {
            if (!State.workers.stockfish || !State.engineFound) return;
            State.isThinking = true;

            if (CONFIG.useBook) {
                const bookMove = await OpeningBook.fetchMove(fen);
                if (bookMove) {
                    Utils.log(`Book Move Found: ${bookMove}`);
                    Engine.handleBestMove(bookMove, true);
                    return;
                }
            }

            State.workers.stockfish.postMessage('stop');
            State.workers.stockfish.postMessage(`position fen ${fen}`);
            const multipv = CONFIG.showThreats ? 2 : 1;
            State.workers.stockfish.postMessage(`setoption name MultiPV value ${multipv}`);
            State.workers.stockfish.postMessage(`go depth ${CONFIG.engineDepth}`);
        },

        parseScore: (msg) => {
            const scoreMatch = msg.match(/score (cp|mate) (-?\d+)/);
            const pvMatch = msg.match(/multipv (\d+)/);
            const depthMatch = msg.match(/depth (\d+)/);
            const moveMatch = msg.match(/ pv (\w+)/);

            if (scoreMatch && pvMatch && moveMatch) {
                const type = scoreMatch[1];
                let value = parseInt(scoreMatch[2]);
                const mpv = parseInt(pvMatch[1]);

                if (type === 'cp') value = value / 100;

                if (mpv === 1) {
                    State.currentEval = { type, value, depth: parseInt(depthMatch?.[1] || 0) };
                    State.currentBestMove = moveMatch[1];
                }

                if (mpv === 1 && msg.includes(' pv ')) {
                    const pvMoves = msg.split(' pv ')[1].split(' ');
                    if (pvMoves.length > 1) {
                        State.opponentResponse = pvMoves[1];
                    }
                }
            }
        },

        handleBestMove: async (bestMove, isBook = false) => {
            State.isThinking = false;
            Utils.log(`Best Move: ${bestMove} ${isBook ? '(Book)' : ''}`);

            let finalMove = bestMove;

            UI.drawMove(finalMove, '#4caf50');

            if (CONFIG.showThreats && State.opponentResponse) {
                UI.drawMove(State.opponentResponse, '#ff5252', true);
            }

            UI.updatePanel(State.currentEval, { move: finalMove });

            if (CONFIG.auto.enabled && Game.isMyTurn(State.lastFen)) {

                let min = CONFIG.auto.minDelay;
                let max = CONFIG.auto.maxDelay;

                if (isBook || State.moveCount < 10) {
                    min = 200; max = 600;
                } else if (Game.isCapture(finalMove)) {
                    min = 150; max = 450;
                    Utils.log('Humanizer: Reflex Capture');
                } else {

                    if (State.currentEval && Math.abs(State.currentEval.value) > 3) {
                        max = 800;
                    }
                    else if (State.currentEval && Math.abs(State.currentEval.value) < 1.0) {
                        min += 400; max += 1000;
                    }

                    if (Math.random() < 0.05) {
                        min = 2500; max = 5000;
                        Utils.log('Humanizer: Deep Thinking...');
                    }
                }

                const delay = Utils.randomRange(min, max);
                Utils.log(`Waiting ${Math.round(delay)}ms...`);
                await Utils.sleep(delay);

                await Humanizer.executeMove(finalMove);
            }
        }
    };

    const Humanizer = {
        createEvent: (type, x, y, options = {}) => {
            const defaults = {
                bubbles: true,
                cancelable: true,
                view: window,
                detail: 1,
                screenX: x,
                screenY: y,
                clientX: x,
                clientY: y,
                pointerId: 1,
                pointerType: 'mouse',
                isPrimary: true,
                button: 0,
                buttons: 1,
                which: 1,
                composed: true
            };
            return new PointerEvent(type, { ...defaults, ...options });
        },

        getElementsAt: (x, y) => {
            return document.elementsFromPoint(x, y).filter(el =>
                el.tagName !== 'HTML' && el.tagName !== 'BODY' && !el.classList.contains('ba-overlay')
            );
        },

        makeGodMove: (from, to, promo) => false,

        showClick: (x, y, color = 'red') => {
            const dot = document.createElement('div');
            dot.style.cssText = `
                position: absolute;
                left: ${x}px; top: ${y}px;
                width: 12px; height: 12px;
                background: ${color}; border-radius: 50%;
                z-index: 100000; pointer-events: none;
                transform: translate(-50%, -50%);
                box-shadow: 0 0 4px white;
            `;
            document.body.appendChild(dot);
            setTimeout(() => dot.remove(), 800);
        },

        dragDrop: async (fromSq, toSq) => {
            const board = Game.getBoard();
            if (!board) return;

            const startPos = Humanizer.getCoords(fromSq);
            const endPos = Humanizer.getCoords(toSq);
            if (!startPos || !endPos) return;

            Humanizer.showClick(startPos.x, startPos.y, '#00ff00');

            const fromCoords = Game.squareToCoords(fromSq);
            const pieceEl = board.querySelector(`.piece.square-${fromCoords}`) ||
                document.elementFromPoint(startPos.x, startPos.y);

            const targetSource = pieceEl || board;

            Utils.log(`Draggable: ${fromSq} -> ${toSq}`);

            const opts = { bubbles: true, composed: true, buttons: 1, pointerId: 1, isPrimary: true };

            targetSource.dispatchEvent(new PointerEvent('pointerover', { ...opts, clientX: startPos.x, clientY: startPos.y }));
            targetSource.dispatchEvent(new PointerEvent('pointerdown', { ...opts, clientX: startPos.x, clientY: startPos.y }));
            targetSource.dispatchEvent(new MouseEvent('mousedown', { ...opts, clientX: startPos.x, clientY: startPos.y }));

            await Utils.sleep(Utils.randomRange(20, 40));

            const initialMoveX = startPos.x + (endPos.x - startPos.x) * 0.1;
            const initialMoveY = startPos.y + (endPos.y - startPos.y) * 0.1;

            document.dispatchEvent(new PointerEvent('pointermove', { ...opts, clientX: initialMoveX, clientY: initialMoveY }));

            await Utils.sleep(20);

            const steps = 6;
            for (let i = 1; i <= steps; i++) {
                const t = i / steps;
                const curX = startPos.x + (endPos.x - startPos.x) * t;
                const curY = startPos.y + (endPos.y - startPos.y) * t;

                document.dispatchEvent(new PointerEvent('pointermove', {
                    ...opts,
                    clientX: curX,
                    clientY: curY,
                    pressure: 0.5
                }));
                document.dispatchEvent(new MouseEvent('mousemove', { ...opts, clientX: curX, clientY: curY }));

                if (i % 2 === 0) await Utils.sleep(5);
            }

            const toCoords = Game.squareToCoords(toSq);
            const targetEl = board.querySelector(`.square-${toCoords}`) ||
                document.elementFromPoint(endPos.x, endPos.y);

            const dropTarget = targetEl || board;

            Humanizer.showClick(endPos.x, endPos.y, 'red');

            dropTarget.dispatchEvent(new PointerEvent('pointerup', { ...opts, clientX: endPos.x, clientY: endPos.y }));
            dropTarget.dispatchEvent(new MouseEvent('mouseup', { ...opts, clientX: endPos.x, clientY: endPos.y }));
            dropTarget.dispatchEvent(new PointerEvent('click', { ...opts, clientX: endPos.x, clientY: endPos.y }));
        },

        clickSquare: async (sq) => { },

        executeMove: async (move) => {
            const currentFen = Game.getFen();
            if (currentFen !== State.lastFen && State.moveCount > 0) return;

            const from = move.substring(0, 2);
            const to = move.substring(2, 4);
            const promo = move.length > 4 ? move[4] : null;

            if (Humanizer.makeGodMove(from, to, promo)) {
                Utils.log('API Success?');
            }

            Utils.log(`Auto-playing (Drag): ${from} -> ${to}`);
            await Humanizer.dragDrop(from, to);

            if (promo) {
                await Utils.sleep(300);
                const screenPromo = document.querySelector('.promotion-window');
                if (screenPromo) {
                    const promoImg = screenPromo.querySelector(`img[src*="${promo}"]`) ||
                        screenPromo.querySelector(`.${promo}`);

                    if (promoImg) {
                        const center = promoImg.getBoundingClientRect();
                        const x = center.left + center.width / 2;
                        const y = center.top + center.height / 2;

                        const opts = { bubbles: true, composed: true, buttons: 1 };
                        promoImg.dispatchEvent(new PointerEvent('pointerdown', { ...opts, clientX: x, clientY: y }));
                        promoImg.dispatchEvent(new PointerEvent('pointerup', { ...opts, clientX: x, clientY: y }));
                        promoImg.dispatchEvent(new PointerEvent('click', { ...opts, clientX: x, clientY: y }));
                    }
                }
            }
        },

        getCoords: (sq) => {
            const board = Game.getBoard();
            if (!board) return null;

            const rect = board.getBoundingClientRect();
            const sqSize = rect.width / 8;
            const isFlipped = State.playerColor === 'b';

            const f = sq.charCodeAt(0) - 97;
            const r = parseInt(sq[1]) - 1;

            const x = rect.left + (isFlipped ? 7 - f : f) * sqSize + sqSize / 2;
            const y = rect.top + (isFlipped ? r : 7 - r) * sqSize + sqSize / 2;

            return { x, y };
        }
    };

    const Main = {
        init: async () => {
            UI.injectStyles();
            UI.createInterface();

            let board = null;
            while (!board) {
                board = Game.getBoard();
                if (!board) await Utils.sleep(500);
            }

            Utils.log('Board detected. Starting Engine...');
            await Engine.init();

            Main.setupObservers();

            Main.gameLoop();

            document.addEventListener('keydown', (e) => {
                if (e.key === 'a' && !e.ctrlKey && !e.shiftKey && !e.target.matches('input')) {
                    CONFIG.auto.enabled = !CONFIG.auto.enabled;
                    Utils.log(`Auto-Play: ${CONFIG.auto.enabled}`);
                    UI.updatePanel(State.currentEval, {});
                }

                if (e.key === 'x' && !e.target.matches('input')) {
                    UI.toggleStealth();
                }
            });
        },

        checkAutoQueue: () => {
            if (!CONFIG.auto.autoQueue) return;
            const buttons = [
                'button[data-cy="new-game-index-main"]',
                '.game-over-button-component.primary',
                '.ui_v5-button-component.ui_v5-button-primary'
            ];

            for (const sel of buttons) {
                const btn = document.querySelector(sel);
                if (btn && btn.offsetParent !== null) {
                    Utils.log('Auto-Queue: Clicking New Game...');
                    btn.click();
                    break;
                }
            }
        },

        setupObservers: () => {
            const movesList = Utils.query(SELECTORS.moves) || document.body;

            const observer = new MutationObserver((mutations) => {
                requestAnimationFrame(Main.gameLoop);
            });

            observer.observe(movesList, { childList: true, subtree: true, characterData: true });

            setInterval(Main.gameLoop, CONFIG.pollInterval);
        },

        gameLoop: () => {
            const fen = Game.getFen();
            if (!fen || fen === State.lastFen) return;

            State.playerColor = Game.detectColor();
            State.lastFen = fen;

            State.currentEval = null;
            UI.clearOverlay();
            UI.updatePanel(null, null);

            if (Utils.query(SELECTORS.gameOver)) {
                if (CONFIG.auto.autoQueue) {
                    setTimeout(Main.checkAutoQueue, 2000);
                }
                return;
            }

            if (Game.isMyTurn(fen)) {
                Engine.analyze(fen);
            } else {
                UI.updateStatus('#888');
            }
        }
    };

    Main.init();

})();