Samxander's home

You shall see the difference now that we are back again!

0%

简单扫雷游戏实现

如题,我编写了一个简单的C++程序,即一个简单扫雷游戏的实现。

游戏具有以下特点:

  • 每次输入时,会有输入提示,便于确定想打开的地块对应的行和列。
  • 基于 bfs 实现了连锁挖开地块,即点开一个无雷地块时,会连锁挖开其周围所有相邻的无雷地块,直到地块周围出现雷为止。
  • 可以自定义地图大小和地雷数量。

程序具有以下特点:

  • 游戏功能被封装在 Game 类中,使得代码具有更好的可维护性和扩展性。
  • 类的成员函数实现了扫雷游戏的核心逻辑,如放置地雷、计算周围地雷数量、处理连锁挖掘等。
  • 使用随机数生成,确保每次游戏的地雷位置不同
  • 进行边界的检查。当玩家输入不合法的位置时,会进行提示,让其重新输入。

对游戏的期望

  • 考虑增加图形化界面,使得与用户的交互性更好。
  • 引入插旗功能,使得用户能对疑似地雷的方块有更清晰的记录。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include <iostream>
#include <vector>
#include <queue>
#include <cstdlib>
#include <ctime>

using namespace std;

class Game { // 创建一个 Game 类
public:
Game(int row, int col, int mines);
void showMap(); // 函数声明,用于展示地图
void Play();

private:
int row, col, mines;
vector<vector<bool>> if_mine;
vector<vector<int>> mine_num;
vector<vector<bool>> if_dig;
void putMines(); // 函数声明,用于放置地雷
void countSurroundings(); // 函数声明,用于计算每个地块周围一圈的地雷数量
void digSurroundings(int x, int y); // 函数声明,用于连锁挖开地块
bool checkMines(int x, int y); // 函数声明,用于检查是否踩雷
bool checkWin(); // 函数声明,用于检测是否胜利
void showFinalMap(); // 函数声明,用于展示游戏结束后的地图
};

Game::Game(int r, int c, int m) : row(r), col(c), mines(m) { // 构造函数,初始化两个数组并展示地图
if_mine.resize(row, vector<bool>(col, false)); // 有雷为true,没雷为false
mine_num.resize(row, vector<int>(col, 0)); // 记录每个地块周围8个块的雷数量
if_dig.resize(row, vector<bool>(col, false)); // 记录地块是否挖开
putMines();
countSurroundings();
}

void Game::putMines()
{
srand(time(0));
int count = 0;
while (count < mines)
{
int r = rand() % row; // 随机生成行和列,如果没有雷就放置雷,直到放置指定数量
int c = rand() % col;
if (if_mine[r][c] == false)
{
if_mine[r][c] = true;
count++;
}
}
}

void Game::countSurroundings() // 检测周围 8 个地块中(或 5 个、3 个)雷的数量
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
int mineCount = 0;
for (int di = -1; di <= 1; ++di) {
for (int dj = -1; dj <= 1; ++dj) {
int ni = i + di, nj = j + dj;
if (ni >= 0 && ni < row && nj >= 0 && nj < col && if_mine[ni][nj]) {
mineCount++;
}
}
}
mine_num[i][j] = mineCount;
}
}
}

void Game::digSurroundings(int x, int y) // 要开始bfs挖地块了~
{
if (mine_num[x][y] != 0) return; // 如果当前格子不是空的,直接返回

int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; // 方向,分别是上、右上、右、右下、下、左下、左、左上
int dy[8] = {-1, 0, 1, 1, -1, -1, 0, 1};

vector<vector<bool>> visit(row, vector<bool>(col, false)); // 记录访问二维数组
queue<pair<int, int>> q; // 队列
q.push({x, y}); // 先把挖开的方块插入队尾
visit[x][y] = true;
if_dig[x][y] = true;

while (!q.empty())
{
pair<int, int> curr = q.front(); // 记录第一个元素,从你开始bfs
q.pop(); // 出队
x = curr.first;
y = curr.second;

for (int i = 0; i < 8; i++)
{
int newX = x + dx[i];
int newY = y + dy[i];

if (newX >= 0 && newX < row && newY >= 0 && newY < col && !visit[newX][newY])
{
visit[newX][newY] = true; // 访问后设为true
if_dig[newX][newY] = true; // 挖之后,将数组改为true,以便于其他函数能正常运行
if (mine_num[newX][newY] == 0)
{
q.push({newX, newY});
}
}
}
}
}

void Game::showMap()
{
int num = 0; // 每次输入行列的提示
cout << " ";
for (int i = 0; i < col; i++)
{
// 输出列提示
cout << i;
}
cout << endl;

for (int i = 0; i < row; i++)
{
cout << num; // 输出行提示
for(int j = 0; j < col; j++)
{
if (if_dig[i][j] == true && if_mine[i][j] != true)
{
cout << mine_num[i][j];
}
else if (if_dig[i][j] == false)
{
cout << "*";
}
}
cout << endl;
num++;
}
cout << "----------------------------" << endl;
}

void Game::showFinalMap()
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (if_dig[i][j] == true && if_mine[i][j] == false)
{
cout << mine_num[i][j];
}
else if (if_mine[i][j] == true)
{
cout << "X"; // 把雷全输出
}
else
{
cout << "*"; // 未挖开的就先不挖了
}
}
cout << endl;
}
}

bool Game::checkMines(int x, int y)
{
return if_mine[x][y]; // 检测是不是雷
}

bool Game::checkWin()
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (if_dig[i][j] == false && if_mine[i][j] == false)
{
return false;
}
}
}
return true;
}

void Game::Play()
{
int ro, co;
while (checkWin() != true)
{
showMap();
cout << "Please enter the row and column of the square you want to open: ";
cin >> ro >> co;
if(ro < 0 || ro >= row || co < 0 || co >= col)
{
cout << "Wuxiao input! Please try again" << endl; // 不能越界,否则重输入
continue;
}
if_dig[ro][co] = true;
digSurroundings(ro, co);
if (checkMines(ro, co) == true)
{
cout << "You lose!" << endl; // 一旦挖的块是雷,直接结束游戏,并展示地图中所有雷的位置
showFinalMap();
return;
}
}
cout << "You win!" << endl; // 不然就赢,也展示地图
showFinalMap();
}

int main()
{
int row = 0, col = 0;
cout << "Please enter the row and column of map: ";
cin >> row >> col;

int mines = 0;
cout << endl << "Please enter the number of mines: ";
cin >> mines;
cout << endl;

Game game(row, col, mines);
game.Play();
}
Insist on writing original high-quality articles. Your support is my biggest motivation.