AI写的五子棋和扫雷,未作任何改动,纯娱乐

五子棋源码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>五子棋 - 人机对战 & 双人对战</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
            background-size: 400% 400%;
            animation: gradientBG 15s ease infinite;
            padding: 20px;
            overflow-x: hidden;
        }

        @keyframes gradientBG {
            0% { background-position: 0% 50%; }
            50% { background-position: 100% 50%; }
            100% { background-position: 0% 50%; }
        }

        .container {
            display: flex;
            flex-direction: column;
            align-items: center;
            max-width: 1100px;
            width: 100%;
            background: rgba(255, 255, 255, 0.08);
            backdrop-filter: blur(15px);
            border-radius: 20px;
            padding: 30px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
            color: white;
        }

        .header {
            text-align: center;
            margin-bottom: 25px;
            width: 100%;
        }

        h1 {
            font-size: 3.5rem;
            color: #fff;
            text-shadow: 0 0 20px rgba(255, 255, 255, 0.7);
            margin-bottom: 5px;
            position: relative;
            display: inline-block;
            font-weight: 700;
        }

        h1::after {
            content: '';
            position: absolute;
            bottom: -10px;
            left: 25%;
            width: 50%;
            height: 4px;
            background: linear-gradient(90deg, transparent, #ffcc00, transparent);
            border-radius: 2px;
        }

        .subtitle {
            color: #e0e0e0;
            font-size: 1.2rem;
            max-width: 600px;
            margin: 15px auto;
            line-height: 1.6;
        }

        .game-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 30px;
            width: 100%;
            margin-top: 15px;
        }

        .board-container {
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .status-panel {
            display: flex;
            justify-content: space-between;
            width: 100%;
            margin-bottom: 20px;
            gap: 15px;
        }

        .status {
            flex: 1;
            background: rgba(255, 255, 255, 0.12);
            padding: 15px 25px;
            border-radius: 12px;
            color: white;
            font-weight: 500;
            font-size: 1.2rem;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
        }

        .status.active {
            background: rgba(255, 217, 0, 0.2);
            border: 2px solid #ffcc00;
        }

        .status i {
            margin-right: 12px;
            font-size: 1.4rem;
        }

        .black-status i {
            color: #111;
        }

        .white-status i {
            color: #ddd;
        }

        .board {
            width: 520px;
            height: 520px;
            background-color: #dcb35c;
            border: 3px solid #a67c52;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
            border-radius: 4px;
            position: relative;
            overflow: hidden;
        }

        .board::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect x="0" y="0" width="100" height="100" fill="none" stroke="rgba(166, 124, 82, 0.3)" stroke-width="1"/></svg>');
        }

        .grid {
            position: absolute;
            top: 25px;
            left: 25px;
            width: calc(100% - 50px);
            height: calc(100% - 50px);
            display: grid;
            grid-template-columns: repeat(15, 1fr);
            grid-template-rows: repeat(15, 1fr);
        }

        .grid-line {
            position: absolute;
            background-color: rgba(0, 0, 0, 0.8);
        }

        .grid-line.horizontal {
            width: 100%;
            height: 1px;
            left: 0;
        }

        .grid-line.vertical {
            width: 1px;
            height: 100%;
            top: 0;
        }

        .board-point {
            position: absolute;
            width: 10px;
            height: 10px;
            background: #000;
            border-radius: 50%;
            transform: translate(-50%, -50%);
            z-index: 1;
        }

        .piece {
            position: absolute;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            transform: translate(-50%, -50%);
            z-index: 10;
            box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
            transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            cursor: pointer;
        }

        .piece.black {
            background: radial-gradient(circle at 30% 30%, #444, #000);
            border: 1px solid #333;
        }

        .piece.white {
            background: radial-gradient(circle at 30% 30%, #fff, #ccc);
            border: 1px solid #aaa;
        }

        .piece.animate {
            animation: drop 0.4s ease-out;
        }

        .piece.win {
            box-shadow: 0 0 0 5px rgba(255, 217, 0, 0.8),
                        0 0 20px 8px rgba(255, 217, 0, 0.6);
            animation: pulse 1.5s infinite;
        }

        .control-panel {
            background: rgba(255, 255, 255, 0.1);
            padding: 30px;
            border-radius: 20px;
            min-width: 350px;
            color: white;
            height: 520px;
            display: flex;
            flex-direction: column;
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
        }

        .panel-section {
            margin-bottom: 25px;
        }

        .panel-title {
            font-size: 1.6rem;
            margin-bottom: 15px;
            color: #ffcc00;
            display: flex;
            align-items: center;
        }

        .panel-title i {
            margin-right: 10px;
        }

        .score-board {
            display: flex;
            justify-content: space-between;
            gap: 15px;
        }

        .score {
            flex: 1;
            text-align: center;
            padding: 15px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            transition: all 0.3s;
        }

        .score.active {
            background: rgba(255, 217, 0, 0.2);
            transform: scale(1.03);
        }

        .score-title {
            font-size: 1.2rem;
            margin-bottom: 8px;
            color: #ccc;
        }

        .score-value {
            font-size: 3rem;
            font-weight: bold;
            color: #ffcc00;
            text-shadow: 0 0 10px rgba(255, 204, 0, 0.5);
        }

        .controls {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 15px;
            margin-bottom: 20px;
        }

        button {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            background: rgba(255, 255, 255, 0.15);
            border: none;
            padding: 14px;
            border-radius: 12px;
            color: white;
            font-size: 1.1rem;
            cursor: pointer;
            transition: all 0.3s;
            font-weight: 500;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
        }

        button:hover {
            background: rgba(255, 255, 255, 0.25);
            transform: translateY(-2px);
        }

        button:active {
            transform: translateY(1px);
        }

        button.primary {
            background: linear-gradient(45deg, #ff8a00, #ff2070);
        }

        button.primary:hover {
            background: linear-gradient(45deg, #ff9a2a, #ff3080);
        }

        .mode-selector {
            display: flex;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 12px;
            padding: 8px;
            margin-bottom: 20px;
        }

        .mode-option {
            flex: 1;
            padding: 12px;
            text-align: center;
            border-radius: 8px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s;
        }

        .mode-option.active {
            background: rgba(255, 217, 0, 0.3);
            box-shadow: 0 0 15px rgba(255, 204, 0, 0.3);
        }

        .difficulty {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        .difficulty label {
            display: flex;
            align-items: center;
            gap: 15px;
            padding: 14px 20px;
            border-radius: 12px;
            cursor: pointer;
            transition: background 0.2s;
            background: rgba(255, 255, 255, 0.1);
        }

        .difficulty label:hover {
            background: rgba(255, 255, 255, 0.15);
        }

        .difficulty label.active {
            background: rgba(255, 217, 0, 0.2);
        }

        .difficulty i {
            font-size: 1.4rem;
        }

        .difficulty .fa-star {
            color: #ffcc00;
        }

        .win-line {
            position: absolute;
            background-color: gold;
            height: 6px;
            border-radius: 3px;
            z-index: 5;
            transform-origin: 0 0;
        }

        @keyframes drop {
            0% {
                transform: translate(-50%, -50%) scale(1.8);
                opacity: 0;
            }
            70% {
                transform: translate(-50%, -50%) scale(0.95);
            }
            100% {
                transform: translate(-50%, -50%) scale(1);
                opacity: 1;
            }
        }

        @keyframes pulse {
            0% {
                box-shadow: 0 0 0 4px rgba(255, 217, 0, 0.8);
            }
            50% {
                box-shadow: 0 0 0 8px rgba(255, 217, 0, 0.4);
            }
            100% {
                box-shadow: 0 0 0 4px rgba(255, 217, 0, 0.8);
            }
        }

        .game-result {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.8);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 100;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.5s;
        }

        .result-content {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f);
            padding: 50px;
            border-radius: 20px;
            text-align: center;
            max-width: 600px;
            width: 90%;
            transform: scale(0.9);
            transition: transform 0.5s;
            box-shadow: 0 0 40px rgba(255, 204, 0, 0.6);
        }

        .result-title {
            font-size: 3.5rem;
            color: #ffcc00;
            margin-bottom: 20px;
            text-shadow: 0 0 20px rgba(255, 204, 0, 0.5);
        }

        .winner-name {
            font-size: 2.2rem;
            margin: 20px 0;
            color: white;
        }

        .result-message {
            font-size: 1.2rem;
            margin-bottom: 30px;
            line-height: 1.6;
        }

        /* 响应式设计 */
        @media (max-width: 1100px) {
            .board {
                width: 450px;
                height: 450px;
            }

            .control-panel {
                min-width: 300px;
            }
        }

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

            .board {
                width: 85vw;
                height: 85vw;
                max-width: 480px;
                max-height: 480px;
            }

            .control-panel {
                width: 100%;
                max-width: 520px;
                height: auto;
            }
        }

        @media (max-width: 500px) {
            h1 {
                font-size: 2.5rem;
            }

            .board {
                width: 95vw;
                height: 95vw;
            }

            .status {
                font-size: 1rem;
                padding: 10px 15px;
            }

            .controls {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>五子棋</h1>
            <p class="subtitle">策略与智慧的博弈游戏 - 在横、竖或对角线上连成五子即可获胜!</p>
        </div>

        <div class="game-container">
            <div class="board-container">
                <div class="status-panel">
                    <div class="status black-status active" id="blackStatus">
                        <i class="fas fa-circle"></i>
                        <span id="blackStatusText">黑棋</span>
                    </div>
                    <div class="status white-status" id="whiteStatus">
                        <i class="fas fa-circle"></i>
                        <span id="whiteStatusText">白棋</span>
                    </div>
                </div>
                <div class="board" id="board"></div>
            </div>

            <div class="control-panel">
                <div class="score-board panel-section">
                    <div class="score black-score active">
                        <div class="score-title">黑棋得分</div>
                        <div class="score-value" id="playerScore">0</div>
                    </div>
                    <div class="score white-score">
                        <div class="score-title">白棋得分</div>
                        <div class="score-value" id="aiScore">0</div>
                    </div>
                </div>

                <div class="panel-section">
                    <div class="panel-title">
                        <i class="fas fa-cogs"></i>游戏设置
                    </div>

                    <div class="mode-selector">
                        <div class="mode-option active" data-mode="pvc">人机对战</div>
                        <div class="mode-option" data-mode="pvp">双人对战</div>
                    </div>

                    <div class="difficulty">
                        <label class="active">
                            <i class="fas fa-star"></i>
                            <span>初级难度(简单)</span>
                            <input type="radio" name="difficulty" value="easy" checked>
                        </label>
                        <label>
                            <i class="fas fa-star"></i>
                            <i class="fas fa-star"></i>
                            <span>中级难度(普通)</span>
                            <input type="radio" name="difficulty" value="medium">
                        </label>
                        <label>
                            <i class="fas fa-star"></i>
                            <i class="fas fa-star"></i>
                            <i class="fas fa-star"></i>
                            <span>高级难度(困难)</span>
                            <input type="radio" name="difficulty" value="hard">
                        </label>
                    </div>
                </div>

                <div class="panel-section">
                    <div class="panel-title">
                        <i class="fas fa-gamepad"></i>游戏控制
                    </div>

                    <div class="controls">
                        <button class="primary" id="restartBtn">
                            <i class="fas fa-sync-alt"></i>开始新游戏
                        </button>
                        <button id="undoBtn">
                            <i class="fas fa-undo-alt"></i>悔棋
                        </button>
                        <button id="hintBtn">
                            <i class="fas fa-lightbulb"></i>提示
                        </button>
                        <button id="soundBtn">
                            <i class="fas fa-volume-up"></i>音效
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="game-result" id="gameResult">
        <div class="result-content">
            <h2 class="result-title">游戏结束</h2>
            <div class="winner-name" id="winnerName">黑棋获胜!</div>
            <p class="result-message" id="resultMessage">恭喜你赢得了比赛!你的棋艺精湛,策略运用得当。</p>
            <button class="primary" id="playAgainBtn" style="margin-top: 20px; padding: 15px 30px;">
                <i class="fas fa-redo"></i> 再玩一局
            </button>
        </div>
    </div>

    <script>
        // 游戏主逻辑
        document.addEventListener('DOMContentLoaded', () => {
            const boardEl = document.getElementById('board');
            const blackStatusText = document.getElementById('blackStatusText');
            const whiteStatusText = document.getElementById('whiteStatusText');
            const blackStatusEl = document.getElementById('blackStatus');
            const whiteStatusEl = document.getElementById('whiteStatus');
            const blackScoreEl = document.querySelector('.black-score');
            const whiteScoreEl = document.querySelector('.white-score');
            const restartBtn = document.getElementById('restartBtn');
            const undoBtn = document.getElementById('undoBtn');
            const hintBtn = document.getElementById('hintBtn');
            const soundBtn = document.getElementById('soundBtn');
            const playAgainBtn = document.getElementById('playAgainBtn');
            const playerScoreEl = document.getElementById('playerScore');
            const aiScoreEl = document.getElementById('aiScore');
            const gameResultEl = document.getElementById('gameResult');
            const winnerNameEl = document.getElementById('winnerName');
            const resultMessageEl = document.getElementById('resultMessage');
            const modeOptions = document.querySelectorAll('.mode-option');
            const difficultyLabels = document.querySelectorAll('.difficulty label');

            // 游戏状态变量
            const BOARD_SIZE = 15;
            let board = [];
            let currentPlayer = 'black'; // 黑棋先行
            let gameActive = true;
            let gameMode = 'pvc'; // 'pvc' 人机对战, 'pvp' 双人对战
            let blackScore = 0;
            let whiteScore = 0;
            let moveHistory = [];
            let winLine = null;
            let soundEnabled = true;

            // 初始化棋盘
            function initializeBoard() {
                board = [];
                boardEl.innerHTML = '';
                moveHistory = [];

                // 创建网格线
                createGrid();

                // 创建棋盘中心点
                createBoardPoints();

                // 初始化空棋盘
                for (let i = 0; i < BOARD_SIZE; i++) {
                    board[i] = [];
                    for (let j = 0; j < BOARD_SIZE; j++) {
                        board[i][j] = null;
                    }
                }

                gameActive = true;
                currentPlayer = 'black';
                updateStatus();

                // 更新分数显示
                playerScoreEl.textContent = blackScore;
                aiScoreEl.textContent = whiteScore;

                // 移除胜利连线
                if (winLine) {
                    winLine.remove();
                    winLine = null;
                }

                // 重置分数高亮
                blackScoreEl.classList.add('active');
                whiteScoreEl.classList.remove('active');
            }

            // 创建棋盘网格线
            function createGrid() {
                const gridSize = BOARD_SIZE - 1;
                const cellSize = (boardEl.clientWidth - 50) / gridSize;

                // 横线
                for (let i = 0; i < BOARD_SIZE; i++) {
                    const hLine = document.createElement('div');
                    hLine.classList.add('grid-line', 'horizontal');
                    hLine.style.top = `${25 + i * cellSize}px`;
                    boardEl.appendChild(hLine);
                }

                // 竖线
                for (let i = 0; i < BOARD_SIZE; i++) {
                    const vLine = document.createElement('div');
                    vLine.classList.add('grid-line', 'vertical');
                    vLine.style.left = `${25 + i * cellSize}px`;
                    boardEl.appendChild(vLine);
                }
            }

            // 创建棋盘标记点
            function createBoardPoints() {
                const points = [
                    [3, 3], [3, 11], [7, 7], [11, 3], [11, 11]
                ];

                const cellSize = (boardEl.clientWidth - 50) / (BOARD_SIZE - 1);

                points.forEach(point => {
                    const [x, y] = point;
                    const pointEl = document.createElement('div');
                    pointEl.classList.add('board-point');
                    pointEl.style.left = `${25 + x * cellSize}px`;
                    pointEl.style.top = `${25 + y * cellSize}px`;
                    boardEl.appendChild(pointEl);
                });
            }

            // 更新游戏状态显示
            function updateStatus() {
                if (!gameActive) return;

                blackStatusText.textContent = gameMode === 'pvc' 
                    ? (currentPlayer === 'black' ? '您落子 (黑棋)' : '电脑思考中...') 
                    : (currentPlayer === 'black' ? '黑方回合' : '白方回合');

                whiteStatusText.textContent = gameMode === 'pvc' 
                    ? '电脑 (白棋)' 
                    : '白方';

                // 更新当前玩家高亮
                if (currentPlayer === 'black') {
                    blackStatusEl.classList.add('active');
                    whiteStatusEl.classList.remove('active');
                    blackScoreEl.classList.add('active');
                    whiteScoreEl.classList.remove('active');
                } else {
                    whiteStatusEl.classList.add('active');
                    blackStatusEl.classList.remove('active');
                    whiteScoreEl.classList.add('active');
                    blackScoreEl.classList.remove('active');
                }
            }

            // 处理玩家落子
            function handlePlayerMove(e) {
                if (!gameActive || (gameMode === 'pvc' && currentPlayer !== 'black') || 
                    (gameMode === 'pvp' && !gameActive)) return;

                const rect = boardEl.getBoundingClientRect();
                const cellSize = (rect.width - 50) / (BOARD_SIZE - 1);

                // 计算点击位置
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;

                // 排除边缘区域
                if (x < 15 || x > rect.width - 15 || y < 15 || y > rect.height - 15) return;

                // 转换为网格坐标
                const col = Math.round((x - 25) / cellSize);
                const row = Math.round((y - 25) / cellSize);

                // 检查位置是否有效
                if (row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE && !board[row][col]) {
                    placePiece(row, col, currentPlayer);
                    playSound('place');

                    // 检查是否获胜
                    if (checkWin(row, col, currentPlayer)) {
                        endGame(currentPlayer);
                        return;
                    }

                    // 切换到下一位玩家
                    currentPlayer = currentPlayer === 'black' ? 'white' : 'black';
                    updateStatus();

                    // 如果是人机模式且轮到AI,则执行AI落子
                    if (gameMode === 'pvc' && currentPlayer === 'white') {
                        setTimeout(makeAIMove, 600);
                    }
                }
            }

            // 在指定位置放置棋子
            function placePiece(row, col, player) {
                // 存储历史记录用于悔棋
                moveHistory.push({row, col, player});

                // 更新棋盘数据
                board[row][col] = player;

                // 创建棋子元素
                const piece = document.createElement('div');
                piece.classList.add('piece', player, 'animate');

                // 计算像素位置
                const cellSize = (boardEl.clientWidth - 50) / (BOARD_SIZE - 1);
                const left = 25 + col * cellSize;
                const top = 25 + row * cellSize;

                piece.style.left = `${left}px`;
                piece.style.top = `${top}px`;
                boardEl.appendChild(piece);

                // 保存棋子引用
                board[row][col] = {element: piece, player};
            }

            // AI落子逻辑
            function makeAIMove() {
                if (!gameActive || currentPlayer !== 'white') return;

                let bestMove = findBestMove();

                if (bestMove) {
                    const {row, col} = bestMove;
                    setTimeout(() => {
                        placePiece(row, col, 'white');
                        playSound('place');

                        // 检查AI是否获胜
                        if (checkWin(row, col, 'white')) {
                            endGame('white');
                            return;
                        }

                        // 切换回玩家
                        currentPlayer = 'black';
                        updateStatus();
                    }, 300);
                }
            }

            // 查找最佳移动
            function findBestMove() {
                const difficulty = document.querySelector('input[name="difficulty"]:checked').value;

                // 简单AI:随机落子
                if (difficulty === 'easy') {
                    return findRandomMove();
                }

                // 中等AI:基本策略(防守+进攻)
                if (difficulty === 'medium') {
                    return findMediumMove();
                }

                // 困难AI:高级策略(两步思考)
                return findHardMove();
            }

            // 随机移动(简单AI)
            function findRandomMove() {
                const possibleMoves = [];

                // 收集所有空位
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            possibleMoves.push({row, col});
                        }
                    }
                }

                // 随机选择一个位置
                if (possibleMoves.length > 0) {
                    return possibleMoves[Math.floor(Math.random() * possibleMoves.length)];
                }

                return null;
            }

            // 中等AI策略
            function findMediumMove() {
                // 1. 如果AI可以获胜,则选择这个位置
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            // 模拟AI落子
                            board[row][col] = 'white';
                            if (checkWin(row, col, 'white')) {
                                board[row][col] = null; // 重置
                                return {row, col};
                            }
                            board[row][col] = null; // 重置
                        }
                    }
                }

                // 2. 阻止玩家获胜
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            // 模拟玩家落子
                            board[row][col] = 'black';
                            if (checkWin(row, col, 'black')) {
                                board[row][col] = null; // 重置
                                return {row, col};
                            }
                            board[row][col] = null; // 重置
                        }
                    }
                }

                // 3. 如果中心区域有空位,优先选择
                const centerArea = [];
                const centerMin = Math.floor(BOARD_SIZE * 0.4);
                const centerMax = Math.floor(BOARD_SIZE * 0.6);

                for (let row = centerMin; row <= centerMax; row++) {
                    for (let col = centerMin; col <= centerMax; col++) {
                        if (!board[row][col]) {
                            centerArea.push({row, col});
                        }
                    }
                }

                if (centerArea.length > 0) {
                    return centerArea[Math.floor(Math.random() * centerArea.length)];
                }

                // 4. 随机落子
                return findRandomMove();
            }

            // 困难AI策略
            function findHardMove() {
                // 1. 获胜机会
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            board[row][col] = 'white';
                            if (checkWin(row, col, 'white')) {
                                board[row][col] = null;
                                return {row, col};
                            }
                            board[row][col] = null;
                        }
                    }
                }

                // 2. 阻止玩家获胜
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            board[row][col] = 'black';
                            if (checkWin(row, col, 'black')) {
                                board[row][col] = null;
                                return {row, col};
                            }
                            board[row][col] = null;
                        }
                    }
                }

                // 3. 寻找双三、活三等机会
                let bestScore = -Infinity;
                let bestMove = null;

                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            // 计算此位置的价值
                            let score = evaluatePosition(row, col);

                            if (score > bestScore) {
                                bestScore = score;
                                bestMove = {row, col};
                            }
                        }
                    }
                }

                return bestMove;
            }

            // 评估位置价值(困难AI使用)
            function evaluatePosition(row, col) {
                let score = 0;

                // 位置权重 (中心位置价值更高)
                const center = BOARD_SIZE / 2;
                const distance = Math.sqrt(Math.pow(row - center, 2) + Math.pow(col - center, 2));
                score += 10 / (distance + 1);

                // 方向数组: 水平、垂直、对角线
                const directions = [
                    [0, 1],  [1, 0],  [1, 1], [1, -1]
                ];

                // 计算每个方向上的得分
                for (const [dx, dy] of directions) {
                    score += getDirectionScore(row, col, dx, dy);
                }

                return score;
            }

            function getDirectionScore(row, col, dx, dy) {
                let aiScore = 0;
                let playerScore = 0;

                // 分析一个方向上的棋子情况
                for (let i = -4; i <= 4; i++) {
                    if (i === 0) continue; // 跳过当前位置

                    const r = row + dx * i;
                    const c = col + dy * i;

                    if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE) {
                        if (board[r][c] && board[r][c].player === 'white') {
                            aiScore++;
                        } else if (board[r][c] && board[r][c].player === 'black') {
                            playerScore++;
                        }
                    }
                }

                // 计算得分
                let score = 0;

                // AI的潜在连子
                if (aiScore === 4) score += 1000;
                if (aiScore === 3) score += 100;
                if (aiScore === 2) score += 10;
                if (aiScore === 1) score += 1;

                // 阻止玩家的连子
                if (playerScore === 4) score += 500;
                if (playerScore === 3) score += 50;
                if (playerScore === 2) score += 5;

                return score;
            }

            // 检查是否获胜
            function checkWin(row, col, player) {
                const directions = [
                    [[0, 1], [0, -1]],   // 水平
                    [[1, 0], [-1, 0]],   // 垂直
                    [[1, 1], [-1, -1]],  // 对角线
                    [[1, -1], [-1, 1]]   // 反对角线
                ];

                for (const [positive, negative] of directions) {
                    let count = 1; // 当前位置已经有一个棋子

                    // 正方向
                    for (let i = 1; i < 5; i++) {
                        const newRow = row + positive[0] * i;
                        const newCol = col + positive[1] * i;

                        if (newRow < 0 || newRow >= BOARD_SIZE || 
                            newCol < 0 || newCol >= BOARD_SIZE ||
                            !board[newRow][newCol] || board[newRow][newCol].player !== player) {
                            break;
                        }
                        count++;
                    }

                    // 反方向
                    for (let i = 1; i < 5; i++) {
                        const newRow = row + negative[0] * i;
                        const newCol = col + negative[1] * i;

                        if (newRow < 0 || newRow >= BOARD_SIZE || 
                            newCol < 0 || newCol >= BOARD_SIZE ||
                            !board[newRow][newCol] || board[newRow][newCol].player !== player) {
                            break;
                        }
                        count++;
                    }

                    if (count >= 5) {
                        createWinLine(row, col, positive, negative);
                        return true;
                    }
                }

                return false;
            }

            // 创建胜利连线
            function createWinLine(row, col, positive, negative) {
                const cellSize = (boardEl.clientWidth - 50) / (BOARD_SIZE - 1);

                // 计算起点
                let startRow = row;
                let startCol = col;

                // 找到连线起点
                while (true) {
                    const prevRow = startRow + negative[0];
                    const prevCol = startCol + negative[1];

                    if (prevRow < 0 || prevRow >= BOARD_SIZE || 
                        prevCol < 0 || prevCol >= BOARD_SIZE ||
                        !board[prevRow][prevCol] || board[prevRow][prevCol].player !== board[row][col].player) {
                        break;
                    }

                    startRow = prevRow;
                    startCol = prevCol;
                }

                // 计算终点
                let endRow = row;
                let endCol = col;

                while (true) {
                    const nextRow = endRow + positive[0];
                    const nextCol = endCol + positive[1];

                    if (nextRow < 0 || nextRow >= BOARD_SIZE || 
                        nextCol < 0 || nextCol >= BOARD_SIZE ||
                        !board[nextRow][nextCol] || board[nextRow][nextCol].player !== board[row][col].player) {
                        break;
                    }

                    endRow = nextRow;
                    endCol = nextCol;
                }

                // 创建连线元素
                const startX = 25 + startCol * cellSize;
                const startY = 25 + startRow * cellSize;
                const endX = 25 + endCol * cellSize;
                const endY = 25 + endRow * cellSize;

                // 计算线段的长度和角度
                const dx = endX - startX;
                const dy = endY - startY;
                const length = Math.sqrt(dx * dx + dy * dy);
                const angle = Math.atan2(dy, dx) * 180 / Math.PI;

                winLine = document.createElement('div');
                winLine.classList.add('win-line');
                winLine.style.width = `${length}px`;
                winLine.style.left = `${startX}px`;
                winLine.style.top = `${startY}px`;
                winLine.style.transform = `rotate(${angle}deg)`;
                boardEl.appendChild(winLine);

                // 标记胜利棋子
                for (let r = startRow; r <= endRow; r++) {
                    for (let c = startCol; c <= endCol; c++) {
                        if (board[r][c] && board[r][c].element) {
                            board[r][c].element.classList.add('win');
                        }
                    }
                }
            }

            // 结束游戏
            function endGame(winner) {
                gameActive = false;

                // 更新得分
                if (winner === 'black') {
                    blackScore++;
                    winnerNameEl.textContent = gameMode === 'pvc' ? '您获胜了!' : '黑方获胜!';
                    resultMessageEl.textContent = gameMode === 'pvc' 
                        ? '恭喜你赢得了比赛!你的棋艺精湛,策略运用得当。' 
                        : '恭喜黑方获胜!精彩的胜利,精湛的棋艺!';
                } else {
                    whiteScore++;
                    winnerNameEl.textContent = gameMode === 'pvc' ? '电脑获胜!' : '白方获胜!';
                    resultMessageEl.textContent = gameMode === 'pvc' 
                        ? '电脑获得了胜利,继续加油,下次一定能赢!' 
                        : '恭喜白方获胜!出色的策略,完美的胜利!';
                }

                // 显示结果弹窗
                gameResultEl.style.opacity = '1';
                gameResultEl.style.pointerEvents = 'all';
                setTimeout(() => {
                    gameResultEl.querySelector('.result-content').style.transform = 'scale(1)';
                }, 10);
            }

            // 提示功能
            function showHint() {
                if (!gameActive || currentPlayer !== 'black' || gameMode !== 'pvc') return;

                const possibleMoves = [];

                // 收集所有空位
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (!board[row][col]) {
                            possibleMoves.push({row, col});
                        }
                    }
                }

                // 随机选择一个提示位置(实际应用中应该使用算法找到最佳位置)
                if (possibleMoves.length > 0) {
                    const {row, col} = possibleMoves[Math.floor(Math.random() * possibleMoves.length)];

                    // 添加提示标记
                    const hintElement = document.createElement('div');
                    hintElement.style.position = 'absolute';
                    hintElement.style.left = `${25 + col * ((boardEl.clientWidth - 50) / (BOARD_SIZE - 1))}px`;
                    hintElement.style.top = `${25 + row * ((boardEl.clientWidth - 50) / (BOARD_SIZE - 1))}px`;
                    hintElement.style.width = '15px';
                    hintElement.style.height = '15px';
                    hintElement.style.borderRadius = '50%';
                    hintElement.style.backgroundColor = '#ffcc00';
                    hintElement.style.transform = 'translate(-50%, -50%)';
                    hintElement.style.zIndex = '5';
                    hintElement.style.animation = 'pulse 1.5s infinite';
                    boardEl.appendChild(hintElement);

                    // 3秒后移除提示
                    setTimeout(() => {
                        hintElement.remove();
                    }, 3000);
                }
            }

            // 播放音效
            function playSound(type) {
                if (!soundEnabled) return;

                // 在实际应用中这里应该加载音频文件
                // 这是演示版本,所以只是控制台输出
                console.log(`播放音效: ${type}`);
            }

            // 初始化游戏
            initializeBoard();

            // 事件监听
            boardEl.addEventListener('click', handlePlayerMove);

            restartBtn.addEventListener('click', () => {
                initializeBoard();
                playSound('restart');
            });

            undoBtn.addEventListener('click', () => {
                if (moveHistory.length < 1 || !gameActive) return;

                // 移除最后一步
                const lastMove = moveHistory.pop();
                if (lastMove && board[lastMove.row][lastMove.col]?.element) {
                    board[lastMove.row][lastMove.col].element.remove();
                }
                board[lastMove.row][lastMove.col] = null;

                // 如果上一步是AI/对手下的,再移除玩家上一步
                if (moveHistory.length > 0 && moveHistory[moveHistory.length - 1].player === currentPlayer) {
                    const previousMove = moveHistory.pop();
                    if (previousMove && board[previousMove.row][previousMove.col]?.element) {
                        board[previousMove.row][previousMove.col].element.remove();
                    }
                    board[previousMove.row][previousMove.col] = null;
                }

                // 重置为当前玩家回合
                currentPlayer = currentPlayer === 'black' ? 'white' : 'black';
                gameActive = true;
                updateStatus();

                // 移除胜利连线
                if (winLine) {
                    winLine.remove();
                    winLine = null;
                }

                playSound('undo');
            });

            hintBtn.addEventListener('click', showHint);

            soundBtn.addEventListener('click', () => {
                soundEnabled = !soundEnabled;
                soundBtn.innerHTML = soundEnabled 
                    ? '<i class="fas fa-volume-up"></i> 音效' 
                    : '<i class="fas fa-volume-mute"></i> 音效';
            });

            playAgainBtn.addEventListener('click', () => {
                gameResultEl.style.opacity = '0';
                gameResultEl.style.pointerEvents = 'none';
                gameResultEl.querySelector('.result-content').style.transform = 'scale(0.9)';
                initializeBoard();
            });

            // 游戏模式选择
            modeOptions.forEach(option => {
                option.addEventListener('click', () => {
                    modeOptions.forEach(opt => opt.classList.remove('active'));
                    option.classList.add('active');
                    gameMode = option.dataset.mode;

                    initializeBoard();
                    playSound('mode');
                });
            });

            // 难度选择
            difficultyLabels.forEach(label => {
                const input = label.querySelector('input');
                label.addEventListener('click', () => {
                    difficultyLabels.forEach(l => l.classList.remove('active'));
                    label.classList.add('active');
                    playSound('difficulty');
                });
            });
        });
    </script>
</body>
</html>

扫雷源码:

<!DOCTYPE html>
<html>
    <head>
        <title>扫雷小游戏</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
         
         
        <style>
            body {
                margin: 0;
            }
        </style>
 
         
 
        <style>
            :root {
  --grid-width: 350;
  --tile-width: 35;
  --main-color: #8B6AF5;
}
 
body {
  padding: 0;
  margin: 0;
  background: #f9f8fe;
  width: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-family: "Roboto Mono", monospace;
}
 
*,
*:before,
*:after {
  box-sizing: border-box;
}
 
.container {
  width: calc(var(--grid-width) * 1px);
  align-content: center;
  text-align: center;
  box-shadow: 5px 5px 20px 0 rgba(100, 74, 74, 0.1);
  position: relative;
  z-index: 2;
}
 
.header {
  display: flex;
  position: relative;
  align-items: flex-start;
  background-color: var(--main-color);
  color: white;
  justify-content: space-between;
  padding: 1rem;
}
 
.grid {
  height: calc(var(--grid-width) * 1px);
  width: 100%;
  display: flex;
  flex-wrap: wrap;
}
 
.tile {
  height: calc(var(--tile-width) * 1px);
  width: calc(var(--tile-width) * 1px);
  cursor: pointer;
  border: 2px solid;
  border-color: white rgb(221.1428571429, 215.5, 255) rgb(221.1428571429, 215.5, 255) white;
  box-sizing: border-box;
  background-color: #f3f1ff;
  font-weight: 700;
  font-size: 25px;
  display: flex;
  align-items: center;
  justify-content: center;
}
 
.tile svg {
  width: 80%;
}
 
.checked {
  border: 1px solid;
  background-color: rgb(234.2571428571, 230.8, 255);
  border-color: rgb(221.1428571429, 215.5, 255);
}
 
#refresh {
  cursor: pointer;
  width: 30px;
  align-self: flex-end;
}
 
.dropdown {
  color: white;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
  font-size: 1rem;
  text-align: center;
}
.dropdown .title {
  width: 100%;
  padding: 0.5rem 1rem;
  cursor: pointer;
}
.dropdown .menu {
  background: var(--main-color);
  position: absolute;
  overflow: hidden;
  cursor: pointer;
  display: none;
  width: 5rem;
  text-align: left;
  line-height: 1.4rem;
}
.dropdown .menu.show {
  display: block;
}
.dropdown .menu .option {
  padding: 0.5rem;
}
.dropdown .menu .option:hover {
  background: rgba(255, 255, 255, 0.2);
}
 
.has-bomb {
  transition: background 0.25s ease-in;
}
 
#flag-countdown, #timer {
  display: flex;
  font-size: 35px;
}
#flag-countdown span, #timer span {
  margin-left: 0.5rem;
}
 
#modal {
  position: fixed;
  background-color: rgba(57, 57, 91, 0.2);
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 999;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: all 0.3s;
  display: flex;
  align-items: center;
  justify-content: center;
}
#modal.show {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}
#modal .modal-close {
  text-align: right;
}
#modal h2 {
  color: var(--main-color);
}
 
#result-box {
  background-color: #f9f8fe;
  box-shadow: 5px 5px 20px 0 rgba(100, 74, 74, 0.1);
  border-radius: 4px;
  min-width: 400px;
  text-align: center;
}
 
#result-top {
  margin: 2rem;
}
 
#result-message {
  color: var(--dark-color);
  font-size: 40px;
}
 
.result-time {
  display: none;
}
 
.show {
  display: block;
}
 
#new-game {
  padding: 0.5rem;
  background-color: var(--main-color);
  color: #fff;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 1rem;
  height: 60px;
  border-radius: 4px;
  font-family: sans-serif;
}
#new-game * {
  display: inline-block;
}
#new-game h2 {
  line-height: 30px;
  margin: 0 0 0 1rem;
  color: white;
}
 
#background {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  position: absolute;
  z-index: 1;
  width: 100%;
  height: 100%;
}
#background svg {
  width: 50px;
  height: 40px;
  opacity: 0.3;
  position: absolute;
}
 
svg#bomb {
  width: 200px;
}
svg#bomb .sad-face, svg#bomb .happy-face {
  display: none;
}
svg#bomb .sad-face.show, svg#bomb .happy-face.show {
  display: block;
}
svg#bomb .bomb-fill {
  fill: var(--main-color);
}
svg#bomb .bomb-stroke {
  stroke: var(--dark-color);
}
svg#bomb .bomb-fill-dark {
  fill: var(--dark-color);
}
 
svg.hide {
  display: none;
}
 
.shake {
  animation: shake 0.75s cubic-bezier(0.38, 0.06, 0.22, 0.95) both;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
  perspective: 1000px;
}
 
@keyframes shake {
  10%, 90% {
    transform: translate3d(-1px, 0, 0);
  }
  20%, 80% {
    transform: translate3d(2px, 0, 0);
  }
  30%, 50%, 70% {
    transform: translate3d(-3px, 0, 0);
  }
  40%, 60% {
    transform: translate3d(2px, 0, 0);
  }
}
        </style>
    </head>
    <body>
          <svg id="main" class="hide">
    <symbol id="bomb-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 786.7 687.8" width="50px">
      <path d="M427.4,144.2l40.7-41.8a23.1,23.1,0,0,1,32.5,0l72.8,72.8a23.1,23.1,0,0,1,0,32.5l-37.4,37.4" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <circle cx="291.1" cy="385.2" r="282.6" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <path d="M540.2,141.2l15.4-20.5c12.6-16.8,30.4-17.7,43.6-2.4l0.3,0.3c11.8,13.7,31.3,22.6,48.3,11.4,6.1-4,20.7-20.5,20.7-20.5" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <line x1="701.7" y1="63.3" x2="742" y2="23.1" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <line x1="713.2" y1="107.4" x2="766.2" y2="128.2" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <line x1="654.5" y1="60.2" x2="630.8" y2="8.5" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <path d="M82,396.8c0-118.4,95.9-214.3,214.3-214.3" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      <g id="Happy_face" data-name="Happy face">
        <path d="M170.4,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
        <path d="M342.5,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
        <path d="M367,481.7c0,33.7-33.4,64.1-74.6,64.1s-74.6-30.3-74.6-64.1" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
      </g>
    </symbol>
  </svg>
  <div id="background"></div>
  <div id="modal">
      <div id="result-box">
        <div id="result-top">
          <svg id="bomb" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 774.7 676.2">
            <path d="M427.4,144.2l40.7-41.8a23.1,23.1,0,0,1,32.5,0l72.8,72.8a23.1,23.1,0,0,1,0,32.5l-37.4,37.4" transform="translate(0 -11.6)" fill="#8b6af5" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke bomb-fill"/>
            <circle cx="291.1" cy="385.2" r="282.6" fill="#8b6af5" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke bomb-fill"/>
            <path d="M540.2,141.2l15.4-20.5c12.6-16.8,30.4-17.7,43.6-2.4l0.3,0.3c11.8,13.7,31.3,22.6,48.3,11.4,6.1-4,20.7-20.5,20.7-20.5" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
            <line x1="701.7" y1="63.3" x2="742" y2="23.1" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
            <line x1="713.2" y1="107.4" x2="766.2" y2="128.2" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
            <line x1="654.5" y1="60.2" x2="630.8" y2="8.5" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
            <path d="M82,396.8c0-118.4,95.9-214.3,214.3-214.3" transform="translate(0 -11.6)" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-opacity="0.45" stroke-width="17"/>
            <g id="happy-face" class="happy-face">
              <path d="M170.4,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
              <path d="M342.5,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
              <path d="M367,481.7c0,33.7-33.4,64.1-74.6,64.1s-74.6-30.3-74.6-64.1" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
            </g>
            <g id="sad-face" class="sad-face">
              <g>
                <circle cx="377.1" cy="406" r="17.7" fill="#39395b" class="bomb-fill-dark"/>
                <path d="M250,517.1c0-19.2,17.6-42.5,41.1-42.5s43.8,23.3,43.8,42.5" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
                <circle cx="205" cy="406" r="17.7" fill="#39395b" class="bomb-fill-dark"/>
              </g>
            </g>
          </svg>
          <h1 id="result-message"></h1>
          <h2 class="result-time">Your time: <span class="time-display"></span> seconds</h2>
        </div>
        <div id="new-game">
          <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 512 512" enable-background="new 0 0 512 512" fill="#fff" width="30">
            <g>
              <path d="M480.6,235.6c-11.3,0-20.4,9.1-20.4,20.4c0,112.6-91.6,204.2-204.2,204.2c-112.6,0-204.2-91.6-204.2-204.2   S143.4,51.8,256,51.8c61.5,0,118.5,27.1,157.1,73.7h-70.5c-11.3,0-20.4,9.1-20.4,20.4s9.1,20.4,20.4,20.4h114.6   c11.3,0,20.4-9.1,20.4-20.4V31.4c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4v59C390.7,40.1,325.8,11,256,11   C120.9,11,11,120.9,11,256c0,135.1,109.9,245,245,245s245-109.9,245-245C501,244.7,491.9,235.6,480.6,235.6z"/>
            </g>
          </svg>
          <h2>重新开始</h2>
        </div>
      </div>
  </div>
  <div class="container">
    <div class="header">
      <div class='dropdown'>
        <div class='title'>初级</div>
        <div class='menu'>
          <div class='option' id='option1'>初级</div>
          <div class='option' id='option2'>中级</div>
          <div class='option' id='option3'>高级</div>
        </div>
      </div>
      <div id='flag-countdown'><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 287.987 287.987" fill="#fff" width="30" style="enable-background:new 0 0 287.987 287.987;" xml:space="preserve"><g><path d="M228.702,141.029c-3.114-3.754-3.114-9.193,0-12.946l33.58-40.474c2.509-3.024,3.044-7.226,1.374-10.783   c-1.671-3.557-5.246-5.828-9.176-5.828h-57.647v60.98c0,16.618-13.52,30.138-30.138,30.138h-47.093v25.86   c0,5.599,4.539,10.138,10.138,10.138h124.74c3.93,0,7.505-2.271,9.176-5.828c1.671-3.557,1.135-7.759-1.374-10.783L228.702,141.029   z"/><path d="M176.832,131.978V25.138c0-5.599-4.539-10.138-10.138-10.138H53.37c0-8.284-6.716-15-15-15s-15,6.716-15,15   c0,7.827,0,253.91,0,257.987c0,8.284,6.716,15,15,15s15-6.716,15-15c0-6.943,0-126.106,0-130.871h113.324   C172.293,142.116,176.832,137.577,176.832,131.978z"/></g></svg><span id='flags-left'></span></div>
      <div id="timer"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 559.98 559.98"  fill="#fff" width="30px" style="enable-background:new 0 0 559.98 559.98;" xml:space="preserve"><g><path d="M279.99,0C125.601,0,0,125.601,0,279.99c0,154.39,125.601,279.99,279.99,279.99c154.39,0,279.99-125.601,279.99-279.99    C559.98,125.601,434.38,0,279.99,0z M279.99,498.78c-120.644,0-218.79-98.146-218.79-218.79    c0-120.638,98.146-218.79,218.79-218.79s218.79,98.152,218.79,218.79C498.78,400.634,400.634,498.78,279.99,498.78z"/><path d="M304.226,280.326V162.976c0-13.103-10.618-23.721-23.716-23.721c-13.102,0-23.721,10.618-23.721,23.721v124.928    c0,0.373,0.092,0.723,0.11,1.096c-0.312,6.45,1.91,12.999,6.836,17.926l88.343,88.336c9.266,9.266,24.284,9.266,33.543,0    c9.26-9.266,9.266-24.284,0-33.544L304.226,280.326z"/></g></svg><span class="counter">000</span></div>
    </div>
    <div class="grid"></div>
  </div>
        <script type="text/javascript">// watch Ania Kubow's tutorial here: [url]https://www.youtube.com/watch?v=rxdGAKRndz8[/url]
 
document.addEventListener("DOMContentLoaded", () => {
  const grid = document.querySelector(".grid");
  const container = document.querySelector(".container");
  const flagsLeft = document.querySelector("#flags-left");
  const result = document.querySelector("#result-message");
  const modal = document.querySelector("#modal");
  const background = document.querySelector("#background");
  const bombSadFace = document.querySelector("#sad-face");
  const bombHappyFace = document.querySelector("#happy-face");
  const timer = document.querySelector(".counter");
  const finalTimeDisplay = document.querySelector(".time-display");
  const resultTime = document.querySelector(".result-time");
 
  const levels = [
    {
      name: "初级",
      width: 10,
      bombs: 15,
    },
    {
      name: "中级",
      width: 15,
      bombs: 30,
    },
    {
      name: "高级",
      width: 20,
      bombs: 70,
    },
  ];
  const flagIcon =
    '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 287.987 287.987" fill="#695ca8" style="enable-background:new 0 0 287.987 287.987;" xml:space="preserve"><g><path d="M228.702,141.029c-3.114-3.754-3.114-9.193,0-12.946l33.58-40.474c2.509-3.024,3.044-7.226,1.374-10.783   c-1.671-3.557-5.246-5.828-9.176-5.828h-57.647v60.98c0,16.618-13.52,30.138-30.138,30.138h-47.093v25.86   c0,5.599,4.539,10.138,10.138,10.138h124.74c3.93,0,7.505-2.271,9.176-5.828c1.671-3.557,1.135-7.759-1.374-10.783L228.702,141.029   z"/><path d="M176.832,131.978V25.138c0-5.599-4.539-10.138-10.138-10.138H53.37c0-8.284-6.716-15-15-15s-15,6.716-15,15   c0,7.827,0,253.91,0,257.987c0,8.284,6.716,15,15,15s15-6.716,15-15c0-6.943,0-126.106,0-130.871h113.324   C172.293,142.116,176.832,137.577,176.832,131.978z"/></g></svg>';
  const bombIcon =
    '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 512 512" fill="#695ca8" ><g><path d="m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"/><path d="m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"/><path d="m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"/><path d="m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"/><path d="m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"/><path d="m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"/></g></svg>';
 
  let root = document.documentElement;
  let selectedLevel = levels[0];
  let flags = 0;
  let tiles = [];
  let isGameOver = false;
  let isTimerOn = false;
  let timerCount = 0;
  let finalTime = 0;
 
  let numberColors = [
    "#8B6AF5",
    "#74c2f9",
    "#42dfbc",
    "#f9dd5b",
    "#FEAC5E",
    "#ff5d9e",
    "#F29FF5",
    "#c154d8",
  ];
  let bgColors = [
    "#b39ffd",
    "#93c1fd",
    "#8af1f8",
    "#f9dd5b",
    "#FEAC5E",
    "#f87dae",
    "#f6b8f8",
    "#f7efce",
  ];
 
  function getMainColor() {
    const randomColor =
      numberColors[Math.floor(Math.random() * numberColors.length)];
    root.style.setProperty("--main-color", randomColor);
    root.style.setProperty(
      "--dark-color",
      lightenDarkenColor(randomColor, -50)
    );
  }
 
  function clearBoard() {
    isGameOver = false;
    flags = 0;
    if (isTimerOn) stopTimer();
    timerCount = 0;
 
    // isTimerOn = false;
    timer.innerHTML = "000";
    tiles.forEach((tile) => {
      tile.remove();
    });
    tiles = [];
    container.classList.remove("shake");
    bombHappyFace.classList.remove("show");
    bombSadFace.classList.remove("show");
    resultTime.classList.remove("show");
    createBoard();
  }
 
  function setUp() {
    createBackground();
    createBoard();
  }
 
  function createBoard() {
    getMainColor();
 
    flagsLeft.innerHTML = selectedLevel.bombs;
    const width = selectedLevel.width;
    const tileWidth = parseInt(
      getComputedStyle(root).getPropertyValue("--tile-width")
    );
    root.style.setProperty("--grid-width", width * tileWidth);
 
    //add tiles
    for (let i = 0; i < width * width; i++) {
      const tile = document.createElement("div");
      tile.setAttribute("id", i);
      tile.classList.add("tile");
      grid.appendChild(tile);
      tiles.push(tile);
 
      //left click
      tile.addEventListener("click", () => clickTile(tile));
 
      //ctrl and right click
      tile.oncontextmenu = (e) => {
        e.preventDefault();
        addFlag(tile);
      };
    }
 
    //add bombs
    const randomTiles = tiles
      .sort(() => Math.random() - 0.5)
      .slice(0, selectedLevel.bombs)
      .map((tile) => tile.id); // suffle tile's id and select 20 firsts to get 20 random tiles
    tiles.forEach((tile) =>
      randomTiles.includes(tile.id)
        ? tile.classList.add("has-bomb")
        : tile.classList.add("is-empty")
    ); // if tile's id is in ramdomTile array, add bomb
    tiles.sort((a, b) => a.id - b.id); //sort array by id again
 
    //add numbers
    for (let i = 0; i < tiles.length; i++) {
      let total = 0;
      const isLeftEdge = i % width === 0;
      const isRightEdge = i % width === width - 1;
 
      if (!tiles[i].classList.contains("has-bomb")) {
        if (!isLeftEdge) {
          if (tiles[i - 1] && tiles[i - 1].classList.contains("has-bomb"))
            total++;
          if (
            tiles[i - 1 + width] &&
            tiles[i - 1 + width].classList.contains("has-bomb")
          )
            total++;
          if (
            tiles[i - 1 - width] &&
            tiles[i - 1 - width].classList.contains("has-bomb")
          )
            total++;
        }
 
        if (!isRightEdge) {
          if (tiles[i + 1] && tiles[i + 1].classList.contains("has-bomb"))
            total++;
          if (
            tiles[i + 1 + width] &&
            tiles[i + 1 + width].classList.contains("has-bomb")
          )
            total++;
          if (
            tiles[i + 1 - width] &&
            tiles[i + 1 - width].classList.contains("has-bomb")
          )
            total++;
        }
 
        if (tiles[i - width] && tiles[i - width].classList.contains("has-bomb"))
          total++;
        if (tiles[i + width] && tiles[i + width].classList.contains("has-bomb"))
          total++;
 
        tiles[i].setAttribute("data", total);
      }
    }
  }
 
  //click on tile
  function clickTile(tile) {
    if (!isTimerOn) startTimer();
    let currentId = tile.id;
    if (isGameOver) return;
    if (tile.classList.contains("checked") || tile.classList.contains("flag"))
      return;
    if (tile.classList.contains("has-bomb")) {
      gameOver(tile);
    } else {
      let total = tile.getAttribute("data");
      if (total != 0) {
        tile.classList.add("checked");
        tile.style.color = numberColors[total - 1];
        tile.style.textShadow =
          "1px 1px" + lightenDarkenColor(numberColors[total - 1], -20);
        tile.innerHTML = total;
        return;
      }
      checktile(currentId);
    }
    tile.classList.add("checked");
  }
 
  //check neighboring tiles once tile is clicked
  function checktile(currentId) {
    const width = selectedLevel.width;
    const isLeftEdge = currentId % width === 0;
    const isRightEdge = currentId % width === width - 1;
    const parsedId = parseInt(currentId);
 
    function loopThroughtiles(tileId) {
      const newId = tileId.id;
      const newTile = document.getElementById(newId);
      clickTile(newTile);
    }
    setTimeout(() => {
      if (!isRightEdge) {
        if (tiles[parsedId + 1 - width])
          loopThroughtiles(tiles[parsedId + 1 - width]);
        if (tiles[parsedId + 1]) loopThroughtiles(tiles[parsedId + 1]);
        if (tiles[parsedId + 1 + width])
          loopThroughtiles(tiles[parsedId + 1 + width]);
      }
      if (!isLeftEdge) {
        if (tiles[parsedId - 1]) loopThroughtiles(tiles[parsedId - 1]);
        if (tiles[parsedId - 1 - width])
          loopThroughtiles(tiles[parsedId - 1 - width]);
        if (tiles[parsedId - 1 + width])
          loopThroughtiles(tiles[parsedId - 1 + width]);
      }
      if (tiles[parsedId - width]) loopThroughtiles(tiles[parsedId - width]);
      if (tiles[parsedId + width]) loopThroughtiles(tiles[parsedId + width]);
    }, 50);
  }
 
  //game over
  function gameOver(currentTile) {
    isGameOver = true;
    stopTimer();
    currentTile.innerHTML = bombIcon;
    container.classList.add("shake");
    currentTile.style.backgroundColor =
      bgColors[Math.floor(Math.random() * bgColors.length)];
    currentTile.classList.remove("has-bomb");
    currentTile.classList.add("checked");
    let itemsProcessed = 0;
 
    //show all the bombs
    const bombTiles = tiles.filter((tile) =>
      tile.classList.contains("has-bomb")
    );
    bombTiles.forEach((tile, index) => {
      setTimeout(() => {
        tile.innerHTML = bombIcon;
        tile.style.backgroundColor =
          bgColors[Math.floor(Math.random() * bgColors.length)];
        tile.classList.remove("has-bomb");
        tile.classList.add("checked");
        itemsProcessed++;
        if (itemsProcessed === bombTiles.length) {
          setTimeout(() => {
            modal.classList.add("show");
            bombSadFace.classList.add("show");
            result.innerHTML = "游戏结束!";
          }, 1000);
        }
      }, 10 * index);
    });
  }
 
  //add Flag with right click
  function addFlag(tile) {
    if (isGameOver) return;
    if (!tile.classList.contains("checked") && flags < selectedLevel.bombs) {
      if (!tile.classList.contains("flag")) {
        tile.classList.add("flag");
        tile.innerHTML = flagIcon;
        flags++;
        flagsLeft.innerHTML = selectedLevel.bombs - flags;
        checkForWin();
      } else {
        tile.classList.remove("flag");
        tile.innerHTML = "";
        flags--;
        flagsLeft.innerHTML = selectedLevel.bombs - flags;
      }
    }
  }
 
  //check for win
  function checkForWin() {
    let matches = 0;
    tiles.forEach((tile) => {
      if (
        tile.classList.contains("flag") &&
        tile.classList.contains("has-bomb")
      ) {
        matches++;
      }
      if (matches === selectedLevel.bombs) {
        stopTimer();
        modal.classList.add("show");
        bombHappyFace.classList.add("show");
        resultTime.classList.add("show");
        result.innerHTML = "恭喜!";
        isGameOver = true;
 
        // reveal all remaining tiles
        if (!tile.classList.contains("checked")) {
          tile.classList.add("checked");
        }
      }
    });
  }
 
  function replay() {
    if (modal.classList.contains("show")) modal.classList.remove("show");
    clearBoard();
  }
 
  document.querySelector("#new-game").addEventListener("click", replay);
 
  // timer functions
  function startTimer() {
    isTimerOn = true;
    let sec = 0;
    timerCount = setInterval(() => {
      sec++;
      timer.innerHTML = ("00" + sec).slice(-3);
      if (sec > 998) clearInterval(timerCount);
      finalTime = sec;
    }, 1000);
  }
 
  function stopTimer() {
    clearInterval(timerCount);
    isTimerOn = false;
    finalTimeDisplay.innerHTML = finalTime;
  }
 
  // dropdown menu functions
  const dropdownTitle = document.querySelector(".dropdown .title");
  const dropdownOptions = document.querySelectorAll(".dropdown .option");
 
  function toggleMenuDisplay(e) {
    const dropdown = e.target.parentNode; //getting the parent selector
    const menu = dropdown.querySelector(".menu"); //selecting 'menu' fron the parent selector
    menu.classList.toggle("show");
  }
 
  function handleOptionSelected(e) {
    e.target.parentNode.classList.toggle("show"); // using parentnode to get the menu  elementfrom option elements
    dropdownTitle.textContent = e.target.textContent;
    // here: select current level form levels object
    selectedLevel = levels.find((level) => level.name === e.target.textContent);
    clearBoard();
  }
 
  // event listeners
  dropdownTitle.addEventListener("click", toggleMenuDisplay);
  dropdownOptions.forEach((option) =>
    option.addEventListener("click", handleOptionSelected)
  );
 
  // bg functions
  function addElement(x, y) {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    const use = document.createElementNS("http://www.w3.org/2000/svg", "use");
    use.setAttributeNS(
      "http://www.w3.org/1999/xlink",
      "xlink:href",
      "#bomb-svg"
    );
    svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
    svg.setAttribute("style", "top: " + y + "px; left: " + x + "px");
    svg.appendChild(use);
    background.appendChild(svg);
  }
 
  function createBackground() {
    const spacing = 60;
    const w = window.innerWidth;
    const h = window.innerHeight;
    for (let y = 0; y <= h; y += spacing) {
      if (y % (spacing * 2) === 0) {
        for (let x = 0; x <= w; x += spacing) {
          addElement(x, y);
        }
      } else {
        for (let x = -(spacing / 2); x <= w; x += spacing) {
          addElement(x, y);
        }
      }
    }
  }
 
  //helper function
  function lightenDarkenColor(col, amt) {
    let usePound = false;
    if (col[0] == "#") {
      col = col.slice(1);
      usePound = true;
    }
    let num = parseInt(col, 16);
    let r = (num >> 16) + amt;
    if (r > 255) r = 255;
    else if (r < 0) r = 0;
    let b = ((num >> 8) & 0x00ff) + amt;
    if (b > 255) b = 255;
    else if (b < 0) b = 0;
    let g = (num & 0x0000ff) + amt;
    if (g > 255) g = 255;
    else if (g < 0) g = 0;
    return (usePound ? "#" : "") + (g | (b << 8) | (r << 16)).toString(16);
  }
 
  setUp();
});
</script>
    </body>
</html>
请登录后发表评论

    没有回复内容