扫雷是Windows系统自带的经典游戏,陪伴了许多人的童年。
本文将详细解析一个用HTML、CSS和JavaScript实现的扫雷游戏代码,带你了解其背后的实现原理。

游戏概述
这个扫雷游戏实现包含以下核心功能:
10×10的游戏棋盘
15个随机分布的地雷
左键点击揭开格子
右键点击标记地雷
自动展开空白区域
胜利/失败判定
HTML结构
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>扫雷游戏</title>
<!-- CSS样式 -->
</head>
<body>
<h1>扫雷游戏</h1>
<div id="game-board"></div>
<!-- JavaScript代码 -->
</body>
</html>
结构非常简单,只有一个标题和游戏板容器。所有游戏逻辑都通过JavaScript动态生成。
CSS样式设计
CSS部分定义了游戏的外观:
html
.cell {
width: 30px;
height: 30px;
border: 1px solid #ccc;
/* 其他样式 */
}
.cell.revealed {
background-color: #fff;
}
.cell.mine {
background-color: #ff4444;
}
.cell.flagged {
background-color: #ffcc00;
}
-
基本格子样式:灰色背景,浅灰色边框
-
已揭开格子:白色背景
-
地雷格子:红色背景
-
标记格子:黄色背景
JavaScript游戏逻辑
1. 游戏初始化
javascript
html
const BOARD_SIZE = 10;
const MINE_COUNT = 15;
let board = [];
let gameOver = false;
定义了游戏常量:棋盘大小和地雷数量,以及游戏状态变量。
2. 创建游戏板
createBoard()
函数负责初始化游戏:
-
设置CSS Grid布局
-
创建格子DOM元素并添加到游戏板
-
初始化游戏数据结构
-
放置地雷并计算数字
javascript
html
function createBoard() {
// 设置Grid布局
gameBoard.style.gridTemplateColumns = `repeat(${BOARD_SIZE}, 30px)`;
// 创建格子
for (let row = 0; row < BOARD_SIZE; row++) {
board[row] = [];
for (let col = 0; col < BOARD_SIZE; col++) {
// 创建DOM元素
const cell = document.createElement('div');
// 添加事件监听
cell.addEventListener('click', handleCellClick);
cell.addEventListener('contextmenu', handleRightClick);
// 初始化游戏数据
board[row][col] = {
mine: false,
revealed: false,
flagged: false,
count: 0
};
}
}
placeMines();
calculateNumbers();
}
3. 随机放置地雷
placeMines()
函数随机放置指定数量的地雷:
javascript
html
function placeMines() {
let minesPlaced = 0;
while (minesPlaced < MINE_COUNT) {
const row = Math.floor(Math.random() * BOARD_SIZE);
const col = Math.floor(Math.random() * BOARD_SIZE);
if (!board[row][col].mine) {
board[row][col].mine = true;
minesPlaced++;
}
}
}
使用while循环确保放置足够数量的地雷,避免重复放置。
4. 计算周围地雷数
calculateNumbers()
为每个非地雷格子计算周围地雷数量:
javascript
html
function calculateNumbers() {
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
if (board[row][col].mine) continue;
let count = 0;
// 检查周围8个格子
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
const newRow = row + i;
const newCol = col + j;
// 边界检查
if (newRow >= 0 && newRow < BOARD_SIZE &&
newCol >= 0 && newCol < BOARD_SIZE &&
board[newRow][newCol].mine) {
count++;
}
}
}
board[row][col].count = count;
}
}
}
5. 处理玩家点击
左键点击处理函数handleCellClick
:
javascript
html
function handleCellClick(event) {
if (gameOver) return;
const cell = event.target;
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
revealCell(row, col);
}
右键点击处理函数handleRightClick
用于标记地雷:
javascript
html
function handleRightClick(event) {
event.preventDefault(); // 阻止上下文菜单
if (gameOver) return;
const cell = event.target;
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
if (!board[row][col].revealed) {
board[row][col].flagged = !board[row][col].flagged;
cell.classList.toggle('flagged', board[row][col].flagged);
}
}
6. 揭开格子逻辑
revealCell()
是游戏核心函数,处理格子揭开逻辑:
javascript
html
function revealCell(row, col) {
// 已揭开或已标记则返回
if (board[row][col].revealed || board[row][col].flagged) return;
const cell = document.querySelector(`.cell[data-row='${row}'][data-col='${col}']`);
board[row][col].revealed = true;
cell.classList.add('revealed');
// 踩到地雷
if (board[row][col].mine) {
cell.classList.add('mine');
gameOver = true;
alert('游戏结束!你踩到了地雷!');
return;
}
// 显示周围地雷数
cell.textContent = board[row][col].count || '';
// 如果是空白格子,自动展开周围
if (board[row][col].count === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
const newRow = row + i;
const newCol = col + j;
// 边界检查
if (newRow >= 0 && newRow < BOARD_SIZE &&
newCol >= 0 && newCol < BOARD_SIZE) {
revealCell(newRow, newCol);
}
}
}
}
checkWin();
}
7. 胜利判定
checkWin()
检查玩家是否已揭开所有安全格子:
javascript
html
function checkWin() {
let unrevealedSafeCells = 0;
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
if (!board[row][col].mine && !board[row][col].revealed) {
unrevealedSafeCells++;
}
}
}
if (unrevealedSafeCells === 0) {
gameOver = true;
alert('恭喜你,你赢了!');
}
}
游戏特点
-
递归展开:当点击空白格子时,会自动展开相邻的所有空白格子
-
即时反馈:点击地雷立即结束游戏,显示失败
-
标记功能:右键点击可以标记疑似地雷的格子
-
响应式设计:使用CSS Grid布局,适应不同屏幕大小
总结
这个扫雷游戏实现展示了如何使用基本的Web技术创建经典游戏。
通过合理的数据结构和算法设计,实现了游戏的核心逻辑。代码结构清晰,适合初学者学习和扩展。
html代码
如下:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>扫雷游戏</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
}
#game-board {
display: grid;
margin: 20px auto;
border: 2px solid #333;
}
.cell {
width: 30px;
height: 30px;
border: 1px solid #ccc;
text-align: center;
line-height: 30px;
cursor: pointer;
background-color: #eee;
}
.cell.revealed {
background-color: #fff;
cursor: default;
}
.cell.mine {
background-color: #ff4444;
}
.cell.flagged {
background-color: #ffcc00;
}
</style>
</head>
<body>
<h1>扫雷游戏</h1>
<div id="game-board"></div>
<script>
const BOARD_SIZE = 10;
const MINE_COUNT = 15;
let board = [];
let gameOver = false;
function createBoard() {
const gameBoard = document.getElementById('game-board');
gameBoard.style.gridTemplateColumns = `repeat(${BOARD_SIZE}, 30px)`;
for (let row = 0; row < BOARD_SIZE; row++) {
board[row] = [];
for (let col = 0; col < BOARD_SIZE; col++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = row;
cell.dataset.col = col;
cell.addEventListener('click', handleCellClick);
cell.addEventListener('contextmenu', handleRightClick);
gameBoard.appendChild(cell);
board[row][col] = { mine: false, revealed: false, flagged: false, count: 0 };
}
}
placeMines();
calculateNumbers();
}
function placeMines() {
let minesPlaced = 0;
while (minesPlaced < MINE_COUNT) {
const row = Math.floor(Math.random() * BOARD_SIZE);
const col = Math.floor(Math.random() * BOARD_SIZE);
if (!board[row][col].mine) {
board[row][col].mine = true;
minesPlaced++;
}
}
}
function calculateNumbers() {
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
if (board[row][col].mine) continue;
let count = 0;
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
const newRow = row + i;
const newCol = col + j;
if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE && board[newRow][newCol].mine) {
count++;
}
}
}
board[row][col].count = count;
}
}
}
function handleCellClick(event) {
if (gameOver) return;
const cell = event.target;
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
revealCell(row, col);
}
function handleRightClick(event) {
event.preventDefault();
if (gameOver) return;
const cell = event.target;
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
if (!board[row][col].revealed) {
board[row][col].flagged = !board[row][col].flagged;
cell.classList.toggle('flagged', board[row][col].flagged);
}
}
function revealCell(row, col) {
if (board[row][col].revealed || board[row][col].flagged) return;
const cell = document.querySelector(`.cell[data-row='${row}'][data-col='${col}']`);
board[row][col].revealed = true;
cell.classList.add('revealed');
if (board[row][col].mine) {
cell.classList.add('mine');
gameOver = true;
alert('游戏结束!你踩到了地雷!');
return;
}
cell.textContent = board[row][col].count || '';
if (board[row][col].count === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
const newRow = row + i;
const newCol = col + j;
if (newRow >= 0 && newRow < BOARD_SIZE && newCol >= 0 && newCol < BOARD_SIZE) {
revealCell(newRow, newCol);
}
}
}
}
checkWin();
}
function checkWin() {
let unrevealedSafeCells = 0;
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
if (!board[row][col].mine && !board[row][col].revealed) {
unrevealedSafeCells++;
}
}
}
if (unrevealedSafeCells === 0) {
gameOver = true;
alert('恭喜你,你赢了!');
}
}
createBoard();
</script>
</body>
</html>
