脚本网页 双子星棋

功能,摸鱼小游戏,

可以集成到个人网页或APP

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
    <title>双子星棋</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }
        
        body {
            font-family: 'PingFang SC', 'Microsoft YaHei', -apple-system, sans-serif;
            background: linear-gradient(180deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
            /* 确保占据整个高度,不溢出 */
            height: 100vh;
            width: 100vw;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 8px 5px 5px 5px;
            overflow: hidden; /* 禁止滚动 */
        }

        /* 顶部统计栏 */
        .stats-bar {
            width: 96%;
            max-width: 380px;
            background: rgba(255, 255, 255, 0.05);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            padding: 5px 12px;
            margin-bottom: 5px;
            display: flex;
            justify-content: space-between;
            font-size: 0.75rem;
            color: rgba(255, 255, 255, 0.8);
            font-weight: 500;
        }
        
        .stat-item span {
            font-weight: bold;
            margin-left: 2px;
        }
        
        .game-title {
            color: #ffd700;
            font-size: 1.5rem;
            margin-bottom: 2px;
            font-weight: 800;
            letter-spacing: 1px;
            text-shadow: 0 0 15px rgba(255, 215, 0, 0.4);
        }
        
        .game-info {
            background: rgba(255, 255, 255, 0.08);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border-radius: 12px;
            padding: 6px 15px;
            margin-bottom: 6px;
            color: white;
            text-align: center;
            width: 96%;
            max-width: 380px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .turn-indicator {
            font-size: 0.9rem;
            font-weight: 600;
        }
        
        .current-turn {
            display: inline-block;
            padding: 3px 10px;
            border-radius: 12px;
            transition: all 0.3s ease;
        }
        
        .current-turn.player {
            background: linear-gradient(135deg, #667eea, #764ba2);
            box-shadow: 0 0 10px rgba(102, 126, 234, 0.4);
        }
        
        .current-turn.ai {
            background: linear-gradient(135deg, #f093fb, #f5576c);
            box-shadow: 0 0 10px rgba(245, 87, 108, 0.4);
        }
        
        .live-score {
            font-size: 1.1rem;
            font-weight: bold;
            display: flex;
            gap: 10px;
        }
        
        .score-val { font-family: monospace; }
        
        .board-container {
            position: relative;
            /* 动态计算宽度,确保不溢出 */
            width: 94vw; 
            max-width: 350px;
            aspect-ratio: 1;
            margin-bottom: 6px;
            flex-shrink: 0; /* 防止被压缩 */
        }
        
        .board-glow {
            position: absolute;
            inset: -4px;
            background: linear-gradient(135deg, #667eea, #764ba2, #f093fb, #f5576c);
            border-radius: 18px;
            filter: blur(10px);
            opacity: 0.3;
            z-index: 0;
        }
        
        .board {
            position: relative;
            display: grid;
            grid-template-columns: repeat(6, 1fr);
            grid-template-rows: repeat(6, 1fr);
            gap: 2px;
            width: 100%;
            height: 100%;
            background: linear-gradient(145deg, #1a1a2e, #0d0d1a);
            border-radius: 14px;
            padding: 6px;
            z-index: 1;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
        }
        
        .cell {
            background: linear-gradient(145deg, #2a2a4a, #1a1a3a);
            border-radius: 6px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            transition: all 0.2s ease;
            position: relative;
        }
        
        .cell:nth-child(odd) {
            background: linear-gradient(145deg, #252545, #151535);
        }
        
        .cell:active {
            transform: scale(0.95);
        }
        
        .cell.selected {
            box-shadow: inset 0 0 15px rgba(102, 126, 234, 0.8);
            border: 2px solid #667eea;
        }
        
        .cell.valid-move {
            box-shadow: inset 0 0 12px rgba(46, 213, 115, 0.5);
            animation: pulse-green 1.2s infinite;
        }
        
        .cell.valid-move::after {
            content: '';
            position: absolute;
            width: 25%;
            height: 25%;
            background: rgba(46, 213, 115, 0.6);
            border-radius: 50%;
            animation: fadeInOut 1s infinite;
        }
        
        @keyframes pulse-green {
            0%, 100% { box-shadow: inset 0 0 12px rgba(46, 213, 115, 0.5); }
            50% { box-shadow: inset 0 0 18px rgba(46, 213, 115, 0.8); }
        }
        
        @keyframes fadeInOut {
            0%, 100% { opacity: 0.3; transform: scale(1); }
            50% { opacity: 0.8; transform: scale(1.2); }
        }
        
        .piece {
            width: 80%;
            height: 80%;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 1.2rem; /* 稍微调小一点棋子字体 */
            transition: all 0.3s ease;
            position: relative;
            -webkit-user-select: none;
            user-select: none;
        }
        
        .piece.player {
            background: linear-gradient(145deg, #667eea, #5a67d8);
            box-shadow: 0 3px 10px rgba(102, 126, 234, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
        }
        
        .piece.ai {
            background: linear-gradient(145deg, #f5576c, #e63946);
            box-shadow: 0 3px 10px rgba(245, 87, 108, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
        }
        
        .piece.twinstar {
            font-size: 1.5rem;
            animation: twinkle 2s infinite alternate;
        }
        
        .piece.player.twinstar {
            box-shadow: 0 0 15px rgba(102, 126, 234, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3);
        }
        
        .piece.ai.twinstar {
            box-shadow: 0 0 15px rgba(245, 87, 108, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3);
        }
        
        @keyframes twinkle {
            0% { filter: brightness(1); }
            100% { filter: brightness(1.3); }
        }
        
        .piece.guard { font-size: 1.3rem; }
        .piece.soldier { font-size: 1rem; }
        
        .piece.animate-move {
            animation: movePiece 0.3s ease-out;
        }
        
        @keyframes movePiece {
            0% { transform: scale(1.3); opacity: 0.7; }
            100% { transform: scale(1); opacity: 1; }
        }
        
        .goal-marker {
            position: absolute;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            border: 2px dashed;
            opacity: 0.4;
        }
        
        .goal-marker.player-goal { border-color: #667eea; bottom: 10px; }
        .goal-marker.ai-goal { border-color: #f5576c; top: 10px; }
        
        /* 底部控制区紧凑排列 */
        .controls-area {
            display: flex;
            flex-direction: column;
            gap: 6px;
            width: 100%;
            align-items: center;
            margin-top: auto; /* 推到底部 */
            padding-bottom: 5px;
        }

        .difficulty-selector {
            display: flex;
            gap: 6px;
            justify-content: center;
            width: 100%;
        }
        
        .difficulty-btn {
            padding: 4px 10px;
            font-size: 0.75rem;
            background: rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            color: rgba(255, 255, 255, 0.7);
            cursor: pointer;
            transition: all 0.2s ease;
        }
        
        .difficulty-btn.active {
            background: linear-gradient(135deg, #ffd700, #ffaa00);
            color: #0f0c29;
            border-color: transparent;
            font-weight: bold;
            box-shadow: 0 0 10px rgba(255, 215, 0, 0.3);
        }
        
        .main-controls {
            display: flex;
            gap: 10px;
        }
        
        .btn {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            padding: 8px 18px;
            border-radius: 20px;
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s ease;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
        }
        
        .btn:active { transform: scale(0.95); }
        
        .btn.rules-btn {
            background: linear-gradient(135deg, #f093fb, #f5576c);
            box-shadow: 0 4px 15px rgba(245, 87, 108, 0.3);
        }
        
        /* 图例简化,缩小字体 */
        .piece-legend {
            font-size: 0.7rem;
            color: rgba(255, 255, 255, 0.5);
            margin-bottom: 2px;
            display: flex;
            gap: 10px;
        }

        /* 弹窗保持原样 */
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.85);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            justify-content: center;
            align-items: center;
            z-index: 1000;
            padding: 20px;
        }
        
        .modal-content {
            background: linear-gradient(145deg, #1a1a2e, #0d0d1a);
            border-radius: 20px;
            padding: 25px;
            text-align: center;
            color: white;
            max-width: 340px;
            width: 100%;
            animation: modalIn 0.3s ease;
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        @keyframes modalIn {
            from { opacity: 0; transform: scale(0.9); }
            to { opacity: 1; transform: scale(1); }
        }
        
        .modal h2 { font-size: 1.4rem; margin-bottom: 12px; }
        .modal p { margin-bottom: 15px; font-size: 0.95rem; opacity: 0.9; line-height: 1.5; }
        
        .rules-content {
            text-align: left;
            max-height: 55vh;
            overflow-y: auto;
            padding-right: 5px;
        }
        
        .rules-content h3 {
            color: #ffd700;
            margin: 12px 0 6px 0;
            font-size: 0.95rem;
        }
        
        .rules-content ul { padding-left: 15px; }
        .rules-content li { font-size: 0.85rem; margin-bottom: 4px; line-height: 1.4; }
        
        .close-btn {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            padding: 10px 25px;
            border-radius: 20px;
            margin-top: 10px;
            font-weight: 600;
            font-size: 0.9rem;
        }
        
        .thinking {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 15px 25px;
            border-radius: 12px;
            z-index: 500;
            display: none;
            align-items: center;
            gap: 10px;
            font-size: 0.9rem;
        }
        
        .thinking-spinner {
            width: 20px;
            height: 20px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-top-color: #ffd700;
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
        }
        
        @keyframes spin { to { transform: rotate(360deg); } }
    </style>
</head>
<body>
    <!-- 顶部数据统计栏 -->
    <div class="stats-bar">
        <div class="stat-item">📊 <span id="totalGames">0</span></div>
        <div class="stat-item" style="color: #7bed9f;">✅ 胜 <span id="totalWins">0</span></div>
        <div class="stat-item" style="color: #ff6b6b;">❌ 负 <span id="totalLosses">0</span></div>
    </div>

    <h1 class="game-title">⭐ 双子星棋</h1>
    
    <!-- 当局状态栏 -->
    <div class="game-info">
        <div class="turn-indicator">
            <span class="current-turn player" id="currentTurn">👤 玩家</span>
        </div>
        <div class="live-score">
            <span style="color: #7ca5f8;" class="score-val" id="playerScore">0</span>
            <span>:</span>
            <span style="color: #ff6b81;" class="score-val" id="aiScore">0</span>
        </div>
    </div>
    
    <div class="board-container">
        <div class="board-glow"></div>
        <div class="board" id="board">
            <div class="goal-marker player-goal" style="left: 12px;"></div>
            <div class="goal-marker player-goal" style="right: 12px;"></div>
            <div class="goal-marker ai-goal" style="left: 12px;"></div>
            <div class="goal-marker ai-goal" style="right: 12px;"></div>
        </div>
    </div>
    
    <div class="piece-legend">
        <span>⭐双子星</span>
        <span>🛡️护卫</span>
        <span>⚔️士兵</span>
    </div>
    
    <div class="controls-area">
        <div class="difficulty-selector">
            <button class="difficulty-btn active" data-level="1">🌱 新手</button>
            <button class="difficulty-btn" data-level="2">⚡ 进阶</button>
            <button class="difficulty-btn" data-level="3">🔥 高手</button>
            <button class="difficulty-btn" data-level="4">💎 大师</button>
        </div>
        
        <div class="main-controls">
            <button class="btn" onclick="resetGame()">🔄 重开</button>
            <button class="btn rules-btn" onclick="showRules()">📖 规则</button>
        </div>
    </div>
    
    <div class="thinking" id="thinking">
        <div class="thinking-spinner"></div>
        <span>AI思考中...</span>
    </div>
    
    <!-- 规则弹窗 -->
    <div class="modal" id="rulesModal">
        <div class="modal-content">
            <h2>📖 游戏规则</h2>
            <div class="rules-content">
                <h3>🎯 胜利条件</h3>
                <ul>
                    <li>吃掉对方双子星(⭐)</li>
                    <li>双子星到达对角目标</li>
                </ul>
                <h3>♟️ 棋子移动</h3>
                <ul>
                    <li><strong>⭐双子星</strong>:上下左右1格,或跳过相邻棋子</li>
                    <li><strong>🛡️护卫</strong>:八方移动1格</li>
                    <li><strong>⚔️士兵</strong>:上下左右1格</li>
                </ul>
            </div>
            <button class="close-btn" onclick="hideRules()">关闭</button>
        </div>
    </div>
    
    <!-- 结束弹窗 -->
    <div class="modal" id="gameOverModal">
        <div class="modal-content">
            <h2 id="gameOverTitle">🎉 游戏结束!</h2>
            <p id="gameOverMessage"></p>
            <button class="btn" onclick="resetGame()">🔄 再来一局</button>
        </div>
    </div>

    <script>
        // 游戏常量与逻辑基本不变,主要调整UI交互
        const BOARD_SIZE = 6;
        const PIECES = {
            EMPTY: null,
            PLAYER_TWINSTAR: 'PT',
            PLAYER_GUARD: 'PG',
            PLAYER_SOLDIER: 'PS',
            AI_TWINSTAR: 'AT',
            AI_GUARD: 'AG',
            AI_SOLDIER: 'AS'
        };

        const PIECE_INFO = {
            [PIECES.PLAYER_TWINSTAR]: { symbol: '⭐', type: 'twinstar', owner: 'player' },
            [PIECES.PLAYER_GUARD]: { symbol: '🛡️', type: 'guard', owner: 'player' },
            [PIECES.PLAYER_SOLDIER]: { symbol: '⚔️', type: 'soldier', owner: 'player' },
            [PIECES.AI_TWINSTAR]: { symbol: '⭐', type: 'twinstar', owner: 'ai' },
            [PIECES.AI_GUARD]: { symbol: '🛡️', type: 'guard', owner: 'ai' },
            [PIECES.AI_SOLDIER]: { symbol: '⚔️', type: 'soldier', owner: 'ai' }
        };

        const PIECE_VALUES = {
            [PIECES.PLAYER_TWINSTAR]: 1000,
            [PIECES.PLAYER_GUARD]: 35,
            [PIECES.PLAYER_SOLDIER]: 15,
            [PIECES.AI_TWINSTAR]: 1000,
            [PIECES.AI_GUARD]: 35,
            [PIECES.AI_SOLDIER]: 15
        };

        const PLAYER_GOALS = [[5, 0], [5, 5]];
        const AI_GOALS = [[0, 0], [0, 5]];

        let board = [];
        let currentPlayer = 'player';
        let selectedPiece = null;
        let validMoves = [];
        let gameOver = false;
        let difficulty = 1;

        // 持久化数据
        let stats = {
            total: 0,
            wins: 0,
            losses: 0
        };

        // 加载本地存储
        function loadStats() {
            const saved = localStorage.getItem('twinStarStats');
            if (saved) {
                try {
                    stats = JSON.parse(saved);
                } catch (e) {
                    console.error("存档损坏");
                }
            }
            updateStatsUI();
        }

        function saveStats() {
            localStorage.setItem('twinStarStats', JSON.stringify(stats));
        }

        function updateStatsUI() {
            document.getElementById('totalGames').textContent = stats.total;
            document.getElementById('totalWins').textContent = stats.wins;
            document.getElementById('totalLosses').textContent = stats.losses;
        }

        function initBoard() {
            board = Array(BOARD_SIZE).fill(null).map(() => Array(BOARD_SIZE).fill(PIECES.EMPTY));
            board[0][2] = PIECES.AI_TWINSTAR;
            board[0][3] = PIECES.AI_TWINSTAR;
            board[1][1] = PIECES.AI_GUARD;
            board[1][4] = PIECES.AI_GUARD;
            board[2][0] = PIECES.AI_SOLDIER;
            board[2][5] = PIECES.AI_SOLDIER;
            board[5][2] = PIECES.PLAYER_TWINSTAR;
            board[5][3] = PIECES.PLAYER_TWINSTAR;
            board[4][1] = PIECES.PLAYER_GUARD;
            board[4][4] = PIECES.PLAYER_GUARD;
            board[3][0] = PIECES.PLAYER_SOLDIER;
            board[3][5] = PIECES.PLAYER_SOLDIER;
        }

        function renderBoard() {
            const boardEl = document.getElementById('board');
            const markers = boardEl.querySelectorAll('.goal-marker');
            boardEl.innerHTML = '';
            markers.forEach(m => boardEl.appendChild(m));
            
            for (let row = 0; row < BOARD_SIZE; row++) {
                for (let col = 0; col < BOARD_SIZE; col++) {
                    const cell = document.createElement('div');
                    cell.className = 'cell';
                    cell.dataset.row = row;
                    cell.dataset.col = col;
                    
                    if (selectedPiece && selectedPiece.row === row && selectedPiece.col === col) {
                        cell.classList.add('selected');
                    }
                    
                    const isValidMove = validMoves.some(move => move.row === row && move.col === col);
                    if (isValidMove) {
                        cell.classList.add('valid-move');
                    }
                    
                    const piece = board[row][col];
                    if (piece) {
                        const pieceEl = document.createElement('div');
                        const info = PIECE_INFO[piece];
                        pieceEl.className = `piece ${info.owner} ${info.type}`;
                        pieceEl.textContent = info.symbol;
                        cell.appendChild(pieceEl);
                    }
                    
                    cell.addEventListener('click', () => handleCellClick(row, col));
                    cell.addEventListener('touchend', (e) => {
                        e.preventDefault();
                        handleCellClick(row, col);
                    }, { passive: false });
                    
                    boardEl.appendChild(cell);
                }
            }
        }

        function handleCellClick(row, col) {
            if (gameOver || currentPlayer !== 'player') return;
            const clickedPiece = board[row][col];
            const isPlayerPiece = clickedPiece && PIECE_INFO[clickedPiece].owner === 'player';
            
            if (isPlayerPiece) {
                selectedPiece = { row, col };
                validMoves = getValidMoves(board, row, col);
                if (navigator.vibrate) navigator.vibrate(5);
                renderBoard();
                return;
            }
            
            if (selectedPiece) {
                const isValidMove = validMoves.some(move => move.row === row && move.col === col);
                if (isValidMove) {
                    makeMove(board, selectedPiece.row, selectedPiece.col, row, col);
                    selectedPiece = null;
                    validMoves = [];
                    if (navigator.vibrate) navigator.vibrate(10);
                    renderBoard();
                    if (checkGameOver(board)) return;
                    currentPlayer = 'ai';
                    updateTurnIndicator();
                    setTimeout(() => aiMove(), 300);
                } else {
                    selectedPiece = null;
                    validMoves = [];
                    renderBoard();
                }
            }
        }

        function getValidMoves(currentBoard, row, col) {
            const piece = currentBoard[row][col];
            if (!piece) return [];
            const info = PIECE_INFO[piece];
            const moves = [];
            const basicDirections = [[-1, 0], [1, 0], [0, -1], [0, 1]];
            if (info.type === 'guard') {
                basicDirections.push([-1, -1], [-1, 1], [1, -1], [1, 1]);
            }
            for (const [dr, dc] of basicDirections) {
                const newRow = row + dr;
                const newCol = col + dc;
                if (isValidPosition(newRow, newCol)) {
                    const targetPiece = currentBoard[newRow][newCol];
                    if (!targetPiece || PIECE_INFO[targetPiece].owner !== info.owner) {
                        moves.push({ row: newRow, col: newCol });
                    }
                }
            }
            if (info.type === 'twinstar') {
                for (const [dr, dc] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) {
                    const midRow = row + dr;
                    const midCol = col + dc;
                    const jumpRow = row + dr * 2;
                    const jumpCol = col + dc * 2;
                    if (isValidPosition(jumpRow, jumpCol) && currentBoard[midRow][midCol] !== PIECES.EMPTY) {
                        const targetPiece = currentBoard[jumpRow][jumpCol];
                        if (!targetPiece || PIECE_INFO[targetPiece].owner !== info.owner) {
                            moves.push({ row: jumpRow, col: jumpCol });
                        }
                    }
                }
            }
            return moves;
        }

        function isValidPosition(row, col) {
            return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE;
        }

        function makeMove(currentBoard, fromRow, fromCol, toRow, toCol) {
            currentBoard[toRow][toCol] = currentBoard[fromRow][fromCol];
            currentBoard[fromRow][fromCol] = PIECES.EMPTY;
        }

        function cloneBoard(currentBoard) {
            return currentBoard.map(row => [...row]);
        }

        function getAllMoves(currentBoard, player) {
            const moves = [];
            for (let row = 0; row < BOARD_SIZE; row++) {
                for (let col = 0; col < BOARD_SIZE; col++) {
                    const piece = currentBoard[row][col];
                    if (piece && PIECE_INFO[piece].owner === player) {
                        const pieceMoves = getValidMoves(currentBoard, row, col);
                        for (const move of pieceMoves) {
                            moves.push({
                                from: { row, col },
                                to: { row: move.row, col: move.col },
                                pieceType: PIECE_INFO[piece].type
                            });
                        }
                    }
                }
            }
            return moves;
        }

        function evaluateBoard(currentBoard) {
            let score = 0;
            for (let row = 0; row < BOARD_SIZE; row++) {
                for (let col = 0; col < BOARD_SIZE; col++) {
                    const piece = currentBoard[row][col];
                    if (piece) {
                        const info = PIECE_INFO[piece];
                        let pieceValue = PIECE_VALUES[piece];
                        if (info.type === 'twinstar') {
                            if (info.owner === 'ai') {
                                pieceValue += row * 15; 
                            } else {
                                pieceValue += (BOARD_SIZE - 1 - row) * 15;
                            }
                        }
                        const centerDist = Math.abs(row - 2.5) + Math.abs(col - 2.5);
                        const posValue = (7 - centerDist) * 3;
                        
                        if (info.owner === 'ai') {
                            score += pieceValue + posValue;
                        } else {
                            score -= pieceValue + posValue;
                        }
                    }
                }
            }
            return score;
        }

        function checkGameOver(currentBoard) {
            let playerTwinstarExists = false;
            let aiTwinstarExists = false;
            
            for (let row = 0; row < BOARD_SIZE; row++) {
                for (let col = 0; col < BOARD_SIZE; col++) {
                    const piece = currentBoard[row][col];
                    if (piece === PIECES.PLAYER_TWINSTAR) {
                        playerTwinstarExists = true;
                        if (PLAYER_GOALS.some(g => g[0] === row && g[1] === col)) {
                            endGame('player', '双子星到达目标!');
                            return true;
                        }
                    }
                    if (piece === PIECES.AI_TWINSTAR) {
                        aiTwinstarExists = true;
                        if (AI_GOALS.some(g => g[0] === row && g[1] === col)) {
                            endGame('ai', 'AI双子星到达目标!');
                            return true;
                        }
                    }
                }
            }
            
            if (!playerTwinstarExists) {
                endGame('ai', '双子星被消灭!');
                return true;
            }
            if (!aiTwinstarExists) {
                endGame('player', '消灭了AI双子星!');
                return true;
            }
            return false;
        }

        function checkGameOverNoEnd(currentBoard) {
            let playerTwinstarExists = false;
            let aiTwinstarExists = false;
            for (let row = 0; row < BOARD_SIZE; row++) {
                for (let col = 0; col < BOARD_SIZE; col++) {
                    const piece = currentBoard[row][col];
                    if (piece === PIECES.PLAYER_TWINSTAR) playerTwinstarExists = true;
                    if (piece === PIECES.AI_TWINSTAR) aiTwinstarExists = true;
                }
            }
            return !playerTwinstarExists || !aiTwinstarExists;
        }

        function minimax(currentBoard, depth, alpha, beta, maximizingPlayer) {
            if (depth === 0 || checkGameOverNoEnd(currentBoard)) {
                return evaluateBoard(currentBoard);
            }
            const player = maximizingPlayer ? 'ai' : 'player';
            const moves = getAllMoves(currentBoard, player);
            if (moves.length === 0) return evaluateBoard(currentBoard);
            
            moves.sort((a, b) => {
                const aCapture = currentBoard[a.to.row][a.to.col] !== PIECES.EMPTY;
                const bCapture = currentBoard[b.to.row][b.to.col] !== PIECES.EMPTY;
                return (bCapture ? 1 : 0) - (aCapture ? 1 : 0);
            });
            
            if (maximizingPlayer) {
                let maxEval = -Infinity;
                for (const move of moves) {
                    const newBoard = cloneBoard(currentBoard);
                    makeMove(newBoard, move.from.row, move.from.col, move.to.row, move.to.col);
                    const evalScore = minimax(newBoard, depth - 1, alpha, beta, false);
                    maxEval = Math.max(maxEval, evalScore);
                    alpha = Math.max(alpha, evalScore);
                    if (beta <= alpha) break;
                }
                return maxEval;
            } else {
                let minEval = Infinity;
                for (const move of moves) {
                    const newBoard = cloneBoard(currentBoard);
                    makeMove(newBoard, move.from.row, move.from.col, move.to.row, move.to.col);
                    const evalScore = minimax(newBoard, depth - 1, alpha, beta, true);
                    minEval = Math.min(minEval, evalScore);
                    beta = Math.min(beta, evalScore);
                    if (beta <= alpha) break;
                }
                return minEval;
            }
        }

        function aiMove() {
            if (gameOver) return;
            document.getElementById('thinking').style.display = 'flex';
            
            setTimeout(() => {
                const moves = getAllMoves(board, 'ai');
                if (moves.length === 0) {
                    document.getElementById('thinking').style.display = 'none';
                    return;
                }
                let bestMove = null;
                let bestScore = -Infinity;
                const searchDepth = Math.min(difficulty + 2, 5);
                const moveScores = [];
                
                for (const move of moves) {
                    const newBoard = cloneBoard(board);
                    makeMove(newBoard, move.from.row, move.from.col, move.to.row, move.to.col);
                    const score = minimax(newBoard, searchDepth - 1, -Infinity, Infinity, false);
                    moveScores.push({ move, score });
                }
                
                const maxScore = Math.max(...moveScores.map(m => m.score));
                const bestMoves = moveScores.filter(m => m.score === maxScore);
                const selected = bestMoves[Math.floor(Math.random() * bestMoves.length)];
                bestMove = selected.move;
                
                if (bestMove) {
                    makeMove(board, bestMove.from.row, bestMove.from.col, 
                            bestMove.to.row, bestMove.to.col);
                    renderBoard();
                    if (navigator.vibrate) navigator.vibrate(10);
                    if (checkGameOver(board)) {
                        document.getElementById('thinking').style.display = 'none';
                        return;
                    }
                    currentPlayer = 'player';
                    updateTurnIndicator();
                }
                document.getElementById('thinking').style.display = 'none';
            }, 50);
        }

        function updateTurnIndicator() {
            const turnEl = document.getElementById('currentTurn');
            if (currentPlayer === 'player') {
                turnEl.textContent = '👤 玩家';
                turnEl.className = 'current-turn player';
            } else {
                turnEl.textContent = '🤖 AI';
                turnEl.className = 'current-turn ai';
            }
        }

        // 更新顶部统计和当前比分
        function updateScore(playerWin) {
            const pScoreEl = document.getElementById('playerScore');
            const aScoreEl = document.getElementById('aiScore');
            let p = parseInt(pScoreEl.innerText);
            let a = parseInt(aScoreEl.innerText);
            
            if (playerWin) {
                p++;
                stats.wins++;
            } else {
                a++;
                stats.losses++;
            }
            stats.total++;
            saveStats();
            updateStatsUI();

            pScoreEl.innerText = p;
            aScoreEl.innerText = a;
        }

        function endGame(winner, message) {
            gameOver = true;
            if (navigator.vibrate) navigator.vibrate([30, 30, 30]);
            
            updateScore(winner === 'player');

            const titleEl = document.getElementById('gameOverTitle');
            const msgEl = document.getElementById('gameOverMessage');
            
            if (winner === 'player') {
                titleEl.textContent = '🎉 胜利!';
                titleEl.style.color = '#ffd700';
            } else {
                titleEl.textContent = '😢 失败';
                titleEl.style.color = '#ff6b6b';
            }
            msgEl.textContent = message;
            
            document.getElementById('gameOverModal').style.display = 'flex';
        }

        function resetGame() {
            gameOver = false;
            currentPlayer = 'player';
            selectedPiece = null;
            validMoves = [];
            document.getElementById('playerScore').innerText = '0';
            document.getElementById('aiScore').innerText = '0';
            initBoard();
            renderBoard();
            updateTurnIndicator();
            document.getElementById('gameOverModal').style.display = 'none';
        }

        function showRules() {
            document.getElementById('rulesModal').style.display = 'flex';
        }

        function hideRules() {
            document.getElementById('rulesModal').style.display = 'none';
        }

        document.querySelectorAll('.difficulty-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                document.querySelectorAll('.difficulty-btn').forEach(b => b.classList.remove('active'));
                btn.classList.add('active');
                difficulty = parseInt(btn.dataset.level);
                resetGame();
            });
        });

        document.querySelectorAll('.modal').forEach(modal => {
            modal.addEventListener('click', (e) => {
                if (e.target === modal) modal.style.display = 'none';
            });
        });

        // 启动加载
        loadStats();
        initBoard();
        renderBoard();
    </script>
</body>
</html>
相关推荐
ullio2 小时前
arc205d - Non-Ancestor Matching
算法
wa的一声哭了2 小时前
内积空间 正交与正交系
java·c++·线性代数·算法·矩阵·eclipse·云计算
SWAGGY..2 小时前
数据结构学习篇(8)---二叉树
数据结构·学习·算法
星轨初途2 小时前
牛客小白月赛126
开发语言·c++·经验分享·笔记·算法
leoufung2 小时前
动态规划DP 自我提问模板
算法·动态规划
爱编程的小吴2 小时前
【力扣练习题】热题100道【哈希】560. 和为 K 的子数组
算法·leetcode·哈希算法
Swift社区2 小时前
LeetCode 463 - 岛屿的周长
算法·leetcode·职场和发展
皮卡蛋炒饭.2 小时前
宽搜bfs与深搜dfs
算法·宽度优先
Coder_Boy_3 小时前
基于SpringAI的智能AIOps项目:部署相关容器化部署管理技术图解版
人工智能·spring boot·算法·贪心算法·aiops