🎮ArcadeLab

Mein Schachbot mit Rating

by BlazeKoala99
322 lines11.3 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mein Schachbot mit Rating</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.css">
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            background-color: #f0f0f0;
            margin: 0;
            overflow: hidden; 
        }
        h1 {
            color: #333;
            margin-bottom: 10px;
            user-select: none;
        }
        .controls {
            margin-bottom: 20px;
            font-size: 1rem;
            user-select: none;
        }
        select {
            padding: 5px 10px;
            font-size: 1rem;
            border-radius: 5px;
            border: 1px solid #ccc;
            cursor: pointer;
        }
        #board {
            width: 400px;
            max-width: 90vw;
            box-shadow: 0 4px 10px rgba(0,0,0,0.3);
            touch-action: none; 
        }
        .chessboard-63984 img, [class^="square-"] img {
            touch-action: none;
            -webkit-user-drag: none;
            user-select: none;
        }
        #status {
            margin-top: 20px;
            font-size: 1.2rem;
            font-weight: bold;
            color: #555;
            user-select: none;
        }
    </style>
</head>
<body>

    <h1>Spiele gegen den Bot!</h1>
    
    <div class="controls">
        <label for="rating-select">Bot-Stärke wählen: </label>
        <select id="rating-select">
            <option value="600">Einsteiger (ca. 600 Elo)</option>
            <option value="800">Hobby-Spieler (ca. 800 Elo)</option>
            <option value="1000">Erfahren (ca. 1000 Elo)</option>
            <option value="1200" selected>Fortgeschritten (ca. 1200 Elo)</option>
            <option value="1600">Herausfordernd (ca. 1600 Elo)</option>
            <option value="2000">Experte (ca. 2000 Elo)</option>
            <option value="2200">Meister (ca. 2200 Elo)</option>
        </select>
    </div>

    <div id="board"></div>
    <div id="status">Du bist dran (Weiß)</div>

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script>

    <script>
        const game = new Chess();
        let board = null;
        const $status = $('#status');

        const pieceValues = {
            p: 10,  
            n: 30,  
            b: 30,  
            r: 50,  
            q: 90,  
            k: 900  
        };

        function onDragStart (source, piece, position, orientation) {
            if (game.game_over()) return false;
            if (piece.search(/^b/) !== -1) return false;
            return true; 
        }

        // Bewertet die aktuelle Stellung auf dem Brett aus Sicht von Schwarz
        function evaluateBoard() {
            let value = 0;
            const boardState = game.board();
            for (let r = 0; r < 8; r++) {
                for (let c = 0; c < 8; c++) {
                    const piece = boardState[r][c];
                    if (piece) {
                        const pValue = pieceValues[piece.type];
                        if (piece.color === 'b') {
                            value += pValue;
                        } else {
                            value -= pValue;
                        }
                    }
                }
            }
            return value;
        }

        function evaluateMove(move) {
            game.move(move);
            let value = evaluateBoard();
            game.undo();
            return value;
        }

        // Minimax-Algorithmus mit Tiefe 2
        function minimaxDepth2() {
            const possibleMoves = game.moves({ verbose: true });
            let bestMoves = [];
            let bestValue = -Infinity;

            for (let i = 0; i < possibleMoves.length; i++) {
                const move = possibleMoves[i];
                game.move(move);

                if (game.in_checkmate()) {
                    game.undo();
                    return move;
                }

                const whiteMoves = game.moves({ verbose: true });
                let worstValueForBlack = Infinity;

                if (whiteMoves.length === 0) {
                    worstValueForBlack = 0;
                } else {
                    for (let j = 0; j < whiteMoves.length; j++) {
                        game.move(whiteMoves[j]);
                        const boardValue = evaluateBoard();
                        if (boardValue < worstValueForBlack) {
                            worstValueForBlack = boardValue;
                        }
                        game.undo();
                    }
                }

                game.undo();

                if (worstValueForBlack > bestValue) {
                    bestValue = worstValueForBlack;
                    bestMoves = [move];
                } else if (worstValueForBlack === bestValue) {
                    bestMoves.push(move);
                }
            }

            const randomIdx = Math.floor(Math.random() * bestMoves.length);
            return bestMoves[randomIdx];
        }

        function makeBotMove () {
            if (game.game_over()) return;

            const possibleMoves = game.moves({ verbose: true });
            if (possibleMoves.length === 0) return;

            const selectedRating = $('#rating-select').val();
            let chosenMove = null;

            // --- 600 ELO (Komplett zufällige Züge) ---
            if (selectedRating === "600") {
                const randomIdx = Math.floor(Math.random() * possibleMoves.length);
                chosenMove = possibleMoves[randomIdx];
            } 
            // --- 800 ELO (Spielt meistens zufällig, macht aber manchmal grobe Fehler) ---
            else if (selectedRating === "800") {
                let worstMoves = [];
                let worstValue = Infinity;

                for (let i = 0; i < possibleMoves.length; i++) {
                    const moveValue = evaluateMove(possibleMoves[i]);
                    if (moveValue < worstValue) {
                        worstValue = moveValue;
                        worstMoves = [possibleMoves[i]];
                    } else if (moveValue === worstValue) {
                        worstMoves.push(possibleMoves[i]);
                    }
                }
                
                // 25% Chance auf einen schlechten Zug, ansonsten zufällig
                if (Math.random() < 0.25) {
                    const randomIdx = Math.floor(Math.random() * worstMoves.length);
                    chosenMove = worstMoves[randomIdx];
                } else {
                    const randomIdx = Math.floor(Math.random() * possibleMoves.length);
                    chosenMove = possibleMoves[randomIdx];
                }
            }
            // --- 1000 ELO (Mischt zufällige Züge mit einfachen Schlagzügen) ---
            else if (selectedRating === "1000") {
                const captures = possibleMoves.filter(m => m.captured);
                if (captures.length > 0 && Math.random() < 0.5) {
                    const randomIdx = Math.floor(Math.random() * captures.length);
                    chosenMove = captures[randomIdx];
                } else {
                    const randomIdx = Math.floor(Math.random() * possibleMoves.length);
                    chosenMove = possibleMoves[randomIdx];
                }
            }
            // --- 1200 ELO (Schlägt bevorzugt Figuren, wenn möglich) ---
            else if (selectedRating === "1200") {
                const captures = possibleMoves.filter(m => m.captured);
                if (captures.length > 0) {
                    const randomIdx = Math.floor(Math.random() * captures.length);
                    chosenMove = captures[randomIdx];
                } else {
                    const randomIdx = Math.floor(Math.random() * possibleMoves.length);
                    chosenMove = possibleMoves[randomIdx];
                }
            } 
            // --- 1600 ELO (Sucht den sofortigen besten Materialgewinn - Tiefe 1) ---
            else if (selectedRating === "1600") {
                let bestMoves = [];
                let bestValue = -Infinity;

                for (let i = 0; i < possibleMoves.length; i++) {
                    const moveValue = evaluateMove(possibleMoves[i]);
                    if (moveValue > bestValue) {
                        bestValue = moveValue;
                        bestMoves = [possibleMoves[i]]; 
                    } else if (moveValue === bestValue) {
                        bestMoves.push(possibleMoves[i]); 
                    }
                }
                const randomIdx = Math.floor(Math.random() * bestMoves.length);
                chosenMove = bestMoves[randomIdx];
            }
            // --- 2000 ELO (Nutzt Minimax Tiefe 2, baut selten kleine Ungenauigkeiten ein) ---
            else if (selectedRating === "2000") {
                if (Math.random() < 0.15) {
                    const randomIdx = Math.floor(Math.random() * possibleMoves.length);
                    chosenMove = possibleMoves[randomIdx];
                } else {
                    chosenMove = minimaxDepth2();
                }
            }
            // --- 2200 ELO (Spielt fehlerfrei auf Minimax Tiefe 2) ---
            else if (selectedRating === "2200") {
                chosenMove = minimaxDepth2();
            }

            game.move(chosenMove);
            board.position(game.fen());
            updateStatus();
        }

        function onDrop (source, target) {
            const move = game.move({
                from: source,
                to: target,
                promotion: 'q' 
            });

            if (move === null) return 'snapback';

            updateStatus();

            if (!game.game_over()) {
                window.setTimeout(makeBotMove, 400);
            }
        }

        function onSnapEnd () {
            board.position(game.fen());
        }

        function updateStatus () {
            let status = '';
            let moveColor = 'Weiß';
            if (game.turn() === 'b') {
                moveColor = 'Schwarz (Bot)';
            }

            if (game.in_checkmate()) {
                status = 'Spiel vorbei! ' + (game.turn() === 'b' ? 'Du hast gewonnen!' : 'Der Bot hat gewonnen!');
            } else if (game.in_draw()) {
                status = 'Spiel vorbei! Unentschieden (Patt oder Materialmangel).';
            } else {
                status = moveColor + ' ist am Zug.';
                if (game.in_check()) {
                    status += ' (Schach!)';
                }
            }
            $status.html(status);
        }

        const config = {
            draggable: true,
            position: 'start',
            onDragStart: onDragStart,
            onDrop: onDrop,
            onSnapEnd: onSnapEnd,
            pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png'
        };

        board = Chessboard('board', config);
        updateStatus();
    </script>
</body>
</html>

Game Source: Mein Schachbot mit Rating

Creator: BlazeKoala99

Libraries: none

Complexity: complex (322 lines, 11.3 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: mein-schachbot-mit-rating-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.