Advertisement

网页版扫雷小游戏制作过程(附全部源码)

阅读量:

游戏概述

一款经典网页版扫雷游戏《 Minesweeper 》采用HTML、CSS和JavaScript技术开发。界面设计精美且用户体验良好。严格遵循经典的扫雷规则,并配备了现代感的视觉效果。

游戏布局

1.五种难度级别:

初级:九乘九的格子阵中设置十个地雷
中级:十六乘十六的格子阵中设置四十个地雷
高级:十六乘三十的格子阵中设置九十九个地雷
专家:二十乘三十的格子阵中设置一百五十个地雷
大师:二十乘四十的格子阵中设置两百个地雷

2.UI设计简洁美观:

平滑的动画过渡
响应式布局适配不同设备
直观的颜色编码数字

3.游戏机制:

初次操作必然是安全的
通过右键操作可以标记出可疑的地雷区域
系统会自动处理并展示未标记的地雷区域
游戏提供实时的时间显示以及剩余地雷数量统计

动画展示

技术亮点

改写说明

经过优化的算法方案中包含两部分技术要点:首先采用随机数算法进行地雷生成,并在初始点击时保证安全性;其次对于空旷区域的扩展,则运用递归方法模拟水位扩散的效果。

状态管理:使用JavaScript对象集中管理游戏状态,保持数据与UI同步。

事件处理:合理使用事件委托技术高效处理大量格子元素的点击事件。

CSS Grid布局:采用现代CSS布局方案来实现灵活构建游戏板,并支持多种难度的游戏模式。

CSS Grid在游戏板中的深层应用体现在其灵活配置的动态网格布局上。这种布局方式通过grid-template-columns: repeat(${cols}, 1fr)来设定可变列数,并确保每一网格项都能智能地调整尺寸以适应内容需求。

通过Flexbox实现主菜单及按钮组的居中排列,在响应式设计过程中, 游戏信息区域的空间分布采用弹性布局策略

交互式动画与视觉过渡效果,在@keyframes指令下完成界面元素的渐出显示。通过@keyframes技术实现界面元素的渐出显示的同时,则可结合transition技术通过按钮悬停状态下的性能优化来实现动画方案的设计。

高效的DOM交互方式用于实现动态生成游戏板组件,并对批量DOM操作进行优化设计;同时通过事件代理机制实现应用功能的提升与稳定性增强

遵循代码模块化策略,在本系统中实施的游戏引擎逻辑与用户界面呈现分离原则下,各功能模块应承担单一职责以确保系统状态维护与用户界面更新之间的清晰边界。

响应式设计框架中构建移动设备端适配方案,并合理运用触控及鼠标事件的一致性处理

性能优化方案;高效计时器管理策略;渲染性能分析与评估;内存泄漏防范机制

游戏核心设计机制

游戏的设计理念主要围绕逻辑推理与概率判断展开。在玩家在游戏中会遇到一个矩形网格界面,在这个界面中你需要通过点击各个方块来进行信息的逐步显示。在这个过程中你需要特别注意不要触碰带有地雷的区域。每一个被点击打开的方块上都会显示出一个数字这个数字表示周围八个方块中潜在的地雷数量。根据这些显示的数据你将需要运用一定的逻辑推理能力来判定哪些未被打开但可能含有地雷的方块以及哪些已经确定为安全可以放心点击的位置。

游戏引入了"首次点击均无风险"的标准扫雷规则。每当玩家在一个无相邻地雷的空白方块上进行首次点击时,在这种情况下系统会自动揭示所有连通区域内的空白方块。这一规定显著减少了早期扫雷游戏中因胡猜乱点而导致的问题,并使整个游戏流程更加注重逻辑推理而非随机运气因素的影响。

部分核心代码分析

游戏状态管理

复制代码
 const gameState = {

    
     level: 1,
    
     board: [],
    
     mineCount: 0,
    
     revealedCount: 0,
    
     flaggedCount: 0,
    
     gameOver: false,
    
     gameWon: false,
    
     firstClick: true,
    
     startTime: 0,
    
     timerInterval: null,
    
     elapsedTime: 0,
    
     levels: [
    
     { rows: 9, cols: 9, mines: 10 },   // 初级
    
     { rows: 16, cols: 16, mines: 40 },  // 中级
    
     { rows: 16, cols: 30, mines: 99 },  // 高级
    
     { rows: 20, cols: 30, mines: 150 }, // 专家
    
     { rows: 20, cols: 40, mines: 200 }  // 大师
    
     ]
    
 };

这段代码定义了游戏的核心状态对象,包含:

当前游戏的难度等级
采用网格结构的游戏面板
包含地雷总数以及已识别的地雷数量
反映游戏运行中的动态信息
涉及计时器所使用的控制变量
包含五个预设配置方案

游戏板初始化

复制代码
 function createBoard() {

    
     const level = gameState.levels[gameState.level - 1];
    
     gameState.board = [];
    
     
    
     elements.gameBoard.innerHTML = '';
    
     elements.gameBoard.style.gridTemplateColumns = `repeat(${level.cols}, 1fr)`;
    
  
    
     for (let row = 0; row < level.rows; row++) {
    
     gameState.board[row] = [];
    
     for (let col = 0; col < level.cols; col++) {
    
         gameState.board[row][col] = {
    
             isMine: false,
    
             revealed: false,
    
             flagged: false,
    
             neighborMines: 0
    
         };
    
  
    
         const cell = document.createElement('div');
    
         cell.className = 'cell';
    
         cell.dataset.row = row;
    
         cell.dataset.col = col;
    
  
    
         cell.addEventListener('click', () => handleCellClick(row, col));
    
         cell.addEventListener('contextmenu', (e) => {
    
             e.preventDefault();
    
             handleRightClick(row, col);
    
         });
    
  
    
         elements.gameBoard.appendChild(cell);
    
     }
    
     }
    
 }

基于难度级别设定的游戏板将被生成。通过CSS Grid布局将实现游戏板列数的配置。初始化一个二维数组来表示游戏的状态。每个格子将被赋予对应的DOM元素,并配置相应的事件监听机制。

地雷放置算法

复制代码
 function placeMines(firstRow, firstCol) {

    
     const level = gameState.levels[gameState.level - 1];
    
     let minesPlaced = 0;
    
     
    
     // 确保第一次点击的格子及其周围没有地雷
    
     const safeZone = [];
    
     for (let r = Math.max(0, firstRow - 1); r <= Math.min(level.rows - 1, firstRow + 1); r++) {
    
     for (let c = Math.max(0, firstCol - 1); c <= Math.min(level.cols - 1, firstCol + 1); c++) {
    
         safeZone.push(`${r},${c}`);
    
     }
    
     }
    
  
    
     // 随机放置地雷
    
     while (minesPlaced < level.mines) {
    
     const row = Math.floor(Math.random() * level.rows);
    
     const col = Math.floor(Math.random() * level.cols);
    
  
    
     if (!safeZone.includes(`${row},${col}`) && !gameState.board[row][col].isMine) {
    
         gameState.board[row][col].isMine = true;
    
         minesPlaced++;
    
     }
    
     }
    
  
    
     calculateNeighborMines();
    
     startTimer();
    
 }

随后,在玩家首次进行操作后立即开始布置第一颗地雷。
为了确保游戏初期的安全性,在玩家首次点击位置周围的九宫格内所有单元格均需确认无风险。
基于概率算法的原理,在剩余的地雷数量中采用随机的方式进行均匀分布。
通过精确的数据统计方法,在每一个单元格周围统计并记录潜在的地雷数量。
启动游戏计时器将触发整个游戏流程的开始阶段,并记录每一次操作的时间数据。

点击格子逻辑

复制代码
 function revealCell(row, col) {

    
     const level = gameState.levels[gameState.level - 1];
    
  
    
     if (row < 0 || row >= level.rows || col < 0 || col >= level.cols ||
    
     gameState.board[row][col].revealed || gameState.board[row][col].flagged) {
    
     return;
    
     }
    
  
    
     gameState.board[row][col].revealed = true;
    
     gameState.revealedCount++;
    
  
    
     const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
    
     cellElement.classList.add('revealed');
    
  
    
     if (gameState.board[row][col].neighborMines > 0) {
    
     cellElement.textContent = gameState.board[row][col].neighborMines;
    
     cellElement.classList.add(`number-${gameState.board[row][col].neighborMines}`);
    
     return;
    
     }
    
  
    
     // 递归揭开空白区域
    
     for (let r = Math.max(0, row - 1); r <= Math.min(level.rows - 1, row + 1); r++) {
    
     for (let c = Math.max(0, col - 1); c <= Math.min(level.cols - 1, col + 1); c++) {
    
         if (r !== row || c !== col) {
    
             revealCell(r, c);
    
         }
    
     }
    
     }
    
 }

边界条件检测及状态验证
将单元格标记为已翻开
显示数字(若附近有地雷)
递归揭示空白区域(类似于水位填充算法)

游戏代码

复制代码
 <!DOCTYPE html>

    
 <html lang="zh-CN">
    
  
    
 <head>
    
     <meta charset="UTF-8">
    
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
     <title>扫雷小游戏</title>
    
     <style>
    
     * {
    
         margin: 0;
    
         padding: 0;
    
         box-sizing: border-box;
    
         font-family: 'Arial', sans-serif;
    
     }
    
  
    
     body {
    
         background-color: #f0f0f0;
    
         display: flex;
    
         justify-content: center;
    
         align-items: center;
    
         height: 100vh;
    
         color: #333;
    
     }
    
  
    
     .container {
    
         width: 100%;
    
         max-width: 800px;
    
         display: flex;
    
         flex-direction: column;
    
         align-items: center;
    
     }
    
  
    
     /* 主菜单样式 */
    
     .main-menu {
    
         text-align: center;
    
         background-color: white;
    
         padding: 40px;
    
         border-radius: 10px;
    
         box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    
         animation: fadeIn 0.5s ease-out;
    
     }
    
  
    
     .main-menu h1 {
    
         font-size: 3rem;
    
         margin-bottom: 30px;
    
         color: #2c3e50;
    
     }
    
  
    
     .menu-btn {
    
         display: block;
    
         width: 200px;
    
         padding: 12px 0;
    
         margin: 15px auto;
    
         background-color: #3498db;
    
         color: white;
    
         border: none;
    
         border-radius: 5px;
    
         font-size: 1.1rem;
    
         cursor: pointer;
    
         transition: all 0.3s;
    
     }
    
  
    
     .menu-btn:hover {
    
         background-color: #2980b9;
    
         transform: translateY(-2px);
    
     }
    
  
    
     /* 关卡选择界面 */
    
     .level-select {
    
         display: none;
    
         text-align: center;
    
         background-color: white;
    
         padding: 30px;
    
         border-radius: 10px;
    
         box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    
         animation: fadeIn 0.5s ease-out;
    
     }
    
  
    
     .level-select h2 {
    
         font-size: 2rem;
    
         margin-bottom: 25px;
    
         color: #2c3e50;
    
     }
    
  
    
     .level-grid {
    
         display: grid;
    
         grid-template-columns: repeat(3, 1fr);
    
         gap: 15px;
    
         margin-bottom: 20px;
    
     }
    
  
    
     .level-btn {
    
         padding: 15px;
    
         background-color: #2ecc71;
    
         color: white;
    
         border: none;
    
         border-radius: 5px;
    
         font-size: 1.1rem;
    
         cursor: pointer;
    
         transition: all 0.3s;
    
     }
    
  
    
     .level-btn:hover {
    
         background-color: #27ae60;
    
         transform: translateY(-2px);
    
     }
    
  
    
     .level-btn.locked {
    
         background-color: #95a5a6;
    
         cursor: not-allowed;
    
     }
    
  
    
     .back-btn {
    
         padding: 10px 20px;
    
         background-color: #e74c3c;
    
         color: white;
    
         border: none;
    
         border-radius: 5px;
    
         font-size: 1rem;
    
         cursor: pointer;
    
         transition: all 0.3s;
    
     }
    
  
    
     .back-btn:hover {
    
         background-color: #c0392b;
    
     }
    
  
    
     /* 游戏界面 */
    
     .game-container {
    
         display: none;
    
         flex-direction: column;
    
         align-items: center;
    
         background-color: white;
    
         padding: 20px;
    
         border-radius: 10px;
    
         box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    
         animation: fadeIn 0.5s ease-out;
    
     }
    
  
    
     .game-header {
    
         width: 100%;
    
         display: flex;
    
         justify-content: space-between;
    
         margin-bottom: 20px;
    
     }
    
  
    
     .game-info {
    
         display: flex;
    
         flex-direction: column;
    
         align-items: center;
    
     }
    
  
    
     .game-info span {
    
         font-size: 1.5rem;
    
         font-weight: bold;
    
         color: #2c3e50;
    
     }
    
  
    
     #mine-count {
    
         color: #e74c3c;
    
     }
    
  
    
     #timer {
    
         color: #3498db;
    
     }
    
  
    
     #game-board {
    
         display: grid;
    
         gap: 2px;
    
         background-color: #bdc3c7;
    
         border: 3px solid #7f8c8d;
    
         border-radius: 5px;
    
     }
    
  
    
     .cell {
    
         width: 30px;
    
         height: 30px;
    
         display: flex;
    
         justify-content: center;
    
         align-items: center;
    
         background-color: #ecf0f1;
    
         font-weight: bold;
    
         cursor: pointer;
    
         user-select: none;
    
     }
    
  
    
     .cell.revealed {
    
         background-color: #bdc3c7;
    
     }
    
  
    
     .cell.flagged {
    
         background-color: #f1c40f;
    
     }
    
  
    
     .cell.mine {
    
         background-color: #e74c3c;
    
     }
    
  
    
     .cell.mine::after {
    
         content: "💣";
    
     }
    
  
    
     .cell.flagged::after {
    
         content: "🚩";
    
     }
    
  
    
     .number-1 {
    
         color: #3498db;
    
     }
    
  
    
     .number-2 {
    
         color: #2ecc71;
    
     }
    
  
    
     .number-3 {
    
         color: #e74c3c;
    
     }
    
  
    
     .number-4 {
    
         color: #9b59b6;
    
     }
    
  
    
     .number-5 {
    
         color: #f39c12;
    
     }
    
  
    
     .number-6 {
    
         color: #1abc9c;
    
     }
    
  
    
     .number-7 {
    
         color: #34495e;
    
     }
    
  
    
     .number-8 {
    
         color: #7f8c8d;
    
     }
    
  
    
     .game-controls {
    
         margin-top: 20px;
    
         display: flex;
    
         gap: 10px;
    
     }
    
  
    
     .control-btn {
    
         padding: 8px 15px;
    
         background-color: #3498db;
    
         color: white;
    
         border: none;
    
         border-radius: 5px;
    
         cursor: pointer;
    
         transition: all 0.3s;
    
     }
    
  
    
     .control-btn:hover {
    
         background-color: #2980b9;
    
     }
    
  
    
     /* 游戏结束界面 */
    
     .game-over {
    
         display: none;
    
         position: fixed;
    
         top: 0;
    
         left: 0;
    
         width: 100%;
    
         height: 100%;
    
         background-color: rgba(0, 0, 0, 0.7);
    
         justify-content: center;
    
         align-items: center;
    
         z-index: 100;
    
         animation: fadeIn 0.3s ease-out;
    
     }
    
  
    
     .game-over-content {
    
         background-color: white;
    
         padding: 30px;
    
         border-radius: 10px;
    
         text-align: center;
    
         max-width: 400px;
    
         width: 90%;
    
     }
    
  
    
     .game-over h2 {
    
         font-size: 2rem;
    
         margin-bottom: 20px;
    
         color: #2c3e50;
    
     }
    
  
    
     .game-over p {
    
         font-size: 1.2rem;
    
         margin-bottom: 25px;
    
     }
    
  
    
     .result-buttons {
    
         display: flex;
    
         justify-content: center;
    
         gap: 10px;
    
     }
    
  
    
     /* 动画效果 */
    
     @keyframes fadeIn {
    
         from {
    
             opacity: 0;
    
             transform: translateY(-10px);
    
         }
    
  
    
         to {
    
             opacity: 1;
    
             transform: translateY(0);
    
         }
    
     }
    
  
    
     /* 响应式设计 */
    
     @media (max-width: 600px) {
    
         .main-menu h1 {
    
             font-size: 2rem;
    
         }
    
  
    
         .level-grid {
    
             grid-template-columns: repeat(2, 1fr);
    
         }
    
  
    
         .cell {
    
             width: 25px;
    
             height: 25px;
    
             font-size: 0.8rem;
    
         }
    
     }
    
     </style>
    
 </head>
    
  
    
 <body>
    
     <div class="container">
    
     <!-- 主菜单 -->
    
     <div class="main-menu" id="mainMenu">
    
         <h1>扫雷小游戏</h1>
    
         <button class="menu-btn" id="startBtn">开始游戏</button>
    
         <button class="menu-btn" id="howToPlayBtn">游戏说明</button>
    
     </div>
    
  
    
     <!-- 关卡选择 -->
    
     <div class="level-select" id="levelSelect">
    
         <h2>选择难度</h2>
    
         <div class="level-grid">
    
             <button class="level-btn" id="level1">初级<br>9×9 格<br>10 个雷</button>
    
             <button class="level-btn" id="level2">中级<br>16×16 格<br>40 个雷</button>
    
             <button class="level-btn" id="level3">高级<br>30×16 格<br>99 个雷</button>
    
             <button class="level-btn" id="level4">专家<br>30×20 格<br>150 个雷</button>
    
             <button class="level-btn" id="level5">大师<br>40×20 格<br>200 个雷</button>
    
             <button class="back-btn" id="backToMenuFromLevels">返回菜单</button>
    
         </div>
    
     </div>
    
  
    
     <!-- 游戏界面 -->
    
     <div class="game-container" id="gameContainer">
    
         <div class="game-header">
    
             <div class="game-info">
    
                 <span>剩余雷数</span>
    
                 <span id="mine-count">0</span>
    
             </div>
    
             <div class="game-info">
    
                 <span>时间</span>
    
                 <span id="timer">0</span>
    
             </div>
    
         </div>
    
  
    
         <div id="game-board"></div>
    
  
    
         <div class="game-controls">
    
             <button class="control-btn" id="restartBtn">重新开始</button>
    
             <button class="control-btn" id="backToMenuBtn">返回菜单</button>
    
         </div>
    
     </div>
    
  
    
     <!-- 游戏说明 -->
    
     <div class="game-over" id="howToPlayMenu">
    
         <div class="game-over-content">
    
             <h2>游戏说明</h2>
    
             <p>左键点击格子揭开它,右键点击格子标记地雷。</p>
    
             <p>数字表示周围8个格子中有多少地雷。</p>
    
             <p>标记出所有地雷或揭开所有非地雷格子即可获胜。</p>
    
             <div class="result-buttons">
    
                 <button class="control-btn" id="backFromHowToPlayBtn">知道了</button>
    
             </div>
    
         </div>
    
     </div>
    
  
    
     <!-- 游戏结束界面 -->
    
     <div class="game-over" id="gameOver">
    
         <div class="game-over-content">
    
             <h2 id="result-title">游戏结束</h2>
    
             <p id="result-message"></p>
    
             <div class="result-buttons">
    
                 <button class="control-btn" id="playAgainBtn">再玩一次</button>
    
                 <button class="control-btn" id="backToMenuFromGameOverBtn">返回菜单</button>
    
             </div>
    
         </div>
    
     </div>
    
     </div>
    
  
    
     <script>
    
     // 游戏状态
    
     const gameState = {
    
         level: 1,
    
         board: [],
    
         mineCount: 0,
    
         revealedCount: 0,
    
         flaggedCount: 0,
    
         gameOver: false,
    
         gameWon: false,
    
         firstClick: true,
    
         startTime: 0,
    
         timerInterval: null,
    
         elapsedTime: 0,
    
         levels: [
    
             { rows: 9, cols: 9, mines: 10 },   // 初级
    
             { rows: 16, cols: 16, mines: 40 },  // 中级
    
             { rows: 16, cols: 30, mines: 99 },  // 高级
    
             { rows: 20, cols: 30, mines: 150 }, // 专家
    
             { rows: 20, cols: 40, mines: 200 }  // 大师
    
         ]
    
     };
    
  
    
     // DOM元素
    
     const elements = {
    
         mainMenu: document.getElementById('mainMenu'),
    
         levelSelect: document.getElementById('levelSelect'),
    
         gameContainer: document.getElementById('gameContainer'),
    
         howToPlayMenu: document.getElementById('howToPlayMenu'),
    
         gameOver: document.getElementById('gameOver'),
    
         gameBoard: document.getElementById('game-board'),
    
         mineCount: document.getElementById('mine-count'),
    
         timer: document.getElementById('timer'),
    
         resultTitle: document.getElementById('result-title'),
    
         resultMessage: document.getElementById('result-message')
    
     };
    
  
    
     // 按钮
    
     const buttons = {
    
         startBtn: document.getElementById('startBtn'),
    
         howToPlayBtn: document.getElementById('howToPlayBtn'),
    
         backFromHowToPlayBtn: document.getElementById('backFromHowToPlayBtn'),
    
         backToMenuFromLevels: document.getElementById('backToMenuFromLevels'),
    
         restartBtn: document.getElementById('restartBtn'),
    
         backToMenuBtn: document.getElementById('backToMenuBtn'),
    
         playAgainBtn: document.getElementById('playAgainBtn'),
    
         backToMenuFromGameOverBtn: document.getElementById('backToMenuFromGameOverBtn')
    
     };
    
  
    
     // 关卡按钮
    
     const levelButtons = [
    
         document.getElementById('level1'),
    
         document.getElementById('level2'),
    
         document.getElementById('level3'),
    
         document.getElementById('level4'),
    
         document.getElementById('level5')
    
     ];
    
  
    
     // 初始化游戏
    
     function initGame(level) {
    
         gameState.level = level;
    
         gameState.mineCount = gameState.levels[level - 1].mines;
    
         gameState.revealedCount = 0;
    
         gameState.flaggedCount = 0;
    
         gameState.gameOver = false;
    
         gameState.gameWon = false;
    
         gameState.firstClick = true;
    
         gameState.elapsedTime = 0;
    
  
    
         // 清除之前的计时器
    
         if (gameState.timerInterval) {
    
             clearInterval(gameState.timerInterval);
    
         }
    
  
    
         // 更新UI
    
         elements.mineCount.textContent = gameState.mineCount;
    
         elements.timer.textContent = '0';
    
  
    
         // 创建游戏板
    
         createBoard();
    
     }
    
  
    
     // 创建游戏板
    
     function createBoard() {
    
         const level = gameState.levels[gameState.level - 1];
    
         gameState.board = [];
    
  
    
         // 清空游戏板
    
         elements.gameBoard.innerHTML = '';
    
         elements.gameBoard.style.gridTemplateColumns = `repeat(${level.cols}, 1fr)`;
    
  
    
         // 初始化二维数组
    
         for (let row = 0; row < level.rows; row++) {
    
             gameState.board[row] = [];
    
             for (let col = 0; col < level.cols; col++) {
    
                 gameState.board[row][col] = {
    
                     isMine: false,
    
                     revealed: false,
    
                     flagged: false,
    
                     neighborMines: 0
    
                 };
    
  
    
                 // 创建单元格元素
    
                 const cell = document.createElement('div');
    
                 cell.className = 'cell';
    
                 cell.dataset.row = row;
    
                 cell.dataset.col = col;
    
  
    
                 // 添加事件监听
    
                 cell.addEventListener('click', () => handleCellClick(row, col));
    
                 cell.addEventListener('contextmenu', (e) => {
    
                     e.preventDefault();
    
                     handleRightClick(row, col);
    
                 });
    
  
    
                 elements.gameBoard.appendChild(cell);
    
             }
    
         }
    
     }
    
  
    
     // 放置地雷 (第一次点击后)
    
     function placeMines(firstRow, firstCol) {
    
         const level = gameState.levels[gameState.level - 1];
    
         let minesPlaced = 0;
    
  
    
         // 确保第一次点击的格子及其周围没有地雷
    
         const safeZone = [];
    
         for (let r = Math.max(0, firstRow - 1); r <= Math.min(level.rows - 1, firstRow + 1); r++) {
    
             for (let c = Math.max(0, firstCol - 1); c <= Math.min(level.cols - 1, firstCol + 1); c++) {
    
                 safeZone.push(`${r},${c}`);
    
             }
    
         }
    
  
    
         // 随机放置地雷
    
         while (minesPlaced < level.mines) {
    
             const row = Math.floor(Math.random() * level.rows);
    
             const col = Math.floor(Math.random() * level.cols);
    
  
    
             // 检查是否在安全区域或已经是地雷
    
             if (!safeZone.includes(`${row},${col}`) && !gameState.board[row][col].isMine) {
    
                 gameState.board[row][col].isMine = true;
    
                 minesPlaced++;
    
             }
    
         }
    
  
    
         // 计算每个格子周围的地雷数
    
         calculateNeighborMines();
    
  
    
         // 开始计时
    
         startTimer();
    
     }
    
  
    
     // 计算每个格子周围的地雷数
    
     function calculateNeighborMines() {
    
         const level = gameState.levels[gameState.level - 1];
    
  
    
         for (let row = 0; row < level.rows; row++) {
    
             for (let col = 0; col < level.cols; col++) {
    
                 if (!gameState.board[row][col].isMine) {
    
                     let count = 0;
    
  
    
                     // 检查周围8个格子
    
                     for (let r = Math.max(0, row - 1); r <= Math.min(level.rows - 1, row + 1); r++) {
    
                         for (let c = Math.max(0, col - 1); c <= Math.min(level.cols - 1, col + 1); c++) {
    
                             if ((r !== row || c !== col) && gameState.board[r][c].isMine) {
    
                                 count++;
    
                             }
    
                         }
    
                     }
    
  
    
                     gameState.board[row][col].neighborMines = count;
    
                 }
    
             }
    
         }
    
     }
    
  
    
     // 处理左键点击
    
     function handleCellClick(row, col) {
    
         if (gameState.gameOver || gameState.board[row][col].revealed || gameState.board[row][col].flagged) {
    
             return;
    
         }
    
         // 第一次点击时放置地雷
    
         if (gameState.firstClick) {
    
             gameState.firstClick = false;
    
             placeMines(row, col);
    
         }
    
  
    
         // 如果点到地雷,游戏结束
    
         if (gameState.board[row][col].isMine) {
    
             revealAllMines();
    
             gameOver(false);
    
             return;
    
         }
    
  
    
         // 揭开格子
    
         revealCell(row, col);
    
  
    
         // 检查是否获胜
    
         checkWin();
    
     }
    
  
    
     // 处理右键点击 (标记地雷)
    
     function handleRightClick(row, col) {
    
         if (gameState.gameOver || gameState.board[row][col].revealed) {
    
             return;
    
         }
    
  
    
         const cell = gameState.board[row][col];
    
         const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
    
  
    
         if (cell.flagged) {
    
             // 取消标记
    
             cell.flagged = false;
    
             gameState.flaggedCount--;
    
             cellElement.classList.remove('flagged');
    
             cellElement.innerHTML = '';
    
         } else {
    
             // 标记地雷
    
             cell.flagged = true;
    
             gameState.flaggedCount++;
    
             cellElement.classList.add('flagged');
    
         }
    
  
    
         // 更新剩余雷数显示
    
         elements.mineCount.textContent = gameState.mineCount - gameState.flaggedCount;
    
     }
    
  
    
     // 揭开格子
    
     function revealCell(row, col) {
    
         const level = gameState.levels[gameState.level - 1];
    
  
    
         // 检查边界和状态
    
         if (row < 0 || row >= level.rows || col < 0 || col >= level.cols ||
    
             gameState.board[row][col].revealed || gameState.board[row][col].flagged) {
    
             return;
    
         }
    
  
    
         // 标记为已揭开
    
         gameState.board[row][col].revealed = true;
    
         gameState.revealedCount++;
    
  
    
         const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
    
         cellElement.classList.add('revealed');
    
  
    
         // 如果不是空白格子,显示数字
    
         if (gameState.board[row][col].neighborMines > 0) {
    
             cellElement.textContent = gameState.board[row][col].neighborMines;
    
             cellElement.classList.add(`number-${gameState.board[row][col].neighborMines}`);
    
             return;
    
         }
    
  
    
         // 如果是空白格子,递归揭开周围的格子
    
         for (let r = Math.max(0, row - 1); r <= Math.min(level.rows - 1, row + 1); r++) {
    
             for (let c = Math.max(0, col - 1); c <= Math.min(level.cols - 1, col + 1); c++) {
    
                 if (r !== row || c !== col) {
    
                     revealCell(r, c);
    
                 }
    
             }
    
         }
    
     }
    
  
    
     // 显示所有地雷 (游戏结束时)
    
     function revealAllMines() {
    
         const level = gameState.levels[gameState.level - 1];
    
  
    
         for (let row = 0; row < level.rows; row++) {
    
             for (let col = 0; col < level.cols; col++) {
    
                 if (gameState.board[row][col].isMine) {
    
                     const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
    
                     cellElement.classList.add('mine');
    
                     cellElement.classList.add('revealed');
    
                 }
    
             }
    
         }
    
     }
    
  
    
     // 开始计时器
    
     function startTimer() {
    
         gameState.startTime = Date.now();
    
         gameState.timerInterval = setInterval(() => {
    
             gameState.elapsedTime = Math.floor((Date.now() - gameState.startTime) / 1000);
    
             elements.timer.textContent = gameState.elapsedTime;
    
         }, 1000);
    
     }
    
  
    
     // 检查是否获胜
    
     function checkWin() {
    
         const level = gameState.levels[gameState.level - 1];
    
         const totalSafeCells = level.rows * level.cols - level.mines;
    
  
    
         if (gameState.revealedCount === totalSafeCells) {
    
             gameOver(true);
    
         }
    
     }
    
  
    
     // 游戏结束
    
     function gameOver(isWin) {
    
         gameState.gameOver = true;
    
         gameState.gameWon = isWin;
    
  
    
         // 停止计时器
    
         clearInterval(gameState.timerInterval);
    
  
    
         // 显示游戏结束界面
    
         if (isWin) {
    
             elements.resultTitle.textContent = '恭喜你赢了!';
    
             elements.resultMessage.textContent = `你用了 ${gameState.elapsedTime} 秒完成了 ${getLevelName(gameState.level)} 难度!`;
    
         } else {
    
             elements.resultTitle.textContent = '游戏结束';
    
             elements.resultMessage.textContent = '你不小心踩到了地雷!';
    
             revealAllMines();
    
         }
    
  
    
         elements.gameOver.style.display = 'flex';
    
     }
    
  
    
     // 获取难度名称
    
     function getLevelName(level) {
    
         const names = ['初级', '中级', '高级', '专家', '大师'];
    
         return names[level - 1];
    
     }
    
  
    
     // 事件监听
    
     function setupEventListeners() {
    
         // 主菜单按钮
    
         buttons.startBtn.addEventListener('click', () => {
    
             elements.mainMenu.style.display = 'none';
    
             elements.levelSelect.style.display = 'block';
    
         });
    
  
    
         buttons.howToPlayBtn.addEventListener('click', () => {
    
             elements.mainMenu.style.display = 'none';
    
             elements.howToPlayMenu.style.display = 'flex';
    
         });
    
  
    
         buttons.backFromHowToPlayBtn.addEventListener('click', () => {
    
             elements.howToPlayMenu.style.display = 'none';
    
             elements.mainMenu.style.display = 'block';
    
         });
    
  
    
         // 关卡选择按钮
    
         buttons.backToMenuFromLevels.addEventListener('click', () => {
    
             elements.levelSelect.style.display = 'none';
    
             elements.mainMenu.style.display = 'block';
    
         });
    
  
    
         for (let i = 0; i < levelButtons.length; i++) {
    
             levelButtons[i].addEventListener('click', () => {
    
                 elements.levelSelect.style.display = 'none';
    
                 elements.gameContainer.style.display = 'flex';
    
                 initGame(i + 1);
    
             });
    
         }
    
  
    
         // 游戏控制按钮
    
         buttons.restartBtn.addEventListener('click', () => {
    
             initGame(gameState.level);
    
             elements.gameOver.style.display = 'none';
    
         });
    
  
    
         buttons.backToMenuBtn.addEventListener('click', () => {
    
             if (confirm('确定要返回主菜单吗?当前游戏进度将丢失。')) {
    
                 elements.gameContainer.style.display = 'none';
    
                 elements.mainMenu.style.display = 'block';
    
                 clearInterval(gameState.timerInterval);
    
             }
    
         });
    
  
    
         // 游戏结束按钮
    
         buttons.playAgainBtn.addEventListener('click', () => {
    
             initGame(gameState.level);
    
             elements.gameOver.style.display = 'none';
    
         });
    
  
    
         buttons.backToMenuFromGameOverBtn.addEventListener('click', () => {
    
             elements.gameOver.style.display = 'none';
    
             elements.gameContainer.style.display = 'none';  // 修复:隐藏游戏容器
    
             elements.mainMenu.style.display = 'block';
    
             clearInterval(gameState.timerInterval);
    
         });
    
     }
    
  
    
     // 初始化
    
     function init() {
    
         setupEventListeners();
    
         elements.mainMenu.style.display = 'block';
    
     }
    
  
    
     // 启动游戏
    
     init();
    
     </script>
    
 </body>
    
  
    
 </html>

喜欢记得点个关注,会持续更新好玩小游戏源码

全部评论 (0)

还没有任何评论哟~