扫雷是经典的益智游戏,传统版本多带有图形界面,而控制台版扫雷可以让我们专注于核心逻辑的实现,非常适合用来练习JavaScript的基础语法和算法思维。整个游戏运行在Node.js环境中,通过命令行交互完成操作。

需求分析
控制台版扫雷需要实现以下核心功能:
- 生成指定大小的棋盘,默认是9行9列
- 随机在棋盘中布置固定数量的地雷,默认10个
- 玩家输入坐标进行扫雷操作
- 点击到地雷则游戏失败,点击到非地雷区域则显示周围地雷数量
- 如果点击到空白区域(周围地雷数为0),则递归展开所有相邻的空白区域
- 当所有非地雷区域都被揭开时,游戏胜利
核心实现步骤
1. 初始化棋盘
首先我们需要创建两个二维数组,一个用来存储地雷的实际分布,另一个用来存储玩家看到的棋盘状态。地雷分布数组中用1表示地雷,0表示安全区域;玩家棋盘数组中用*表示未揭开的区域,数字表示周围地雷数量。
// 定义棋盘行数、列数和地雷数量
const ROWS = 9;
const COLS = 9;
const MINES = 10;
// 初始化地雷分布数组,全部填充0
const mineMap = Array.from({ length: ROWS }, () => Array(COLS).fill(0));
// 初始化玩家可见棋盘,全部填充*
const playerMap = Array.from({ length: ROWS }, () => Array(COLS).fill('*'));
2. 随机布置地雷
我们需要随机生成MINES个不同的坐标,在地雷分布数组中标记为1。为了避免重复布置,每次生成坐标后需要检查该位置是否已经有地雷。
function placeMines() {
let placed = 0;
while (placed < MINES) {
// 生成0到ROWS-1的随机行索引
const row = Math.floor(Math.random() * ROWS);
// 生成0到COLS-1的随机列索引
const col = Math.floor(Math.random() * COLS);
// 如果该位置没有地雷,则放置地雷
if (mineMap[row][col] === 0) {
mineMap[row][col] = 1;
placed++;
}
}
}
3. 计算每个区域周围的地雷数量
对于每个非地雷区域,我们需要统计它周围8个相邻位置的地雷总数,这个数量会显示给玩家。首先编写一个函数用来统计指定坐标周围的地雷数量。
function countAdjacentMines(row, col) {
let count = 0;
// 遍历周围8个方向
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
// 跳过自身位置
if (i === 0 && j === 0) continue;
const newRow = row + i;
const newCol = col + j;
// 检查坐标是否在棋盘范围内
if (newRow >= 0 && newRow < ROWS && newCol >= 0 && newCol < COLS) {
// 如果相邻位置是地雷,计数加1
if (mineMap[newRow][newCol] === 1) {
count++;
}
}
}
}
return count;
}
4. 递归展开空白区域
当玩家点击的区域周围地雷数为0时,需要自动展开所有相邻的空白区域,这个逻辑需要用递归实现。递归的终止条件是:坐标超出范围、该位置已经被揭开、该位置是地雷。
function expandArea(row, col) {
// 坐标超出范围,直接返回
if (row < 0 || row >= ROWS || col < 0 || col >= COLS) return;
// 该位置已经被揭开,直接返回
if (playerMap[row][col] !== '*') return;
// 该位置是地雷,不展开
if (mineMap[row][col] === 1) return;
// 计算该位置周围的地雷数量
const mineCount = countAdjacentMines(row, col);
// 将数量更新到玩家棋盘
playerMap[row][col] = mineCount.toString();
// 如果周围地雷数为0,递归展开相邻区域
if (mineCount === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
if (i === 0 && j === 0) continue;
expandArea(row + i, col + j);
}
}
}
}
5. 游戏主逻辑与交互
最后我们需要编写游戏的主循环,负责打印棋盘、接收玩家输入、处理操作结果、判断游戏胜负。这里使用Node.js的readline模块来实现命令行输入交互。
const readline = require('readline');
// 创建readline接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 打印玩家棋盘
function printPlayerMap() {
// 打印列号
process.stdout.write(' ');
for (let i = 0; i < COLS; i++) {
process.stdout.write(` ${i} `);
}
process.stdout.write('n');
// 打印每一行
for (let i = 0; i < ROWS; i++) {
// 打印行号
process.stdout.write(`${i} `);
for (let j = 0; j < COLS; j++) {
process.stdout.write(` ${playerMap[i][j]} `);
}
process.stdout.write('n');
}
}
// 检查游戏是否胜利
function checkWin() {
for (let i = 0; i < ROWS; i++) {
for (let j = 0; j < COLS; j++) {
// 如果存在未揭开的非地雷区域,游戏未胜利
if (mineMap[i][j] === 0 && playerMap[i][j] === '*') {
return false;
}
}
}
return true;
}
// 游戏主流程
function startGame() {
// 布置地雷
placeMines();
console.log('欢迎来到控制台扫雷游戏!');
console.log('请输入坐标,格式为:行 列,例如:1 2');
console.log('输入q可以退出游戏');
// 游戏循环
function gameLoop() {
printPlayerMap();
rl.question('请输入你的操作:', (input) => {
// 输入q退出游戏
if (input.trim().toLowerCase() === 'q') {
console.log('游戏已退出');
rl.close();
return;
}
// 解析输入的坐标
const parts = input.trim().split(' ');
if (parts.length !== 2) {
console.log('输入格式错误,请按照 行 列 的格式输入');
gameLoop();
return;
}
const row = parseInt(parts[0]);
const col = parseInt(parts[1]);
// 检查坐标是否合法
if (isNaN(row) || isNaN(col) || row < 0 || row >= ROWS || col < 0 || col >= COLS) {
console.log('坐标不合法,请输入0到8之间的数字');
gameLoop();
return;
}
// 检查该位置是否已经被揭开
if (playerMap[row][col] !== '*') {
console.log('该位置已经被揭开,请选择其他位置');
gameLoop();
return;
}
// 点击到地雷,游戏失败
if (mineMap[row][col] === 1) {
console.log('你踩到地雷了!游戏失败');
// 打印所有地雷的位置
for (let i = 0; i < ROWS; i++) {
for (let j = 0; j < COLS; j++) {
if (mineMap[i][j] === 1) {
playerMap[i][j] = 'X';
}
}
}
printPlayerMap();
rl.close();
return;
}
// 展开区域
expandArea(row, col);
// 检查是否胜利
if (checkWin()) {
console.log('恭喜你,扫雷成功!游戏胜利');
printPlayerMap();
rl.close();
return;
}
// 继续游戏循环
gameLoop();
});
}
gameLoop();
}
// 启动游戏
startGame();
运行与测试
将上述代码保存为minesweeper.js文件,确保本地已经安装Node.js环境,在命令行中执行node minesweeper.js即可启动游戏。按照提示输入行和列的坐标,比如输入0 0就是点击左上角的第一个格子,输入q可以随时退出游戏。
你可以根据自己的需求修改ROWS、COLS和MINES常量来调整棋盘大小和地雷数量,比如改成16行16列40个地雷,就可以得到中级难度的扫雷游戏。
JavaScript扫雷游戏控制台程序递归算法修改时间:2026-07-22 03:51:15