<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>墨韵五子棋</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Ma+Shan+Zheng&family=Noto+Serif+SC:wght@400;700&display=swap');

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background: 
                radial-gradient(ellipse at 20% 80%, rgba(139, 90, 43, 0.15) 0%, transparent 50%),
                radial-gradient(ellipse at 80% 20%, rgba(85, 107, 47, 0.12) 0%, transparent 50%),
                linear-gradient(180deg, #f5f0e8 0%, #e8e0d5 50%, #d4c8b8 100%);
            font-family: 'Noto Serif SC', serif;
            overflow: hidden;
        }

        body::before {
            content: '';
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-image: 
                url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%238b7355' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
            pointer-events: none;
            z-index: -1;
        }

        .container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 24px;
            padding: 20px;
        }

        .header {
            text-align: center;
            margin-bottom: 8px;
        }

        .title {
            font-family: 'Ma Shan Zheng', cursive;
            font-size: 3.2rem;
            color: #2c1810;
            text-shadow: 
                2px 2px 0 rgba(139, 90, 43, 0.2),
                4px 4px 8px rgba(0, 0, 0, 0.15);
            letter-spacing: 8px;
            position: relative;
        }

        .title::after {
            content: '· 人机对战 ·';
            display: block;
            font-family: 'Noto Serif SC', serif;
            font-size: 0.9rem;
            color: #6b5344;
            letter-spacing: 4px;
            margin-top: 4px;
        }

        .game-wrapper {
            display: flex;
            align-items: flex-start;
            gap: 32px;
        }

        .board-container {
            position: relative;
            padding: 24px;
            background: 
                linear-gradient(135deg, rgba(205, 170, 120, 0.3) 0%, rgba(180, 140, 90, 0.2) 100%),
                linear-gradient(to bottom, #d4a574 0%, #c49a6c 50%, #b8895a 100%);
            border-radius: 8px;
            box-shadow: 
                0 8px 32px rgba(44, 24, 16, 0.3),
                inset 0 1px 0 rgba(255, 255, 255, 0.2),
                inset 0 -2px 4px rgba(0, 0, 0, 0.1);
        }

        .board-container::before {
            content: '';
            position: absolute;
            top: 8px;
            left: 8px;
            right: 8px;
            bottom: 8px;
            border: 1px solid rgba(139, 90, 43, 0.3);
            border-radius: 4px;
            pointer-events: none;
        }

        #gameCanvas {
            display: block;
            cursor: pointer;
            border-radius: 2px;
        }

        .side-panel {
            display: flex;
            flex-direction: column;
            gap: 20px;
            min-width: 180px;
        }

        .status-box {
            background: linear-gradient(145deg, #faf6f0 0%, #f0e6d8 100%);
            border: 2px solid #c4a77d;
            border-radius: 8px;
            padding: 20px;
            text-align: center;
            box-shadow: 
                0 4px 12px rgba(44, 24, 16, 0.1),
                inset 0 1px 0 rgba(255, 255, 255, 0.8);
        }

        .status-label {
            font-size: 0.85rem;
            color: #8b7355;
            margin-bottom: 8px;
            letter-spacing: 2px;
        }

        .status-text {
            font-size: 1.3rem;
            font-weight: 700;
            color: #2c1810;
            min-height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }

        .status-text.player-turn {
            color: #1a1a1a;
        }

        .status-text.ai-turn {
            color: #4a4a4a;
        }

        .status-text.win {
            color: #8b4513;
            animation: pulse 1s ease-in-out infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.6; }
        }

        .stone-indicator {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            display: inline-block;
            vertical-align: middle;
            box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
        }

        .stone-indicator.black {
            background: radial-gradient(circle at 35% 35%, #4a4a4a 0%, #1a1a1a 60%, #000 100%);
        }

        .stone-indicator.white {
            background: radial-gradient(circle at 35% 35%, #ffffff 0%, #e0e0e0 40%, #c0c0c0 100%);
        }

        .button-group {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .btn {
            padding: 14px 24px;
            font-family: 'Noto Serif SC', serif;
            font-size: 1rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
            letter-spacing: 2px;
        }

        .btn::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
            transition: left 0.5s ease;
        }

        .btn:hover::before {
            left: 100%;
        }

        .btn-restart {
            background: linear-gradient(145deg, #8b7355 0%, #6b5344 100%);
            color: #faf6f0;
            box-shadow: 
                0 4px 12px rgba(44, 24, 16, 0.25),
                inset 0 1px 0 rgba(255, 255, 255, 0.15);
        }

        .btn-restart:hover {
            background: linear-gradient(145deg, #9b8365 0%, #7b6354 100%);
            transform: translateY(-2px);
            box-shadow: 
                0 6px 16px rgba(44, 24, 16, 0.3),
                inset 0 1px 0 rgba(255, 255, 255, 0.15);
        }

        .btn-undo {
            background: linear-gradient(145deg, #f0e6d8 0%, #d4c8b8 100%);
            color: #5a4a3a;
            border: 2px solid #c4a77d;
            box-shadow: 
                0 4px 12px rgba(44, 24, 16, 0.15),
                inset 0 1px 0 rgba(255, 255, 255, 0.8);
        }

        .btn-undo:hover:not(:disabled) {
            background: linear-gradient(145deg, #faf6f0 0%, #e4d8c8 100%);
            transform: translateY(-2px);
            box-shadow: 
                0 6px 16px rgba(44, 24, 16, 0.2),
                inset 0 1px 0 rgba(255, 255, 255, 0.8);
        }

        .btn-undo:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .info-box {
            background: linear-gradient(145deg, #faf6f0 0%, #f0e6d8 100%);
            border: 2px solid #c4a77d;
            border-radius: 8px;
            padding: 16px;
            box-shadow: 
                0 4px 12px rgba(44, 24, 16, 0.1),
                inset 0 1px 0 rgba(255, 255, 255, 0.8);
        }

        .info-title {
            font-size: 0.8rem;
            color: #8b7355;
            margin-bottom: 12px;
            letter-spacing: 2px;
            border-bottom: 1px solid #d4c8b8;
            padding-bottom: 8px;
        }

        .move-count {
            font-size: 0.9rem;
            color: #5a4a3a;
        }

        .thinking-indicator {
            display: none;
            align-items: center;
            gap: 8px;
            font-size: 0.85rem;
            color: #8b7355;
        }

        .thinking-indicator.active {
            display: flex;
        }

        .thinking-dots {
            display: flex;
            gap: 3px;
        }

        .thinking-dots span {
            width: 6px;
            height: 6px;
            background: #8b7355;
            border-radius: 50%;
            animation: bounce 1.4s infinite ease-in-out both;
        }

        .thinking-dots span:nth-child(1) { animation-delay: -0.32s; }
        .thinking-dots span:nth-child(2) { animation-delay: -0.16s; }
        .thinking-dots span:nth-child(3) { animation-delay: 0s; }

        @keyframes bounce {
            0%, 80%, 100% { transform: scale(0); }
            40% { transform: scale(1); }
        }

        .score-display {
            font-size: 0.8rem;
            color: #6b5344;
            margin-top: 8px;
            padding-top: 8px;
            border-top: 1px solid #d4c8b8;
        }

        .difficulty-select {
            margin-top: 12px;
            padding-top: 8px;
            border-top: 1px solid #d4c8b8;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .difficulty-select label {
            font-size: 0.8rem;
            color: #6b5344;
        }

        .difficulty-select select {
            flex: 1;
            padding: 6px 10px;
            font-family: 'Noto Serif SC', serif;
            font-size: 0.85rem;
            color: #2c1810;
            background: linear-gradient(to bottom, #faf6f0 0%, #f0e6d8 100%);
            border: 2px solid #c4a77d;
            border-radius: 4px;
            cursor: pointer;
            outline: none;
            transition: all 0.2s ease;
        }

        .difficulty-select select:hover {
            border-color: #8b7355;
            box-shadow: 0 2px 4px rgba(44, 24, 16, 0.1);
        }

        .difficulty-select select:focus {
            border-color: #6b5344;
            box-shadow: 0 2px 6px rgba(44, 24, 16, 0.15);
        }

        @media (max-width: 768px) {
            .game-wrapper {
                flex-direction: column;
                align-items: center;
            }

            .side-panel {
                flex-direction: row;
                flex-wrap: wrap;
                justify-content: center;
                min-width: auto;
            }

            .button-group {
                flex-direction: row;
            }

            .title {
                font-size: 2.4rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1 class="title">墨韵五子棋</h1>
        </div>

        <div class="game-wrapper">
            <div class="board-container">
                <canvas id="gameCanvas" width="540" height="540"></canvas>
            </div>

            <div class="side-panel">
                <div class="status-box">
                    <div class="status-label">当前状态</div>
                    <div class="status-text player-turn" id="statusText">
                        <span class="stone-indicator black"></span>
                        您的回合
                    </div>
                    <div class="thinking-indicator" id="thinkingIndicator">
                        <div class="thinking-dots">
                            <span></span><span></span><span></span>
                        </div>
                        AI 思考中...
                    </div>
                </div>

                <div class="button-group">
                    <button class="btn btn-restart" id="btnRestart">重新开始</button>
                    <button class="btn btn-undo" id="btnUndo" disabled>悔 棋</button>
                </div>

                <div class="info-box">
                    <div class="info-title">对局信息</div>
                    <div class="move-count">步数: <span id="moveCount">0</span></div>
                    <div class="difficulty-select">
                        <label for="difficultySelect">AI 难度:</label>
                        <select id="difficultySelect">
                            <option value="easy">入门</option>
                            <option value="medium" selected>进阶</option>
                            <option value="hard">大师</option>
                        </select>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        const BOARD_SIZE = 15;
        const CELL_SIZE = 36;
        const PADDING = 18;
        const EMPTY = 0;
        const BLACK = 1;
        const WHITE = 2;

        const SCORES = {
            LIVE_FOUR: 100000,
            FOUR: 10000,
            LIVE_THREE: 1000,
            SLEEP_THREE: 100,
            LIVE_TWO: 10,
            SLEEP_TWO: 1
        };

        const DIRECTIONS = [
            [1, 0],
            [0, 1],
            [1, 1],
            [1, -1]
        ];

        let board = [];
        let gameStatus = 'playing';
        let currentPlayer = BLACK;
        let moveHistory = [];
        let animationQueue = [];

        const DIFFICULTY_CONFIG = {
            easy: { depth: 0, candidateCount: 3 },
            medium: { depth: 2, candidateCount: 6 },
            hard: { depth: 4, candidateCount: 8 }
        };
        let currentDifficulty = 'medium';

        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const statusText = document.getElementById('statusText');
        const moveCountEl = document.getElementById('moveCount');
        const btnRestart = document.getElementById('btnRestart');
        const btnUndo = document.getElementById('btnUndo');
        const thinkingIndicator = document.getElementById('thinkingIndicator');
        const difficultySelect = document.getElementById('difficultySelect');

        function initBoard() {
            board = [];
            for (let i = 0; i < BOARD_SIZE; i++) {
                board[i] = [];
                for (let j = 0; j < BOARD_SIZE; j++) {
                    board[i][j] = EMPTY;
                }
            }
        }

        function drawBoard() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            ctx.fillStyle = '#deb887';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            ctx.strokeStyle = '#4a3728';
            ctx.lineWidth = 1.2;

            for (let i = 0; i < BOARD_SIZE; i++) {
                ctx.beginPath();
                ctx.moveTo(PADDING, PADDING + i * CELL_SIZE);
                ctx.lineTo(PADDING + (BOARD_SIZE - 1) * CELL_SIZE, PADDING + i * CELL_SIZE);
                ctx.stroke();

                ctx.beginPath();
                ctx.moveTo(PADDING + i * CELL_SIZE, PADDING);
                ctx.lineTo(PADDING + i * CELL_SIZE, PADDING + (BOARD_SIZE - 1) * CELL_SIZE);
                ctx.stroke();
            }

            const starPoints = [
                [3, 3], [11, 3], [7, 7],
                [3, 11], [11, 11]
            ];
            
            ctx.fillStyle = '#2c1810';
            for (const [x, y] of starPoints) {
                ctx.beginPath();
                ctx.arc(PADDING + x * CELL_SIZE, PADDING + y * CELL_SIZE, 4, 0, Math.PI * 2);
                ctx.fill();
            }
        }

        function drawPiece(x, y, color, animated = false) {
            const px = PADDING + x * CELL_SIZE;
            const py = PADDING + y * CELL_SIZE;

            if (animated) {
                animationQueue.push({ x, y, color, progress: 0 });
                return;
            }

            drawPieceImmediate(px, py, color);
        }

        function drawPieceImmediate(px, py, color) {
            ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
            ctx.shadowBlur = 4;
            ctx.shadowOffsetX = 2;
            ctx.shadowOffsetY = 2;

            const gradient = ctx.createRadialGradient(
                px - 4, py - 4, 0,
                px, py, CELL_SIZE / 2 - 2
            );

            if (color === BLACK) {
                gradient.addColorStop(0, '#5a5a5a');
                gradient.addColorStop(0.3, '#3a3a3a');
                gradient.addColorStop(1, '#1a1a1a');
            } else {
                gradient.addColorStop(0, '#ffffff');
                gradient.addColorStop(0.3, '#f0f0f0');
                gradient.addColorStop(1, '#d0d0d0');
            }

            ctx.beginPath();
            ctx.arc(px, py, CELL_SIZE / 2 - 3, 0, Math.PI * 2);
            ctx.fillStyle = gradient;
            ctx.fill();

            ctx.shadowColor = 'transparent';
            ctx.shadowBlur = 0;
            ctx.shadowOffsetX = 0;
            ctx.shadowOffsetY = 0;
        }

        function animatePieces() {
            if (animationQueue.length === 0) return;

            const animation = animationQueue[0];
            animation.progress += 0.15;

            if (animation.progress >= 1) {
                drawPieceImmediate(
                    PADDING + animation.x * CELL_SIZE,
                    PADDING + animation.y * CELL_SIZE,
                    animation.color
                );
                animationQueue.shift();
                if (animationQueue.length > 0) {
                    requestAnimationFrame(animatePieces);
                }
                return;
            }

            drawBoard();
            drawAllPieces();

            const px = PADDING + animation.x * CELL_SIZE;
            const py = PADDING + animation.y * CELL_SIZE;
            const scale = easeOutBack(animation.progress);

            ctx.save();
            ctx.translate(px, py);
            ctx.scale(scale, scale);
            ctx.translate(-px, -py);

            drawPieceImmediate(px, py, animation.color);
            ctx.restore();

            requestAnimationFrame(animatePieces);
        }

        function easeOutBack(x) {
            const c1 = 1.70158;
            const c3 = c1 + 1;
            return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
        }

        function drawAllPieces() {
            for (let i = 0; i < BOARD_SIZE; i++) {
                for (let j = 0; j < BOARD_SIZE; j++) {
                    if (board[i][j] !== EMPTY) {
                        drawPieceImmediate(
                            PADDING + i * CELL_SIZE,
                            PADDING + j * CELL_SIZE,
                            board[i][j]
                        );
                    }
                }
            }
        }

        function getBoardPosition(clientX, clientY) {
            const rect = canvas.getBoundingClientRect();
            const x = Math.round((clientX - rect.left - PADDING) / CELL_SIZE);
            const y = Math.round((clientY - rect.top - PADDING) / CELL_SIZE);

            if (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE) {
                return { x, y };
            }
            return null;
        }

        function checkWin(x, y, color) {
            for (const [dx, dy] of DIRECTIONS) {
                let count = 1;

                for (let i = 1; i < 5; i++) {
                    const nx = x + dx * i;
                    const ny = y + dy * i;
                    if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] === color) {
                        count++;
                    } else {
                        break;
                    }
                }

                for (let i = 1; i < 5; i++) {
                    const nx = x - dx * i;
                    const ny = y - dy * i;
                    if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE && board[nx][ny] === color) {
                        count++;
                    } else {
                        break;
                    }
                }

                if (count >= 5) {
                    return true;
                }
            }
            return false;
        }

        function isBoardFull() {
            for (let i = 0; i < BOARD_SIZE; i++) {
                for (let j = 0; j < BOARD_SIZE; j++) {
                    if (board[i][j] === EMPTY) {
                        return false;
                    }
                }
            }
            return true;
        }

        function countPattern(line, player) {
            const opponent = player === BLACK ? WHITE : BLACK;
            let count = 0;
            let openEnds = 0;

            let temp = '';
            for (const cell of line) {
                if (cell === player) {
                    temp += '1';
                } else if (cell === opponent) {
                    temp += '2';
                } else {
                    temp += '0';
                }
            }

            const regex = /1{5}/g;
            if (regex.test(temp)) {
                return { type: 'FIVE', score: SCORES.LIVE_FOUR * 10 };
            }

            const liveFourRegex = /011110|011110/;
            if (liveFourRegex.test(temp)) {
                return { type: 'LIVE_FOUR', score: SCORES.LIVE_FOUR };
            }

            const fourRegex1 = /211110|011112|11011|101101|011011/;
            const fourRegex2 = /11110|01111|10111|11011|11101/;
            if (fourRegex1.test(temp) || fourRegex2.test(temp)) {
                return { type: 'FOUR', score: SCORES.FOUR };
            }

            const liveThreeRegex = /011100|001110|011010|010110|11100|00111/;
            if (liveThreeRegex.test(temp)) {
                return { type: 'LIVE_THREE', score: SCORES.LIVE_THREE };
            }

            const sleepThreeRegex = /211100|001112|010100|001010|100110|011001|110100|001101|101100|011002/;
            if (sleepThreeRegex.test(temp)) {
                return { type: 'SLEEP_THREE', score: SCORES.SLEEP_THREE };
            }

            const liveTwoRegex = /001100|011000|000110|010010|010100|001010/;
            if (liveTwoRegex.test(temp)) {
                return { type: 'LIVE_TWO', score: SCORES.LIVE_TWO };
            }

            const sleepTwoRegex = /001000|000100|000010|100010|010001|100100|001001|010100|001010/;
            if (sleepTwoRegex.test(temp)) {
                return { type: 'SLEEP_TWO', score: SCORES.SLEEP_TWO };
            }

            return { type: 'NONE', score: 0 };
        }

        function getLine(x, y, dx, dy, length = 11) {
            const line = [];
            const startX = x - 5 * dx;
            const startY = y - 5 * dy;

            for (let i = 0; i < length; i++) {
                const nx = startX + i * dx;
                const ny = startY + i * dy;

                if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE) {
                    line.push(board[nx][ny]);
                } else {
                    line.push(-1);
                }
            }

            return line;
        }

        function evaluatePosition(x, y, player) {
            if (board[x][y] !== EMPTY) return 0;

            let totalScore = 0;

            for (const [dx, dy] of DIRECTIONS) {
                const line = getLine(x, y, dx, dy);
                const result = countPattern(line, player);
                totalScore += result.score;
            }

            const centerX = Math.abs(x - 7);
            const centerY = Math.abs(y - 7);
            const distanceFromCenter = centerX + centerY;
            const centerBonus = Math.max(0, 10 - distanceFromCenter) * 0.5;
            totalScore += centerBonus;

            return totalScore;
        }

        function evaluateBoard(player) {
            let totalScore = 0;

            for (let i = 0; i < BOARD_SIZE; i++) {
                for (let j = 0; j < BOARD_SIZE; j++) {
                    if (board[i][j] === EMPTY) {
                        totalScore += evaluatePosition(i, j, player);
                    }
                }
            }

            return totalScore;
        }

        function getAllEmptyPositions() {
            const positions = [];
            for (let i = 0; i < BOARD_SIZE; i++) {
                for (let j = 0; j < BOARD_SIZE; j++) {
                    if (board[i][j] === EMPTY) {
                        const score = evaluatePosition(i, j, WHITE) + evaluatePosition(i, j, BLACK) * 0.9;
                        positions.push({ x: i, y: j, score });
                    }
                }
            }

            positions.sort((a, b) => b.score - a.score);
            return positions.slice(0, 15);
        }

        function minimax(depth, alpha, beta, isMaximizing, player) {
            if (depth === 0) {
                return evaluateBoard(WHITE) - evaluateBoard(BLACK);
            }

            const moves = getAllEmptyPositions();

            if (moves.length === 0) {
                return evaluateBoard(WHITE) - evaluateBoard(BLACK);
            }

            let bestScore = isMaximizing ? -Infinity : Infinity;

            for (const move of moves) {
                board[move.x][move.y] = player;

                const winMove = checkWin(move.x, move.y, player);
                
                if (winMove) {
                    board[move.x][move.y] = EMPTY;
                    return isMaximizing ? 1000000 + depth : -1000000 - depth;
                }

                const score = minimax(
                    depth - 1,
                    alpha,
                    beta,
                    !isMaximizing,
                    player === WHITE ? BLACK : WHITE
                );

                board[move.x][move.y] = EMPTY;

                if (isMaximizing) {
                    bestScore = Math.max(bestScore, score);
                    alpha = Math.max(alpha, score);
                    if (beta <= alpha) break;
                } else {
                    bestScore = Math.min(bestScore, score);
                    beta = Math.min(beta, score);
                    if (beta <= alpha) break;
                }
            }

            return bestScore;
        }

        function aiMove() {
            thinkingIndicator.classList.add('active');

            setTimeout(() => {
                const moves = getAllEmptyPositions();
                
                if (moves.length === 0) {
                    gameStatus = 'draw';
                    updateStatus('平局!');
                    thinkingIndicator.classList.remove('active');
                    return;
                }

                const config = DIFFICULTY_CONFIG[currentDifficulty];
                let bestMove;

                if (config.depth === 0) {
                    const topMoves = moves.slice(0, config.candidateCount);
                    bestMove = topMoves[Math.floor(Math.random() * topMoves.length)];
                } else {
                    bestMove = moves[0];
                    let bestScore = -Infinity;
                    const alpha = -Infinity;
                    const beta = Infinity;

                    const startDepth = Math.min(config.depth, moves.length);

                    for (const move of moves.slice(0, config.candidateCount)) {
                        board[move.x][move.y] = WHITE;

                        if (checkWin(move.x, move.y, WHITE)) {
                            board[move.x][move.y] = EMPTY;
                            bestMove = move;
                            break;
                        }

                        const score = minimax(
                            startDepth - 1,
                            alpha,
                            beta,
                            false,
                            BLACK
                        );

                        board[move.x][move.y] = EMPTY;

                        if (score > bestScore) {
                            bestScore = score;
                            bestMove = move;
                        }
                    }
                }

                makeMove(bestMove.x, bestMove.y, WHITE);
                thinkingIndicator.classList.remove('active');
            }, 100);
        }

        function makeMove(x, y, player) {
            if (gameStatus !== 'playing') return;
            if (board[x][y] !== EMPTY) return;

            board[x][y] = player;
            moveHistory.push({ x, y, player });

            drawBoard();
            drawAllPieces();
            animatePieces();

            moveCountEl.textContent = moveHistory.length;
            btnUndo.disabled = false;

            if (checkWin(x, y, player)) {
                gameStatus = player === BLACK ? 'black_win' : 'white_win';
                updateStatus(player === BLACK ? '恭喜,您赢了!' : 'AI 获胜!', 'win');
                return;
            }

            if (isBoardFull()) {
                gameStatus = 'draw';
                updateStatus('平局!', 'win');
                return;
            }

            currentPlayer = currentPlayer === BLACK ? WHITE : BLACK;

            if (currentPlayer === WHITE) {
                updateStatus('AI 思考中...', 'ai-turn');
                aiMove();
            } else {
                updateStatus('您的回合', 'player-turn');
            }
        }

        function updateStatus(text, className = '') {
            statusText.className = 'status-text ' + className;

            if (className === 'player-turn') {
                statusText.innerHTML = '<span class="stone-indicator black"></span> 您的回合';
            } else if (className === 'ai-turn') {
                statusText.innerHTML = '<span class="stone-indicator white"></span> AI 思考中...';
            } else if (className === 'win') {
                statusText.textContent = text;
            } else {
                statusText.textContent = text;
            }
        }

        function restartGame() {
            initBoard();
            gameStatus = 'playing';
            currentPlayer = BLACK;
            moveHistory = [];
            animationQueue = [];

            drawBoard();
            updateStatus('您的回合', 'player-turn');
            moveCountEl.textContent = '0';
            btnUndo.disabled = true;
            thinkingIndicator.classList.remove('active');
        }

        function undoMove() {
            if (moveHistory.length < 2) return;

            let lastMove = moveHistory.pop();
            board[lastMove.x][lastMove.y] = EMPTY;

            lastMove = moveHistory.pop();
            board[lastMove.x][lastMove.y] = EMPTY;

            gameStatus = 'playing';
            currentPlayer = BLACK;

            drawBoard();
            drawAllPieces();
            updateStatus('您的回合', 'player-turn');
            moveCountEl.textContent = moveHistory.length;
            btnUndo.disabled = moveHistory.length < 2;
            thinkingIndicator.classList.remove('active');
        }

        canvas.addEventListener('click', (e) => {
            if (gameStatus !== 'playing') return;
            if (currentPlayer !== BLACK) return;

            const pos = getBoardPosition(e.clientX, e.clientY);
            if (pos && board[pos.x][pos.y] === EMPTY) {
                makeMove(pos.x, pos.y, BLACK);
            }
        });

        btnRestart.addEventListener('click', restartGame);
        btnUndo.addEventListener('click', undoMove);

        difficultySelect.addEventListener('change', (e) => {
            currentDifficulty = e.target.value;
        });

        initBoard();
        drawBoard();
    </script>
</body>
</html>

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐