休闲摸鱼小游戏扫雷游戏|下载即玩

1.经典玩法与现代设计结合,支持左键点击揭开方块、右键标记地雷

2.多种难度难度级别(初级、中级、高级、自定义),满足不同玩家需求

3.精美的动画效果,包括地雷爆炸、胜利庆祝烟花等

4.深色 / 浅色主题切换,适应不同使用环境

5.游戏计时功能和最高分排行榜,记录您的最佳成绩

6.响应式设计,适配不同屏幕尺寸

代码:复制该成html后缀即可

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>扫雷 - 经典游戏</title>
  <!-- Tailwind CSS v3 -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- Font Awesome -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <!-- GSAP 动画库 -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
  
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3b82f6',
            secondary: '#6366f1',
            accent: '#f43f5e',
            success: '#10b981',
            warning: '#f59e0b',
            info: '#06b6d4',
            dark: '#1e293b',
            light: '#f8fafc'
          },
          fontFamily: {
            sans: ['Inter', 'system-ui', 'sans-serif'],
          },
          animation: {
            'bounce-slow': 'bounce 2s infinite',
            'pulse-slow': 'pulse 3s infinite',
            'spin-slow': 'spin 3s linear infinite',
          }
        }
      },
      darkMode: 'class'
    }
  </script>
  
  <style type="text/tailwindcss">
    @layer utilities {
      .cell-3d {
        @apply relative transform transition-all duration-200 shadow-md;
      }
      
      .cell-3d:hover {
        @apply -translate-y-1 shadow-lg;
      }
      
      .cell-3d:active {
        @apply translate-y-0 shadow-sm;
      }
      
      .text-shadow {
        text-shadow: 0 2px 4px rgba(0,0,0,0.1);
      }
      
      .text-shadow-lg {
        text-shadow: 0 4px 8px rgba(0,0,0,0.2);
      }
      
      .bg-gradient-primary {
        @apply bg-gradient-to-br from-primary to-secondary;
      }
      
      .bg-gradient-dark {
        @apply bg-gradient-to-br from-dark to-slate-800;
      }
      
      .bg-gradient-light {
        @apply bg-gradient-to-br from-light to-slate-100;
      }
      
      .number-1 { @apply text-blue-500; }
      .number-2 { @apply text-green-500; }
      .number-3 { @apply text-red-500; }
      .number-4 { @apply text-blue-800; }
      .number-5 { @apply text-red-800; }
      .number-6 { @apply text-teal-500; }
      .number-7 { @apply text-black; }
      .number-8 { @apply text-gray-500; }
      
      .confetti {
        position: absolute;
        width: 10px;
        height: 10px;
        opacity: 0;
        animation: confetti 5s ease-in-out infinite;
      }
      
      @keyframes confetti {
        0% {
          transform: translateY(0) rotate(0deg);
          opacity: 1;
        }
        100% {
          transform: translateY(100vh) rotate(720deg);
          opacity: 0;
        }
      }
      
      .mine-explosion {
        position: absolute;
        width: 60px;
        height: 60px;
        border-radius: 50%;
        background: radial-gradient(circle, rgba(255,69,0,1) 0%, rgba(255,140,0,1) 50%, rgba(255,165,0,1) 100%);
        opacity: 0;
        transform: scale(0);
        animation: mineExplosion 0.5s ease-out forwards;
      }
      
      @keyframes mineExplosion {
        0% {
          opacity: 1;
          transform: scale(0);
        }
        50% {
          opacity: 1;
          transform: scale(1);
        }
        100% {
          opacity: 0;
          transform: scale(1.5);
        }
      }
      
      .flag-animation {
        animation: flagWave 0.5s ease-in-out;
      }
      
      @keyframes flagWave {
        0% { transform: rotate(0deg); }
        25% { transform: rotate(10deg); }
        50% { transform: rotate(0deg); }
        75% { transform: rotate(-10deg); }
        100% { transform: rotate(0deg); }
      }
      
      .smiley-face {
        transition: transform 0.2s ease;
      }
      
      .smiley-face:hover {
        transform: scale(1.1);
      }
      
      .smiley-face:active {
        transform: scale(0.95);
      }
      
      .game-board {
        display: grid;
        grid-template-columns: repeat(var(--grid-cols), 1fr);
        grid-template-rows: repeat(var(--grid-rows), 1fr);
        gap: 2px;
      }
      
      .dark .cell {
        @apply bg-slate-700 text-white;
      }
      
      .dark .cell.revealed {
        @apply bg-slate-600;
      }
      
      .dark .cell:hover:not(.revealed) {
        @apply bg-slate-600;
      }
      
      .light .cell {
        @apply bg-slate-200 text-slate-800;
      }
      
      .light .cell.revealed {
        @apply bg-slate-100;
      }
      
      .light .cell:hover:not(.revealed) {
        @apply bg-slate-300;
      }
      
      .leaderboard-item {
        @apply flex justify-between items-center p-2 border-b border-opacity-20;
      }
      
      .leaderboard-item:nth-child(odd) {
        @apply bg-opacity-10;
      }
      
      .leaderboard-item:first-child {
        @apply text-yellow-500 font-bold;
      }
      
      .leaderboard-item:nth-child(2) {
        @apply text-gray-400 font-semibold;
      }
      
      .leaderboard-item:nth-child(3) {
        @apply text-amber-700 font-medium;
      }
      
      .custom-slider {
        @apply appearance-none w-full h-2 rounded-lg bg-slate-200 dark:bg-slate-700;
      }
      
      .custom-slider::-webkit-slider-thumb {
        @apply appearance-none w-4 h-4 rounded-full bg-primary cursor-pointer;
      }
      
      .custom-slider::-moz-range-thumb {
        @apply w-4 h-4 rounded-full bg-primary cursor-pointer border-0;
      }
      
      .modal {
        @apply fixed inset-0 flex items-center justify-center z-50 transition-opacity duration-300;
        background-color: rgba(0, 0, 0, 0.5);
      }
      
      .modal-content {
        @apply relative bg-white dark:bg-slate-800 rounded-lg shadow-xl p-6 max-w-md w-full mx-4 transform transition-transform duration-300;
      }
      
      .modal.hidden {
        @apply opacity-0 pointer-events-none;
      }
      
      .modal.hidden .modal-content {
        @apply translate-y-8;
      }
      
      .tooltip {
        @apply invisible absolute z-10 py-1 px-2 bg-slate-800 text-white text-xs rounded opacity-0 transition-opacity duration-300;
        width: 120px;
        bottom: 125%;
        left: 50%;
        transform: translateX(-50%);
      }
      
      .tooltip::after {
        content: "";
        @apply absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-slate-800;
      }
      
      .has-tooltip:hover .tooltip {
        @apply visible opacity-100;
      }
    }
  </style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center p-4 bg-gradient-light dark:bg-gradient-dark transition-colors duration-300">
  <div class="w-full max-w-3xl">
    <!-- 游戏标题 -->
    <header class="text-center mb-6">
      <h1 class="text-4xl font-bold text-primary dark:text-blue-400 text-shadow-lg mb-2">扫雷</h1>
      <p class="text-slate-600 dark:text-slate-300">经典游戏,全新体验</p>
    </header>
    
    <!-- 游戏控制面板 -->
    <div class="bg-white dark:bg-slate-700 rounded-xl shadow-lg p-4 mb-6 transition-all duration-300">
      <div class="flex flex-wrap items-center justify-between gap-4">
        <!-- 难度选择 -->
        <div class="flex items-center gap-2">
          <label for="difficulty" class="text-sm font-medium text-slate-700 dark:text-slate-300">难度:</label>
          <select id="difficulty" class="bg-slate-100 dark:bg-slate-600 border border-slate-200 dark:border-slate-500 text-slate-700 dark:text-slate-300 rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary">
            <option value="easy">初级 (9×9, 10雷)</option>
            <option value="medium">中级 (16×16, 40雷)</option>
            <option value="hard">高级 (16×30, 99雷)</option>
            <option value="custom">自定义</option>
          </select>
        </div>
        
        <!-- 游戏信息 -->
        <div class="flex items-center gap-6">
          <!-- 地雷计数器 -->
          <div class="flex items-center gap-2">
            <img src="https://p11-doubao-search-sign.byteimg.com/labis/3f387a46336d38186be6e8fb6dbbb292~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=Gw6lzs4Oao1sza428nsMPTnO4oY%3D" alt="地雷" class="w-6 h-6">
            <span id="mine-counter" class="bg-slate-100 dark:bg-slate-600 text-accent font-mono text-xl px-3 py-1 rounded-lg min-w-[60px] text-center">0</span>
          </div>
          
          <!-- 重置按钮 -->
          <button id="reset-btn" class="smiley-face bg-primary hover:bg-primary/90 text-white rounded-full w-10 h-10 flex items-center justify-center shadow-md transition-all">
            <img src="https://p11-doubao-search-sign.byteimg.com/tos-cn-i-be4g95zd3a/1008012349039706117~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=H0VbbA99c4KalLNHqNO0gHq1JHY%3D" alt="笑脸" class="w-6 h-6">
          </button>
          
          <!-- 计时器 -->
          <div class="flex items-center gap-2">
            <i class="fa fa-clock-o text-slate-600 dark:text-slate-300 text-xl"></i>
            <span id="timer" class="bg-slate-100 dark:bg-slate-600 text-primary font-mono text-xl px-3 py-1 rounded-lg min-w-[60px] text-center">0</span>
          </div>
        </div>
        
        <!-- 主题切换和排行榜 -->
        <div class="flex items-center gap-3">
          <!-- 主题切换 -->
          <button id="theme-toggle" class="bg-slate-100 dark:bg-slate-600 text-slate-700 dark:text-slate-300 p-2 rounded-lg hover:bg-slate-200 dark:hover:bg-slate-500 transition-colors">
            <i class="fa fa-moon-o dark:hidden"></i>
            <i class="fa fa-sun-o hidden dark:inline-block"></i>
          </button>
          
          <!-- 排行榜 -->
          <button id="leaderboard-btn" class="bg-slate-100 dark:bg-slate-600 text-slate-700 dark:text-slate-300 p-2 rounded-lg hover:bg-slate-200 dark:hover:bg-slate-500 transition-colors has-tooltip">
            <i class="fa fa-trophy"></i>
            <span class="tooltip">查看排行榜</span>
          </button>
        </div>
      </div>
    </div>
    
    <!-- 自定义难度设置 (默认隐藏) -->
    <div id="custom-settings" class="bg-white dark:bg-slate-700 rounded-xl shadow-lg p-4 mb-6 hidden transition-all duration-300">
      <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
        <div>
          <label for="custom-width" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">宽度:</label>
          <div class="flex items-center gap-2">
            <input type="range" id="custom-width" min="5" max="30" value="16" class="custom-slider flex-1">
            <span id="custom-width-value" class="text-sm font-medium text-slate-700 dark:text-slate-300 min-w-[30px] text-center">16</span>
          </div>
        </div>
        
        <div>
          <label for="custom-height" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">高度:</label>
          <div class="flex items-center gap-2">
            <input type="range" id="custom-height" min="5" max="24" value="16" class="custom-slider flex-1">
            <span id="custom-height-value" class="text-sm font-medium text-slate-700 dark:text-slate-300 min-w-[30px] text-center">16</span>
          </div>
        </div>
        
        <div>
          <label for="custom-mines" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">地雷数:</label>
          <div class="flex items-center gap-2">
            <input type="range" id="custom-mines" min="1" max="99" value="40" class="custom-slider flex-1">
            <span id="custom-mines-value" class="text-sm font-medium text-slate-700 dark:text-slate-300 min-w-[30px] text-center">40</span>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 游戏主区域 -->
    <div class="relative bg-white dark:bg-slate-700 rounded-xl shadow-xl p-4 overflow-hidden transition-all duration-300">
      <div id="game-board" class="game-board mx-auto" style="--grid-cols: 9; --grid-rows: 9;"></div>
      
      <!-- 游戏结果覆盖层 -->
      <div id="game-overlay" class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
        <div class="bg-white dark:bg-slate-800 rounded-xl p-6 text-center max-w-sm w-full mx-4 transform transition-all duration-300 scale-95 opacity-0" id="game-result">
          <h2 id="result-title" class="text-2xl font-bold mb-4 text-primary">游戏胜利!</h2>
          <p id="result-message" class="text-slate-700 dark:text-slate-300 mb-6">你成功找到了所有地雷!</p>
          <div class="flex flex-col gap-3">
            <div class="flex justify-center items-center gap-2">
              <i class="fa fa-clock-o text-primary text-xl"></i>
              <span id="result-time" class="text-xl font-mono">00:00</span>
            </div>
            <button id="play-again-btn" class="bg-primary hover:bg-primary/90 text-white py-2 px-4 rounded-lg transition-colors">再玩一次</button>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 游戏说明 -->
    <div class="mt-6 bg-white dark:bg-slate-700 rounded-xl shadow-lg p-4 transition-all duration-300">
      <h3 class="text-lg font-semibold text-slate-700 dark:text-slate-300 mb-2">游戏说明</h3>
      <ul class="text-sm text-slate-600 dark:text-slate-400 space-y-1">
        <li><span class="font-medium">左键点击:</span> 揭开方块</li>
        <li><span class="font-medium">右键点击:</span> 标记地雷</li>
        <li><span class="font-medium">数字:</span> 表示周围8个格子中的地雷数量</li>
        <li><span class="font-medium">目标:</span> 揭开所有非地雷格子,标记所有地雷</li>
      </ul>
    </div>
  </div>
  
  <!-- 排行榜模态框 -->
  <div id="leaderboard-modal" class="modal hidden">
    <div class="modal-content">
      <div class="flex justify-between items-center mb-4">
        <h3 class="text-xl font-bold text-slate-700 dark:text-slate-300">排行榜</h3>
        <button id="close-leaderboard" class="text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">
          <i class="fa fa-times"></i>
        </button>
      </div>
      
      <div class="mb-4">
        <select id="leaderboard-difficulty" class="w-full bg-slate-100 dark:bg-slate-600 border border-slate-200 dark:border-slate-500 text-slate-700 dark:text-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary">
          <option value="easy">初级 (9×9, 10雷)</option>
          <option value="medium">中级 (16×16, 40雷)</option>
          <option value="hard">高级 (16×30, 99雷)</option>
        </select>
      </div>
      
      <div id="leaderboard-list" class="max-h-80 overflow-y-auto bg-slate-50 dark:bg-slate-700 rounded-lg">
        <!-- 排行榜内容将通过JavaScript动态生成 -->
      </div>
      
      <div class="mt-4 text-center">
        <button id="clear-leaderboard" class="text-sm text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300">清空排行榜</button>
      </div>
    </div>
  </div>
  
  <!-- 输入用户名模态框 -->
  <div id="username-modal" class="modal hidden">
    <div class="modal-content">
      <h3 class="text-xl font-bold text-slate-700 dark:text-slate-300 mb-4">恭喜!</h3>
      <p class="text-slate-600 dark:text-slate-400 mb-4">你创造了新的记录,请输入你的名字:</p>
      
      <div class="mb-4">
        <input type="text" id="username-input" maxlength="10" class="w-full bg-slate-100 dark:bg-slate-600 border border-slate-200 dark:border-slate-500 text-slate-700 dark:text-slate-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary" placeholder="输入你的名字">
      </div>
      
      <div class="flex justify-end">
        <button id="save-username" class="bg-primary hover:bg-primary/90 text-white py-2 px-4 rounded-lg transition-colors">保存</button>
      </div>
    </div>
  </div>

  <script>
    // 游戏状态
    const gameState = {
      width: 9,
      height: 9,
      mines: 10,
      cells: [],
      revealedCells: 0,
      flaggedCells: 0,
      isGameOver: false,
      isGameWon: false,
      startTime: null,
      timer: null,
      currentTime: 0,
      difficulty: 'easy'
    };
    
    // 难度设置
    const difficultySettings = {
      easy: { width: 9, height: 9, mines: 10 },
      medium: { width: 16, height: 16, mines: 40 },
      hard: { width: 30, height: 16, mines: 99 }
    };
    
    // DOM 元素
    const gameBoard = document.getElementById('game-board');
    const mineCounter = document.getElementById('mine-counter');
    const timerElement = document.getElementById('timer');
    const resetBtn = document.getElementById('reset-btn');
    const difficultySelect = document.getElementById('difficulty');
    const customSettings = document.getElementById('custom-settings');
    const customWidth = document.getElementById('custom-width');
    const customHeight = document.getElementById('custom-height');
    const customMines = document.getElementById('custom-mines');
    const customWidthValue = document.getElementById('custom-width-value');
    const customHeightValue = document.getElementById('custom-height-value');
    const customMinesValue = document.getElementById('custom-mines-value');
    const themeToggle = document.getElementById('theme-toggle');
    const gameOverlay = document.getElementById('game-overlay');
    const gameResult = document.getElementById('game-result');
    const resultTitle = document.getElementById('result-title');
    const resultMessage = document.getElementById('result-message');
    const resultTime = document.getElementById('result-time');
    const playAgainBtn = document.getElementById('play-again-btn');
    const leaderboardBtn = document.getElementById('leaderboard-btn');
    const leaderboardModal = document.getElementById('leaderboard-modal');
    const closeLeaderboard = document.getElementById('close-leaderboard');
    const leaderboardDifficulty = document.getElementById('leaderboard-difficulty');
    const leaderboardList = document.getElementById('leaderboard-list');
    const clearLeaderboard = document.getElementById('clear-leaderboard');
    const usernameModal = document.getElementById('username-modal');
    const usernameInput = document.getElementById('username-input');
    const saveUsername = document.getElementById('save-username');
    
    // 图标
    const mineIcon = 'https://p11-doubao-search-sign.byteimg.com/labis/3f387a46336d38186be6e8fb6dbbb292~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=Gw6lzs4Oao1sza428nsMPTnO4oY%3D';
    const flagIcon = 'https://p11-doubao-search-sign.byteimg.com/labis/3a96a1b270b1cd1a946f1f69f6cd371f~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=VzkBIqZPOtoBAypRKnlzuCDvUGg%3D';
    const smileyIcon = 'https://p11-doubao-search-sign.byteimg.com/tos-cn-i-be4g95zd3a/1008012349039706117~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=H0VbbA99c4KalLNHqNO0gHq1JHY%3D';
    const smileyOohIcon = 'https://p11-doubao-search-sign.byteimg.com/tos-cn-i-be4g95zd3a/1978633691853619215~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=it5Lye0g%2FGmLUCwDRjlWZ55Ogi4%3D';
    const smileyLoseIcon = 'https://p11-doubao-search-sign.byteimg.com/tos-cn-i-be4g95zd3a/1008012349039706117~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=H0VbbA99c4KalLNHqNO0gHq1JHY%3D';
    const smileyWinIcon = 'https://p11-doubao-search-sign.byteimg.com/tos-cn-i-be4g95zd3a/1008012349039706117~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1777797012&x-signature=H0VbbA99c4KalLNHqNO0gHq1JHY%3D';
    
    // 颜色映射
    const numberColors = [
      '', // 0
      'number-1', // 1
      'number-2', // 2
      'number-3', // 3
      'number-4', // 4
      'number-5', // 5
      'number-6', // 6
      'number-7', // 7
      'number-8'  // 8
    ];
    
    // 初始化游戏
    function initGame() {
      // 重置游戏状态
      gameState.cells = [];
      gameState.revealedCells = 0;
      gameState.flaggedCells = 0;
      gameState.isGameOver = false;
      gameState.isGameWon = false;
      gameState.startTime = null;
      gameState.currentTime = 0;
      
      // 更新地雷计数器
      updateMineCounter();
      
      // 重置计时器
      resetTimer();
      
      // 更新笑脸图标
      updateSmileyIcon('normal');
      
      // 创建游戏板
      createGameBoard();
      
      // 隐藏游戏结果覆盖层
      gameOverlay.classList.add('hidden');
    }
    
    // 创建游戏板
    function createGameBoard() {
      // 清空游戏板
      gameBoard.innerHTML = '';
      
      // 设置网格大小
      gameBoard.style.setProperty('--grid-cols', gameState.width);
      gameBoard.style.setProperty('--grid-rows', gameState.height);
      
      // 计算单元格大小
      const maxWidth = gameBoard.parentElement.clientWidth - 16; // 减去内边距
      const maxHeight = window.innerHeight * 0.6; // 最大高度为窗口高度的60%
      const cellWidth = maxWidth / gameState.width;
      const cellHeight = maxHeight / gameState.height;
      const cellSize = Math.min(cellWidth, cellHeight);
      
      // 创建单元格
      for (let row = 0; row < gameState.height; row++) {
        gameState.cells[row] = [];
        
        for (let col = 0; col < gameState.width; col++) {
          // 创建单元格对象
          const cell = {
            row,
            col,
            isMine: false,
            isRevealed: false,
            isFlagged: false,
            adjacentMines: 0
          };
          
          gameState.cells[row][col] = cell;
          
          // 创建单元格元素
          const cellElement = document.createElement('div');
          cellElement.classList.add('cell', 'cell-3d', 'flex', 'items-center', 'justify-center', 'rounded-lg', 'cursor-pointer', 'text-lg', 'font-bold');
          cellElement.style.width = `${cellSize}px`;
          cellElement.style.height = `${cellSize}px`;
          cellElement.dataset.row = row;
          cellElement.dataset.col = col;
          
          // 添加点击事件
          cellElement.addEventListener('click', () => handleCellClick(row, col));
          cellElement.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            handleCellRightClick(row, col);
          });
          
          // 添加鼠标按下和释放事件以更新笑脸图标
          cellElement.addEventListener('mousedown', () => {
            if (!gameState.isGameOver && !gameState.isGameWon) {
              updateSmileyIcon('ooh');
            }
          });
          
          document.addEventListener('mouseup', () => {
            if (!gameState.isGameOver && !gameState.isGameWon) {
              updateSmileyIcon('normal');
            }
          });
          
          gameBoard.appendChild(cellElement);
        }
      }
      
      // 放置地雷
      placeMines();
      
      // 计算相邻地雷数量
      calculateAdjacentMines();
    }
    
    // 放置地雷
    function placeMines() {
      let minesPlaced = 0;
      
      while (minesPlaced < gameState.mines) {
        const row = Math.floor(Math.random() * gameState.height);
        const col = Math.floor(Math.random() * gameState.width);
        
        if (!gameState.cells[row][col].isMine) {
          gameState.cells[row][col].isMine = true;
          minesPlaced++;
        }
      }
    }
    
    // 计算相邻地雷数量
    function calculateAdjacentMines() {
      for (let row = 0; row < gameState.height; row++) {
        for (let col = 0; col < gameState.width; col++) {
          if (!gameState.cells[row][col].isMine) {
            let count = 0;
            
            // 检查周围8个单元格
            for (let r = Math.max(0, row - 1); r <= Math.min(gameState.height - 1, row + 1); r++) {
              for (let c = Math.max(0, col - 1); c <= Math.min(gameState.width - 1, col + 1); c++) {
                if (gameState.cells[r][c].isMine) {
                  count++;
                }
              }
            }
            
            gameState.cells[row][col].adjacentMines = count;
          }
        }
      }
    }
    
    // 处理单元格点击
    function handleCellClick(row, col) {
      const cell = gameState.cells[row][col];
      
      // 如果游戏已结束、单元格已揭开或已标记,则不做任何操作
      if (gameState.isGameOver || gameState.isGameWon || cell.isRevealed || cell.isFlagged) {
        return;
      }
      
      // 如果是第一次点击,开始计时
      if (!gameState.startTime) {
        startTimer();
      }
      
      // 如果点击的是地雷,游戏结束
      if (cell.isMine) {
        revealMine(row, col);
        gameOver(false);
        return;
      }
      
      // 揭开单元格
      revealCell(row, col);
      
      // 检查游戏是否胜利
      checkGameWin();
    }
    
    // 处理单元格右键点击
    function handleCellRightClick(row, col) {
      const cell = gameState.cells[row][col];
      
      // 如果游戏已结束或单元格已揭开,则不做任何操作
      if (gameState.isGameOver || gameState.isGameWon || cell.isRevealed) {
        return;
      }
      
      // 如果是第一次点击,开始计时
      if (!gameState.startTime) {
        startTimer();
      }
      
      // 切换标记状态
      cell.isFlagged = !cell.isFlagged;
      
      // 更新游戏状态
      if (cell.isFlagged) {
        gameState.flaggedCells++;
      } else {
        gameState.flaggedCells--;
      }
      
      // 更新地雷计数器
      updateMineCounter();
      
      // 更新单元格显示
      updateCellDisplay(row, col);
      
      // 添加旗帜动画
      const cellElement = getCellElement(row, col);
      if (cell.isFlagged) {
        cellElement.classList.add('flag-animation');
        setTimeout(() => {
          cellElement.classList.remove('flag-animation');
        }, 500);
      }
    }
    
    // 揭开单元格
    function revealCell(row, col) {
      const cell = gameState.cells[row][col];
      
      // 如果单元格已揭开、已标记或是地雷,则不做任何操作
      if (cell.isRevealed || cell.isFlagged || cell.isMine) {
        return;
      }
      
      // 揭开单元格
      cell.isRevealed = true;
      gameState.revealedCells++;
      
      // 更新单元格显示
      updateCellDisplay(row, col);
      
      // 如果相邻地雷数量为0,递归揭开周围单元格
      if (cell.adjacentMines === 0) {
        for (let r = Math.max(0, row - 1); r <= Math.min(gameState.height - 1, row + 1); r++) {
          for (let c = Math.max(0, col - 1); c <= Math.min(gameState.width - 1, col + 1); c++) {
            revealCell(r, c);
          }
        }
      }
    }
    
    // 揭开地雷
    function revealMine(row, col) {
      const cell = gameState.cells[row][col];
      
      // 揭开地雷
      cell.isRevealed = true;
      
      // 更新单元格显示
      updateCellDisplay(row, col);
      
      // 添加爆炸动画
      const cellElement = getCellElement(row, col);
      const explosion = document.createElement('div');
      explosion.classList.add('mine-explosion');
      
      // 计算爆炸位置
      const rect = cellElement.getBoundingClientRect();
      const boardRect = gameBoard.getBoundingClientRect();
      explosion.style.left = `${rect.left - boardRect.left + rect.width / 2}px`;
      explosion.style.top = `${rect.top - boardRect.top + rect.height / 2}px`;
      
      gameBoard.appendChild(explosion);
      
      // 移除爆炸动画
      setTimeout(() => {
        explosion.remove();
      }, 500);
      
      // 显示所有地雷
      for (let r = 0; r < gameState.height; r++) {
        for (let c = 0; c < gameState.width; c++) {
          if (gameState.cells[r][c].isMine && !gameState.cells[r][c].isRevealed) {
            gameState.cells[r][c].isRevealed = true;
            updateCellDisplay(r, c);
          }
        }
      }
    }
    
    // 更新单元格显示
    function updateCellDisplay(row, col) {
      const cell = gameState.cells[row][col];
      const cellElement = getCellElement(row, col);
      
      // 清空单元格内容
      cellElement.innerHTML = '';
      
      // 如果单元格已揭开
      if (cell.isRevealed) {
        cellElement.classList.add('revealed');
        cellElement.classList.remove('cursor-pointer');
        
        // 如果是地雷,显示地雷图标
        if (cell.isMine) {
          const img = document.createElement('img');
          img.src = mineIcon;
          img.alt = '地雷';
          img.classList.add('w-6', 'h-6');
          cellElement.appendChild(img);
        } 
        // 如果有相邻地雷,显示数字
        else if (cell.adjacentMines > 0) {
          cellElement.textContent = cell.adjacentMines;
          cellElement.classList.add(numberColors[cell.adjacentMines]);
        }
      } 
      // 如果单元格已标记,显示旗帜图标
      else if (cell.isFlagged) {
        const img = document.createElement('img');
        img.src = flagIcon;
        img.alt = '旗帜';
        img.classList.add('w-6', 'h-6');
        cellElement.appendChild(img);
      }
    }
    
    // 获取单元格元素
    function getCellElement(row, col) {
      return document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
    }
    
    // 更新地雷计数器
    function updateMineCounter() {
      const remainingMines = gameState.mines - gameState.flaggedCells;
      mineCounter.textContent = remainingMines < 0 ? 0 : remainingMines;
    }
    
    // 开始计时器
    function startTimer() {
      gameState.startTime = Date.now();
      
      // 清除之前的计时器
      if (gameState.timer) {
        clearInterval(gameState.timer);
      }
      
      // 设置新的计时器
      gameState.timer = setInterval(() => {
        gameState.currentTime = Math.floor((Date.now() - gameState.startTime) / 1000);
        updateTimerDisplay();
      }, 1000);
    }
    
    // 重置计时器
    function resetTimer() {
      if (gameState.timer) {
        clearInterval(gameState.timer);
        gameState.timer = null;
      }
      
      gameState.currentTime = 0;
      updateTimerDisplay();
    }
    
    // 更新计时器显示
    function updateTimerDisplay() {
      const minutes = Math.floor(gameState.currentTime / 60);
      const seconds = gameState.currentTime % 60;
      timerElement.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
    }
    
    // 更新笑脸图标
    function updateSmileyIcon(state) {
      let iconSrc;
      
      switch (state) {
        case 'normal':
          iconSrc = smileyIcon;
          break;
        case 'ooh':
          iconSrc = smileyOohIcon;
          break;
        case 'win':
          iconSrc = smileyWinIcon;
          break;
        case 'lose':
          iconSrc = smileyLoseIcon;
          break;
        default:
          iconSrc = smileyIcon;
      }
      
      resetBtn.innerHTML = `<img src="${iconSrc}" alt="笑脸" class="w-6 h-6">`;
    }
    
    // 检查游戏是否胜利
    function checkGameWin() {
      // 计算需要揭开的非地雷单元格数量
      const totalNonMineCells = gameState.width * gameState.height - gameState.mines;
      
      // 如果已揭开所有非地雷单元格,游戏胜利
      if (gameState.revealedCells === totalNonMineCells) {
        gameOver(true);
      }
    }
    
    // 游戏结束
    function gameOver(isWin) {
      gameState.isGameOver = true;
      gameState.isGameWon = isWin;
      
      // 停止计时器
      if (gameState.timer) {
        clearInterval(gameState.timer);
      }
      
      // 更新笑脸图标
      updateSmileyIcon(isWin ? 'win' : 'lose');
      
      // 显示游戏结果
      showGameResult(isWin);
      
      // 如果是胜利,添加庆祝效果
      if (isWin) {
        createConfetti();
        
        // 检查是否是新记录
        checkNewRecord();
      }
    }
    
    // 显示游戏结果
    function showGameResult(isWin) {
      // 设置结果标题和消息
      resultTitle.textContent = isWin ? '游戏胜利!' : '游戏失败!';
      resultMessage.textContent = isWin ? '你成功找到了所有地雷!' : '你踩到地雷了!';
      resultMessage.className = isWin ? 'text-green-600 dark:text-green-400 mb-6' : 'text-red-600 dark:text-red-400 mb-6';
      
      // 设置结果时间
      resultTime.textContent = timerElement.textContent;
      
      // 显示游戏结果覆盖层
      gameOverlay.classList.remove('hidden');
      
      // 添加动画效果
      setTimeout(() => {
        gameResult.classList.remove('scale-95', 'opacity-0');
        gameResult.classList.add('scale-100', 'opacity-100');
      }, 10);
    }
    
    // 创建庆祝效果
    function createConfetti() {
      const colors = ['#f94144', '#f3722c', '#f8961e', '#f9c74f', '#90be6d', '#43aa8b', '#577590'];
      const confettiContainer = document.createElement('div');
      confettiContainer.classList.add('absolute', 'inset-0', 'pointer-events-none');
      gameBoard.appendChild(confettiContainer);
      
      // 创建100个彩色纸屑
      for (let i = 0; i < 100; i++) {
        const confetti = document.createElement('div');
        confetti.classList.add('confetti');
        confetti.style.left = `${Math.random() * 100}%`;
        confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
        confetti.style.animationDelay = `${Math.random() * 5}s`;
        confettiContainer.appendChild(confetti);
      }
      
      // 5秒后移除庆祝效果
      setTimeout(() => {
        confettiContainer.remove();
      }, 5000);
    }
    
    // 检查是否是新记录
    function checkNewRecord() {
      // 如果是自定义难度,不记录成绩
      if (gameState.difficulty === 'custom') {
        return;
      }
      
      // 获取排行榜数据
      const leaderboard = getLeaderboard(gameState.difficulty);
      
      // 如果成绩能进入排行榜前10名,或者排行榜未满10名
      if (leaderboard.length < 10 || gameState.currentTime < leaderboard[leaderboard.length - 1].time) {
        // 显示输入用户名模态框
        showUsernameModal();
      }
    }
    
    // 显示输入用户名模态框
    function showUsernameModal() {
      usernameModal.classList.remove('hidden');
      usernameInput.focus();
    }
    
    // 保存用户名和成绩
    function saveUserScore(username) {
      // 如果用户名为空,使用默认名称
      if (!username.trim()) {
        username = '匿名玩家';
      }
      
      // 获取排行榜数据
      const leaderboard = getLeaderboard(gameState.difficulty);
      
      // 添加新成绩
      leaderboard.push({
        username: username.trim(),
        time: gameState.currentTime,
        date: new Date().toISOString()
      });
      
      // 按时间排序
      leaderboard.sort((a, b) => a.time - b.time);
      
      // 只保留前10名
      if (leaderboard.length > 10) {
        leaderboard.pop();
      }
      
      // 保存排行榜数据
      localStorage.setItem(`minesweeper_leaderboard_${gameState.difficulty}`, JSON.stringify(leaderboard));
      
      // 隐藏输入用户名模态框
      usernameModal.classList.add('hidden');
      
      // 显示排行榜
      showLeaderboard(gameState.difficulty);
    }
    
    // 获取排行榜数据
    function getLeaderboard(difficulty) {
      const leaderboardData = localStorage.getItem(`minesweeper_leaderboard_${difficulty}`);
      return leaderboardData ? JSON.parse(leaderboardData) : [];
    }
    
    // 显示排行榜
    function showLeaderboard(difficulty) {
      // 获取排行榜数据
      const leaderboard = getLeaderboard(difficulty);
      
      // 清空排行榜列表
      leaderboardList.innerHTML = '';
      
      // 如果排行榜为空,显示提示信息
      if (leaderboard.length === 0) {
        const emptyMessage = document.createElement('div');
        emptyMessage.classList.add('p-4', 'text-center', 'text-slate-500', 'dark:text-slate-400');
        emptyMessage.textContent = '暂无记录';
        leaderboardList.appendChild(emptyMessage);
        return;
      }
      
      // 添加排行榜项目
      leaderboard.forEach((entry, index) => {
        const item = document.createElement('div');
        item.classList.add('leaderboard-item');
        
        // 格式化时间
        const minutes = Math.floor(entry.time / 60);
        const seconds = entry.time % 60;
        const formattedTime = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
        
        // 格式化日期
        const date = new Date(entry.date);
        const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
        
        item.innerHTML = `
          <span class="font-medium">${index + 1}</span>
          <span class="truncate">${entry.username}</span>
          <span class="font-mono">${formattedTime}</span>
          <span class="text-xs text-slate-500 dark:text-slate-400">${formattedDate}</span>
        `;
        
        leaderboardList.appendChild(item);
      });
      
      // 显示排行榜模态框
      leaderboardModal.classList.remove('hidden');
    }
    
    // 清空排行榜
    function clearLeaderboardData(difficulty) {
      if (confirm('确定要清空排行榜吗?')) {
        localStorage.removeItem(`minesweeper_leaderboard_${difficulty}`);
        showLeaderboard(difficulty);
      }
    }
    
    // 初始化主题
    function initTheme() {
      // 检查用户偏好
      if (localStorage.getItem('minesweeper_theme') === 'dark' || 
          (!localStorage.getItem('minesweeper_theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
        document.documentElement.classList.add('dark');
      } else {
        document.documentElement.classList.remove('dark');
      }
    }
    
    // 切换主题
    function toggleTheme() {
      if (document.documentElement.classList.contains('dark')) {
        document.documentElement.classList.remove('dark');
        localStorage.setItem('minesweeper_theme', 'light');
      } else {
        document.documentElement.classList.add('dark');
        localStorage.setItem('minesweeper_theme', 'dark');
      }
    }
    
    // 事件监听器
    function setupEventListeners() {
      // 重置按钮
      resetBtn.addEventListener('click', initGame);
      
      // 难度选择
      difficultySelect.addEventListener('change', () => {
        const difficulty = difficultySelect.value;
        
        // 更新游戏状态
        gameState.difficulty = difficulty;
        
        // 如果是自定义难度,显示自定义设置
        if (difficulty === 'custom') {
          customSettings.classList.remove('hidden');
          
          // 使用自定义设置
          gameState.width = parseInt(customWidth.value);
          gameState.height = parseInt(customHeight.value);
          gameState.mines = parseInt(customMines.value);
        } else {
          customSettings.classList.add('hidden');
          
          // 使用预设难度
          const settings = difficultySettings[difficulty];
          gameState.width = settings.width;
          gameState.height = settings.height;
          gameState.mines = settings.mines;
          
          // 更新自定义设置滑块
          customWidth.value = settings.width;
          customWidthValue.textContent = settings.width;
          customHeight.value = settings.height;
          customHeightValue.textContent = settings.height;
          customMines.value = settings.mines;
          customMinesValue.textContent = settings.mines;
        }
        
        // 初始化游戏
        initGame();
      });
      
      // 自定义宽度滑块
      customWidth.addEventListener('input', () => {
        const value = parseInt(customWidth.value);
        customWidthValue.textContent = value;
        
        // 更新游戏状态
        gameState.width = value;
        
        // 更新地雷数量限制
        const maxMines = value * gameState.height - 1;
        customMines.max = maxMines;
        
        if (gameState.mines > maxMines) {
          gameState.mines = maxMines;
          customMines.value = maxMines;
          customMinesValue.textContent = maxMines;
        }
        
        // 如果当前难度是自定义,重新初始化游戏
        if (gameState.difficulty === 'custom') {
          initGame();
        }
      });
      
      // 自定义高度滑块
      customHeight.addEventListener('input', () => {
        const value = parseInt(customHeight.value);
        customHeightValue.textContent = value;
        
        // 更新游戏状态
        gameState.height = value;
        
        // 更新地雷数量限制
        const maxMines = gameState.width * value - 1;
        customMines.max = maxMines;
        
        if (gameState.mines > maxMines) {
          gameState.mines = maxMines;
          customMines.value = maxMines;
          customMinesValue.textContent = maxMines;
        }
        
        // 如果当前难度是自定义,重新初始化游戏
        if (gameState.difficulty === 'custom') {
          initGame();
        }
      });
      
      // 自定义地雷数量滑块
      customMines.addEventListener('input', () => {
        const value = parseInt(customMines.value);
        customMinesValue.textContent = value;
        
        // 更新游戏状态
        gameState.mines = value;
        
        // 如果当前难度是自定义,重新初始化游戏
        if (gameState.difficulty === 'custom') {
          initGame();
        }
      });
      
      // 主题切换
      themeToggle.addEventListener('click', toggleTheme);
      
      // 再玩一次按钮
      playAgainBtn.addEventListener('click', initGame);
      
      // 排行榜按钮
      leaderboardBtn.addEventListener('click', () => {
        showLeaderboard(gameState.difficulty);
      });
      
      // 关闭排行榜
      closeLeaderboard.addEventListener('click', () => {
        leaderboardModal.classList.add('hidden');
      });
      
      // 排行榜难度选择
      leaderboardDifficulty.addEventListener('change', () => {
        showLeaderboard(leaderboardDifficulty.value);
      });
      
      // 清空排行榜
      clearLeaderboard.addEventListener('click', () => {
        clearLeaderboardData(leaderboardDifficulty.value);
      });
      
      // 保存用户名
      saveUsername.addEventListener('click', () => {
        saveUserScore(usernameInput.value);
      });
      
      // 用户名输入框回车事件
      usernameInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
          saveUserScore(usernameInput.value);
        }
      });
      
      // 点击模态框背景关闭模态框
      leaderboardModal.addEventListener('click', (e) => {
        if (e.target === leaderboardModal) {
          leaderboardModal.classList.add('hidden');
        }
      });
      
      usernameModal.addEventListener('click', (e) => {
        if (e.target === usernameModal) {
          usernameModal.classList.add('hidden');
        }
      });
      
      // 窗口大小改变时重新调整游戏板大小
      window.addEventListener('resize', () => {
        if (!gameState.isGameOver && !gameState.isGameWon) {
          createGameBoard();
        }
      });
    }
    
    // 初始化游戏
    function init() {
      // 初始化主题
      initTheme();
      
      // 设置事件监听器
      setupEventListeners();
      
      // 初始化游戏
      initGame();
    }
    
    // 启动游戏
    init();
  </script>
</body>
</html>

展示画面

相关推荐
wanhengidc11 小时前
云手机是由什么组成的?
运维·服务器·web安全·游戏·智能手机
世洋Blog16 小时前
利用<<左移运算符优雅的设计游戏能力的任意组合和判断
游戏·unity·c#
da_vinci_x18 小时前
PS 3D Viewer (Beta):概念美术的降维打击,白模直接在PS里转光打影出5张大片
人工智能·游戏·3d·prompt·aigc·材质·游戏美术
め.19 小时前
用Unity复刻童年经典游戏—愤怒的小鸟
游戏
喵了几个咪19 小时前
游戏字体渲染
开发语言·python·游戏
张丶大帅20 小时前
别踩白块游戏(附源代码)
c语言·游戏
2501_9400940220 小时前
模拟器全部BIOS合集 RetroArch BIOS 解决模拟器提示缺少bios的问题 通用所有游戏模拟器
游戏
wanhengidc21 小时前
跨境电商为什么依赖于云手机
运维·服务器·游戏·智能手机·云计算
2501_915106321 天前
游戏上架 App Store 的技术流程解析 从构建到审核的全流程指南
游戏·macos·ios·小程序·uni-app·cocoa·iphone