导读:本期聚焦于小伙伴创作的《使用HTML与JavaScript实现扫雷游戏:从矩阵生成到递归展开的完整教程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《使用HTML与JavaScript实现扫雷游戏:从矩阵生成到递归展开的完整教程》有用,将其分享出去将是对创作者最好的鼓励。

使用HTML实现扫雷游戏及矩阵点击逻辑详解

一、扫雷游戏的基本结构与核心逻辑

扫雷是经典的益智游戏,核心玩法围绕二维矩阵展开,玩家通过点击格子排查地雷,标记可疑位置,最终揭开所有非雷格子即可获胜。使用HTML、CSS和JavaScript实现扫雷时,核心依赖以下部分:

  • 二维矩阵:存储每个格子的状态(是否地雷、周围地雷数、是否揭开、是否标记)

  • DOM渲染:根据矩阵状态生成对应的游戏界面

  • 点击逻辑:区分左键点击(揭开格子)和右键点击(标记/取消标记格子)

  • 递归展开:点击空白格子时,自动展开周围无雷的相邻格子

二、基础HTML结构搭建

首先搭建游戏的基础页面结构,包含游戏容器、难度选择、状态显示区域和雷区矩阵容器:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTML扫雷游戏</title>
    <style>
        .game-container {
            width: fit-content;
            margin: 20px auto;
            text-align: center;
        }
        .control-panel {
            margin-bottom: 15px;
        }
        .mine-grid {
            display: grid;
            gap: 1px;
            background-color: #ccc;
            border: 2px solid #999;
        }
        .cell {
            width: 30px;
            height: 30px;
            background-color: #ddd;
            border: 1px solid #999;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            user-select: none;
            font-weight: bold;
        }
        .cell.revealed {
            background-color: #eee;
            cursor: default;
        }
        .cell.mine {
            background-color: #f44;
        }
        .cell.flagged::after {
            content: "?";
        }
        .status {
            margin-top: 10px;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="control-panel">
            <button data-difficulty="easy">简单(9×9,10雷)</button>
            <button data-difficulty="medium">中等(16×16,40雷)</button>
            <button data-difficulty="hard">困难(16×30,99雷)</button>
        </div>
        <div id="mineGrid" class="mine-grid"></div>
        <div class="status" id="gameStatus">点击格子开始游戏</div>
    </div>
    <script src="game.js"></script>
</body>
</html>

三、核心JavaScript逻辑实现

1. 游戏初始化与矩阵生成

首先需要定义不同难度的配置,初始化时生成对应的二维矩阵,随机布置地雷,再计算每个格子周围的地雷数量:

// 游戏配置
const DIFFICULTY_CONFIG = {
    easy: { rows: 9, cols: 9, mines: 10 },
    medium: { rows: 16, cols: 16, mines: 40 },
    hard: { rows: 16, cols: 30, mines: 99 }
};

// 游戏状态变量
let gameMatrix = []; // 二维矩阵存储格子状态
let gameConfig = DIFFICULTY_CONFIG.easy; // 当前难度配置
let isGameOver = false;
let revealedCount = 0; // 已揭开的格子数
let totalSafeCells = 0; // 总安全格子数

// 初始化游戏
function initGame(difficulty) {
    gameConfig = DIFFICULTY_CONFIG[difficulty] || DIFFICULTY_CONFIG.easy;
    isGameOver = false;
    revealedCount = 0;
    totalSafeCells = gameConfig.rows * gameConfig.cols - gameConfig.mines;
    document.getElementById('gameStatus').textContent = '点击格子开始游戏';

    // 初始化矩阵:每个格子默认状态
    gameMatrix = [];
    for (let i = 0; i < gameConfig.rows; i++) {
        gameMatrix[i] = [];
        for (let j = 0; j < gameConfig.cols; j++) {
            gameMatrix[i][j] = {
                isM false,
                isRevealed: false,
                isFlagged: false,
                neighborMines: 0 // 周围地雷数量
            };
        }
    }

    // 随机布置地雷
    let placedMines = 0;
    while (placedMines < gameConfig.mines) {
        const row = Math.floor(Math.random() * gameConfig.rows);
        const col = Math.floor(Math.random() * gameConfig.cols);
        if (!gameMatrix[row][col].isMine) {
            gameMatrix[row][col].isMine = true;
            placedMines++;
        }
    }

    // 计算每个格子周围的地雷数
    for (let i = 0; i < gameConfig.rows; i++) {
        for (let j = 0; j < gameConfig.cols; j++) {
            if (!gameMatrix[i][j].isMine) {
                gameMatrix[i][j].neighborMines = countNeighborMines(i, j);
            }
        }
    }

    // 渲染雷区
    renderGrid();
}

// 统计格子周围的地雷数量
function countNeighborMines(row, col) {
    let count = 0;
    // 遍历周围8个方向
    for (let r = row - 1; r <= row + 1; r++) {
        for (let c = col - 1; c <= col + 1; c++) {
            // 排除自身,且坐标在矩阵范围内
            if (r === row && c === col) continue;
            if (r >=  r < gameConfig.rows && c >= 0 && c < gameConfig.cols) {
                if (gameMatrix[r][c].isMine) count++;
            }
        }
    }
    return count;
}

2. 雷区渲染逻辑

根据矩阵状态生成对应的DOM元素,为每个格子绑定点击事件,同时设置网格的列数适配矩阵宽度:

// 渲染雷区网格
function renderGrid() {
    const gridContainer = document.getElementById('mineGrid');
   .innerHTML = '';
    // 设置网格列数
    gridContainer.style.gridTemplateColumns = `repeat(${gameConfig.cols}, 30px)`;

    for (let i = 0; i < gameConfig.rows; i++) {
        for (let j = 0; j < gameConfig.cols; j++) {
            const cell = document.createElement('div');
            cell.className = 'cell';
            cell.dataset.row = i;
            cell.dataset.col = j;

            // 左键点击:揭开格子
            cell.addEventListener('click', () => handleCellClick(i, j));
            // 右键点击:标记/取消标记格子
            cell.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                handleCellRightClick(i, j);
            });

            // 根据状态更新格子显示
            updateCellDisplay(cell, i, j);
            gridContainer.appendChild(cell);
        }
    }
}

// 更新单个格子的显示状态
function updateCellDisplay(cell, row, col) {
    const cellData = gameMatrix[row][col];
    // 重置类名
    cell.className = 'cell';
    cell.textContent = '';

    if (cellData.isRevealed) {
        cell.classList.add('revealed');
        if (cellData.isMine) {
            cell.classList.add('mine');
            cell.textContent = '?';
        } else if (cellData.neighborMines > 0) {
            // 不同数字显示不同颜色
            cell.textContent = cellData.neighborMines;
            const colorMap = ['', '', 'blue', 'green', 'red', 'darkblue', 'darkred', 'cyan', 'black', 'gray'];
            cell.style.color = colorMap[cellData.neighborMines];
        }
    } else if (cellData.isFlagged) {
        cell.classList.add('flagged');
    }
}

3. 矩阵点击核心逻辑实现

点击逻辑分为左键揭开和右键标记两部分,其中左键点击空白格子时需要实现递归展开相邻的无雷区域:

// 左键点击格子处理
function handleCellClick(row, col) {
    if (isGameOver) return;
    const cellData = gameMatrix[row][col];
    if (cellData.isRevealed || cellData.isFlagged) return;

    // 揭开格子
    revealCell(row, col);
}

// 递归揭开格子(核心展开逻辑)
function revealCell(row, col) {
    // 边界检查、已揭开或标记的检查
    if (row < 0 || row >= gameConfig.rows || col < 0 || col >= gameConfig.cols) return;
    const cellData = gameMatrix[row][col];
    if (cellData.isRevealed || cellData.isFlagged) return;

    // 标记为已揭开
    cellData.isRevealed = true;
    revealedCount++;

    // 如果是地雷,游戏结束
    if (cellData.isMine) {
        isGameOver = true;
        document.getElementById('gameStatus').textContent = '游戏结束!你踩到地雷了';
        // 揭开所有地雷
        for (let i = 0; i < gameConfig.rows; i++) {
            for (let j = 0; j < gameConfig.cols; j++) {
                if (gameMatrix[i][j].isMine) {
                    gameMatrix[i][j].isRevealed = true;
                }
            }
        }
        renderGrid();
        return;
    }

    // 检查是否获胜:已揭开的安全格子数等于总安全格子数
    if (revealedCount === totalSafeCells) {
        isGameOver = true;
        document.getElementById('gameStatus').textContent = '恭喜你!扫雷成功';
        renderGrid();
        return;
    }

    // 如果当前格子周围地雷数为0,递归展开周围格子
    if (cellData.neighborMines === 0) {
        // 遍历周围8个方向
        for (let r = row - 1; r <= row + 1; r++) {
            for (let c = col - 1; c <= col + 1; c++) {
                if (r === row && c === col) continue;
                revealCell(r, c);
            }
        }
    }

    // 更新当前格子的显示
    const gridContainer = document.getElementById('mineGrid');
    const index = row * gameConfig.cols + col;
    const cellElement = gridContainer.children[index];
    updateCellDisplay(cellElement, row, col);
}

// 右键点击格子处理(标记/取消标记)
function handleCellRightClick(row, col) {
    if (isGameOver) return;
    const cellData = gameMatrix[row][col];
    if (cellData.isRevealed) return;

    // 切换标记状态
    cellData.isFlagged = !cellData.isFlagged;

    // 更新显示
    const gridContainer = document.getElementById('mineGrid');
    const index = row * gameConfig.cols + col;
    const cellElement = gridContainer.children[index];
    updateCellDisplay(cellElement, row, col);
}

4. 难度切换绑定

最后为难度选择按钮绑定点击事件,实现切换难度重新初始化游戏:

// 绑定难度按钮事件
document.querySelectorAll('.control-panel button').forEach(btn => {
    btn.addEventListener('click', ()        const difficulty = btn.dataset.difficulty;
        initGame(difficulty);
    });
});

// 页面加载时初始化简单难度游戏
window.addEventListener('DOMContentLoaded', () => {
    initGame('easy');
});

四、逻辑关键点说明

矩阵点击逻辑的核心在于revealCell函数的递归设计:当点击的格子周围地雷数为0时,会自动向周围8个方向递归调用揭开函数,直到遇到周围有地雷的格子为止,这正是扫雷中点击空白区域自动展开的效果实现原理。

同时需要注意边界判断,避免递归时出现数组越界的问题,另外游戏状态的判断要放在揭开逻辑的最前端,防止游戏结束后还能继续操作。

如果需要优化性能,可以将DOM操作尽量合并,避免频繁更新单个格子导致的重绘重排,也可以在递归时加入已访问标记,避免重复处理同一个格子。

HTML扫雷游戏JavaScript矩阵逻辑递归展开算法扫雷游戏实现游戏开发教程

免责声明:已尽一切努力确保本网站所含信息的准确性。网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。