扫雷游戏实现

AI 智能总结

简介

扫雷是经典的单人逻辑游戏,玩家需要根据数字提示推断地雷位置并安全翻开所有非雷格子。游戏厅版本支持三级难度、标旗/翻开两种模式切换,以及胜率统计功能。

数据结构

棋盘生成

使用二维数组存储每个格子的状态:

 1var board = []; // board[row][col] = { mine: bool, number: int, revealed: bool, flagged: bool }
 2var rows, cols, totalMines;
 3
 4function initBoard() {
 5  board = [];
 6  for (var r = 0; r < rows; r++) {
 7    board[r] = [];
 8    for (var c = 0; c < cols; c++) {
 9      board[r][c] = { mine: false, number: 0, revealed: false, flagged: false };
10    }
11  }
12}

难度配置

难度行数列数雷数
简单9910
中等161640
困难163099
1var difficulties = {
2  easy:   { rows: 9,  cols: 9,  mines: 10 },
3  medium: { rows: 16, cols: 16, mines: 40 },
4  hard:   { rows: 16, cols: 30, mines: 99 }
5};

核心算法

随机布雷

在玩家首次点击后布雷,确保首次点击和周围 8 格不是雷:

 1function placeMines(safeRow, safeCol) {
 2  var safeZone = [];
 3  for (var dr = -1; dr <= 1; dr++)
 4    for (var dc = -1; dc <= 1; dc++) {
 5      var r = safeRow + dr, c = safeCol + dc;
 6      if (r >= 0 && r < rows && c >= 0 && c < cols) safeZone.push([r, c]);
 7    }
 8
 9  var placed = 0;
10  while (placed < totalMines) {
11    var r = Math.floor(Math.random() * rows);
12    var c = Math.floor(Math.random() * cols);
13    if (board[r][c].mine) continue;
14    // 跳过安全区
15    if (safeZone.some(function(s) { return s[0] === r && s[1] === c; })) continue;
16    board[r][c].mine = true;
17    placed++;
18  }
19  calculateNumbers();
20}

数字计算

遍历每个格子,统计周围 8 格的雷数:

 1function calculateNumbers() {
 2  for (var r = 0; r < rows; r++)
 3    for (var c = 0; c < cols; c++) {
 4      if (board[r][c].mine) continue;
 5      var count = 0;
 6      for (var dr = -1; dr <= 1; dr++)
 7        for (var dc = -1; dc <= 1; dc++) {
 8          if (dr === 0 && dc === 0) continue;
 9          var nr = r + dr, nc = c + dc;
10          if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && board[nr][nc].mine) count++;
11        }
12      board[r][c].number = count;
13    }
14}

BFS 区域展开

点击数字为 0 的格子时,自动展开周围所有安全区域。使用广度优先搜索(BFS):

 1function revealCell(row, col) {
 2  if (board[row][col].revealed || board[row][col].flagged) return;
 3  board[row][col].revealed = true;
 4
 5  if (board[row][col].mine) {
 6    gameOver(false);
 7    return;
 8  }
 9
10  if (board[row][col].number === 0) {
11    var queue = [[row, col]];
12    var visited = {};
13    visited[row + ',' + col] = true;
14
15    while (queue.length > 0) {
16      var cur = queue.shift();
17      for (var dr = -1; dr <= 1; dr++)
18        for (var dc = -1; dc <= 1; dc++) {
19          var nr = cur[0] + dr, nc = cur[1] + dc;
20          if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue;
21          if (visited[nr + ',' + nc]) continue;
22          visited[nr + ',' + nc] = true;
23          if (board[nr][nc].mine || board[nr][nc].flagged) continue;
24          board[nr][nc].revealed = true;
25          if (board[nr][nc].number === 0) queue.push([nr, nc]);
26        }
27    }
28  }
29  checkWin();
30}

胜利判定

所有非雷格子都被翻开即为胜利:

1function checkWin() {
2  for (var r = 0; r < rows; r++)
3    for (var c = 0; c < cols; c++)
4      if (!board[r][c].mine && !board[r][c].revealed) return false;
5  return true;
6}

交互设计

标旗/翻开模式切换

提供两种操作模式的按钮切换:

  • 翻开模式:左键点击翻开格子
  • 标旗模式:左键点击插旗/取消旗子
1var mode = 'reveal'; // 'reveal' | 'flag'
2
3function toggleMode() {
4  mode = mode === 'reveal' ? 'flag' : 'reveal';
5  updateModeButton();
6}

右键标旗

右键点击始终执行标旗/取消操作,不受模式影响:

1cell.addEventListener('contextmenu', function(e) {
2  e.preventDefault();
3  toggleFlag(row, col);
4});

双击快速展开

在已翻开的数字格子上双击,如果周围旗子数等于数字,自动翻开周围未标记的格子:

1function chordReveal(row, col) {
2  if (!board[row][col].revealed || board[row][col].number === 0) return;
3  var flagCount = countFlagsAround(row, col);
4  if (flagCount !== board[row][col].number) return;
5  for (var dr = -1; dr <= 1; dr++)
6    for (var dc = -1; dc <= 1; dc++)
7      revealCell(row + dr, col + dc);
8}

胜率统计

使用 localStorage 记录游戏次数和胜利次数:

1function recordGame(won) {
2  var stats = JSON.parse(localStorage.getItem('minesweeper-stats') || '{"played":0,"won":0}');
3  stats.played++;
4  if (won) stats.won++;
5  localStorage.setItem('minesweeper-stats', JSON.stringify(stats));
6  updateStatsDisplay(stats);
7}

数字颜色

不同数字使用不同颜色,方便快速识别:

1.mine-cell.n1 { color: #0000ff; }  /* 1 - 蓝 */
2.mine-cell.n2 { color: #008000; }  /* 2 - 绿 */
3.mine-cell.n3 { color: #ff0000; }  /* 3 - 红 */
4.mine-cell.n4 { color: #000080; }  /* 4 - 深蓝 */
5.mine-cell.n5 { color: #800000; }  /* 5 - 深红 */
6.mine-cell.n6 { color: #008080; }  /* 6 - 青色 */
7.mine-cell.n7 { color: #000000; }  /* 7 - 黑 */
8.mine-cell.n8 { color: #808080; }  /* 8 - 灰 */

总结

扫雷的核心在于首次点击安全布雷和 BFS 区域展开算法。通过模式切换和双击快速展开,提升了操作效率。胜率统计功能增加了游戏的挑战性和可玩性。

留言评论

期待你的想法

评论加载中