游戏简介
扫雷,是一款益智类小游戏。
游戏目标是找出所有没有地雷的方格,完成游戏;要是按了有地雷的方格,游戏失败;玩家可标记雷的位置。游戏以完成时间来评高低。有不同的游戏难度可选择。
实现的功能介绍
1.计时
2.初始化雷盘
3.打印雷盘
4.随机设置雷的分布,可选择游戏难易程度
5.统计坐标位置周围的雷数
6.第一次排雷不会被炸死
7.扩展式排雷,展开周围的非雷区
8.给所选坐标位置做标记,或取消标记
该程序分为三个文件:
1.game.h :包含头文件的引用、函数的声明和宏定义
2.game.c :包含游戏各功能函数的具体实现
3.pro.c :各功能函数的调用(程序的流程)
PS:文章末尾附完整代码 及 游戏效果图
因为排雷时要计算每个位置周围八个位置的雷数,所以在创建数组时要多一圈,即行列都要加2。给用户显示的数组不需要加。
游戏功能代码详解
1.计时
运用clock函数,该函数需要的头文件为 “time.h”
函数原型:clock_t clock(void);
功能:程序从启动到函数调用占用CPU的时间
这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间;若挂钟时间不可取,则返回-1。其中clock_t是用来保存时间的数据类型。
1
2
3
4void set_time()//计时 { printf("用时:%u 秒n", clock() / CLOCKS_PER_SEC); }
2.初始化雷盘
这里我用到的是memset函数,需要的头文件为“memory.h”或“string.h”
函数原型:void *memset(void *s, int ch, size_t n);
功能:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。在一段内存块中填充某个给定的值。
1
2
3
4void init_board(char board[ROWS][COLS], int row, int col, char c)//初始化雷盘 { memset(board, c, row*col*sizeof(board[0][0])); }
3.打印雷盘
运用两个循环体实现雷盘数组的赋值、行号、列号的打印。正式游戏时可以加上system(“CLS”); 清屏语句,每次调用时都清屏一次,使游戏画面更简洁清晰。
我们把计时函数放在里面,每次打印雷盘时就可以显示所用的时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void disp_board(char board[ROWS][COLS], int row, int col)//打印雷盘 { int i = 0; int j = 0; //system("CLS");//清屏 for (i = 0; i <= row; i++) { printf("%2d ", i);//打印行号 } printf("n"); for (i = 1; i <= row; i++) { printf("%2d ", i);//打印列号 for (j = 1; j <= col; j++) { printf("%2c ", board[i][j]); } printf("n"); } printf("n"); set_time();//打印所用的时间 }
4.随机设置雷的分布,可选择游戏难易程度
放置雷必须是随机的,这里用到了rand函数,它和srand函数配合使用产生随机数。srand(time(NULL))放在主函数中调用一次,通过系统时间提供的种子值,使rand函数生成不同的伪随机数序列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void set_mine(char board[ROWS][COLS], int row, int col,int count)//置雷 { int x = 0; int y = 0; while (count) { x = rand() % row + 1;//随机位置范围1~row y = rand() % col + 1;//随机位置范围1~col if (board[x][y] == '0')//判断是否已有雷 { board[x][y] = '1';//有雷的位置赋为1 count--; } } }
5.统计坐标位置周围的雷数 及 未扫的位置的个数
当扫到一个没有雷的位置时,会显示这个位置周围一圈八个位置的含雷的总数,所以我们要写一个“数雷”函数来数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23int count_mine(char mine[ROWS][COLS], int x, int y)//数雷 { return mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y] + mine[x + 1][y - 1] + mine[x][y - 1] + mine[x - 1][y - 1] - 8 * '0';//数周围一圈八个位置的雷数 } int count_print(char print[ROWS][COLS], int row, int col)//数未扫位置 { int count = 0; int i = 0; for (i = 1; i <= row; i++) { int j = 0; for (j = 1; j <= col; j++) { if (print[i][j] == '@' ||print[i][j] == '*') { count++; } } } return count; }
6.第一次排雷不会被炸死
为了增加游戏的可玩性,加入“第一次排雷不被炸死”这个函数。当第一次排就遇到雷时,我们把雷偷偷挪走,随机放在一个原本无雷的位置。
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
26void safe_mine(char mine[ROWS][COLS],char print[ROWS][COLS],int x,int y,int row,int col)//第一次排雷不炸死 { char ch = 0; int ret = 1; int number = 0; if (mine[x][y] == '1')//第一次踩到雷后补救 { mine[x][y] = '0'; char ch = count_mine(mine, x, y); print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine, print, x, y); while (ret)//在其余有空的地方设置一个雷 { int x = rand() % row + 1;//产生1到row的随机数,在数组下标为1到10的范围内布雷 int y = rand() % col + 1;//产生1到col的随机数,在数组下标为1到10的范围内布雷 if (mine[x][y] == '0')//找不是雷的地方布雷 { mine[x][y] = '1'; disp_board(print, row, col); //disp_board(mine, row, col); ret--; break; } } } }
7.扩展式排雷,展开周围的非雷区
当游戏中排到一个周围一圈都无雷的位置时,运用递归,实现扩展展开周围的一片无雷区。
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
88void extend_board(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y)//运用递归扩展周围 { int n = 0; n = count_mine(mine, x, y); if (n == 0)//当该位置周围雷数为0时扩展 { print[x][y] = ' ';//扩展的位置变为“空格”打印出来 if (mine[x - 1][y] == '0' && print[x - 1][y] == '@') { extend_board(mine, print, x - 1, y);//递归 } if (mine[x + 1][y] == '0' && print[x + 1][y] == '@') { extend_board(mine, print, x + 1, y); } if (mine[x][y + 1] == '0' && print[x][y + 1] == '@') { extend_board(mine, print, x, y + 1); } if (mine[x - 1][y + 1] == '0' && print[x - 1][y + 1] == '@') { extend_board(mine, print, x - 1, y + 1); } if (mine[x + 1][y + 1] == '0' && print[x + 1][y + 1] == '@') { extend_board(mine, print, x + 1, y + 1); } if (mine[x][y - 1] == '0' && print[x][y - 1] == '@') { extend_board(mine, print, x, y - 1); } if (mine[x + 1][y - 1] == '0' && print[x + 1][y -1] == '@') { extend_board(mine, print, x + 1, y - 1); } if (mine[x - 1][y - 1] == '0' && print[x - 1][y - 1] == '@') { extend_board(mine, print, x - 1, y - 1); } } else print[x][y] = n + '0'; } int find_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int row, int col,int count)//排雷 { int x = 0; int y = 0; int number = 0; int ret = 0; while (1) { printf("输入坐标扫雷n"); scanf("%d%d", &x, &y);//玩家输入扫雷的坐标位置 if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//判断输入坐标是否有误,输入错误重新输入 { if (mine[x][y] == '0')//没踩到雷 { number++;//记录扫雷的次数 char ch = count_mine(mine, x, y);//数雷数 print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine, print, x, y); disp_board(mine, row, col); disp_board(print, row, col); if (count_print(print, row, col) == count)//剩余未扫位置=雷数 时胜利 { return 0; } to_sign(print);//判断是否标记 disp_board(print, row, col); } else if (mine[x][y] == '1')//踩到雷 { if (ret == 0 && number == 0) { ret++; safe_mine(mine,print,x,y,row,col); } else return 1; } } else { printf("输入错误!请重新输入n"); } } }
8.给所选坐标位置做标记,或取消标记
扫雷游戏还有一个功能:可以给你认为是雷的位置标记,或者取消标记。
我通过三个函数来实现,一个判断用户是否需要标记;一个实现标记功能,将@标记成* ;一个实现取消标记功能,将* 改回@。
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
54void to_sign(char board[ROWS][COLS])//判断是否标记 { int chose_b = 0; int x = 0; int y = 0; printf("是否需要标记/取消标记:>n(1.标记 ;2.取消标记 ;3.跳过该步骤) :>"); scanf("%d", &chose_b); do{ switch (chose_b) { case 1: { printf("请输入需要标记的位置坐标:>n"); scanf("%d%d", &x, &y); sign(board, x, y); break; } case 2: { printf("请输入取消标记的位置坐标:>n"); scanf("%d%d", &x, &y); unsign(board, x, y); break; } case 3: { printf("跳过此步骤。n"); chose_b = 0; break; } default: { printf("输入错误!n"); chose_b = 0; break; } } chose_b = 0; } while (chose_b); } void sign(char board[ROWS][COLS], int x, int y)//用‘*'标记雷 { if (board[x][y] == '@') { board[x][y] = '*'; } } void unsign(char board[ROWS][COLS], int x, int y)//取消标记 { if (board[x][y] == '*') { board[x][y] = '@'; } }
附:完整代码
game.h
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#ifndef _GAME_H_ #define _GAME_H_ //用到的头文件 #include<stdio.h> #include<stdlib.h> #include<time.h> #include<string.h> #include<windows.h> //定义打印的雷盘行、列 #define _ROW 9 #define _COL 9 #define ROW 16 #define COL 16 //定义数组的行、列 #define _ROWS _ROW+2 #define _COLS _COL+2 #define ROWS ROW+2 #define COLS COL+2 //定义难、易程度雷数 #define EASY_COUNT 10 #define HARD_COUNT 40 //定义游戏中的函数 void init_board(char board[ROWS][COLS],int row, int col, char c);//初始化 void disp_board(char board[ROWS][COLS],int row,int col);//打印 void set_mine(char board[ROWS][COLS], int row, int col,int count);//置雷 void safe_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y, int row, int col);//第一次排雷不炸死 int find_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int row, int col,int count);//排雷 int count_mine(char mine[ROWS][COLS], int x, int y);//数雷 void extend_board(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y);//扩展 void to_sign(char board[ROWS][COLS]);//判断是否标记 void sign(char board[ROWS][COLS], int x, int y);//标记 void unsign(char board[ROWS][COLS], int x, int y);//取消标记 int count_print(char print[ROWS][COLS], int row, int col);//数未扫位置 #endif//_GAME_H_
game.c
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248#define _CRT_SECURE_NO_WARNINGS #include "game.h" void set_time()//计时 { printf("用时:%u 秒n", clock() / CLOCKS_PER_SEC); } void init_board(char board[ROWS][COLS], int row, int col, char c)//初始化雷盘 { memset(board, c, row*col*sizeof(board[0][0])); } void disp_board(char board[ROWS][COLS], int row, int col)//打印雷盘 { int i = 0; int j = 0; system("CLS");//清屏 for (i = 0; i <= row; i++)//加行号 { printf("%2d ", i); } printf("n"); for (i = 1; i <= row; i++)//加列号 { printf("%2d ", i); for (j = 1; j <= col; j++) { printf("%2c ", board[i][j]); } printf("n"); } printf("n"); set_time();//打印所用的时间 } void set_mine(char board[ROWS][COLS], int row, int col,int count)//置雷 { int x = 0; int y = 0; while (count) { x = rand() % row + 1;//随机位置范围1~row y = rand() % col + 1;//随机位置范围1~col if (board[x][y] == '0')//判断是否已有雷 { board[x][y] = '1'; count--; } } } void safe_mine(char mine[ROWS][COLS],char print[ROWS][COLS],int x,int y,int row,int col)//第一次排雷不炸死 { char ch = 0; int ret = 1; int number = 0; if (mine[x][y] == '1')//第一次踩到雷后补救 { mine[x][y] = '0'; char ch = count_mine(mine, x, y); print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine, print, x, y); while (ret)//在其余有空的地方设置一个雷 { int x = rand() % row + 1;//产生1到row的随机数,在数组下标为1到10的范围内布雷 int y = rand() % col + 1;//产生1到col的随机数,在数组下标为1到10的范围内布雷 if (mine[x][y] == '0')//找不是雷的地方布雷 { mine[x][y] = '1'; disp_board(print, row, col); //disp_board(mine, row, col); ret--; break; } } } } int count_mine(char mine[ROWS][COLS], int x, int y)//数雷 { return mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y] + mine[x + 1][y - 1] + mine[x][y - 1] + mine[x - 1][y - 1] - 8 * '0';//数周围一圈八个位置的雷数 } int count_print(char print[ROWS][COLS], int row, int col)//数未扫位置 { int count = 0; int i = 0; for (i = 1; i <= row; i++) { int j = 0; for (j = 1; j <= col; j++) { if (print[i][j] == '@' ||print[i][j] == '*') { count++; } } } return count; } void extend_board(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y)//运用递归扩展周围 { int n = 0; n = count_mine(mine, x, y); if (n == 0)//当该位置周围雷数为0时扩展 { print[x][y] = ' ';//扩展的位置变为“空格”打印出来 if (mine[x - 1][y] == '0' && print[x - 1][y] == '@') { extend_board(mine, print, x - 1, y);//递归 } if (mine[x + 1][y] == '0' && print[x + 1][y] == '@') { extend_board(mine, print, x + 1, y); } if (mine[x][y + 1] == '0' && print[x][y + 1] == '@') { extend_board(mine, print, x, y + 1); } if (mine[x - 1][y + 1] == '0' && print[x - 1][y + 1] == '@') { extend_board(mine, print, x - 1, y + 1); } if (mine[x + 1][y + 1] == '0' && print[x + 1][y + 1] == '@') { extend_board(mine, print, x + 1, y + 1); } if (mine[x][y - 1] == '0' && print[x][y - 1] == '@') { extend_board(mine, print, x, y - 1); } if (mine[x + 1][y - 1] == '0' && print[x + 1][y -1] == '@') { extend_board(mine, print, x + 1, y - 1); } if (mine[x - 1][y - 1] == '0' && print[x - 1][y - 1] == '@') { extend_board(mine, print, x - 1, y - 1); } } else print[x][y] = n + '0'; } int find_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int row, int col,int count)//排雷 { int x = 0; int y = 0; int number = 0; int ret = 0; while (1) { printf("输入坐标扫雷n"); scanf("%d%d", &x, &y);//玩家输入扫雷的坐标位置 if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//判断输入坐标是否有误,输入错误重新输入 { if (mine[x][y] == '0')//没踩到雷 { number++;//记录扫雷的次数 char ch = count_mine(mine, x, y);//数雷数 print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine, print, x, y); // disp_board(mine, row, col); disp_board(print, row, col); if (count_print(print, row, col) == count)//剩余未扫位置=雷数 时胜利 { return 0; } to_sign(print);//判断是否标记 disp_board(print, row, col); } else if (mine[x][y] == '1')//踩到雷 { if (ret == 0 && number == 0) { ret++; safe_mine(mine,print,x,y,row,col); } else return 1; } } else { printf("输入错误!请重新输入n"); } } } void sign(char board[ROWS][COLS], int x, int y)//用‘*'标记雷 { if (board[x][y] == '@') { board[x][y] = '*'; } } void unsign(char board[ROWS][COLS], int x, int y)//取消标记 { if (board[x][y] == '*') { board[x][y] = '@'; } } void to_sign(char board[ROWS][COLS])//判断是否标记 { int chose_b = 0; int x = 0; int y = 0; printf("是否需要标记/取消标记:>n(1.标记 ;2.取消标记 ;3.跳过该步骤) :>"); scanf("%d", &chose_b); do{ switch (chose_b) { case 1: { printf("请输入需要标记的位置坐标:>n"); scanf("%d%d", &x, &y); sign(board, x, y); break; } case 2: { printf("请输入取消标记的位置坐标:>n"); scanf("%d%d", &x, &y); unsign(board, x, y); break; } case 3: { printf("跳过此步骤。n"); chose_b = 0; break; } default: { printf("输入错误!n"); chose_b = 0; break; } } chose_b = 0; } while (chose_b); }
pro.c
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#define _CRT_SECURE_NO_WARNINGS #include "game.h" void menu() { printf("+---------------------------------+n"); printf("+ Welcome to 扫雷世界 ! +n"); printf("+ ο(=>ω<=)ρ⌒☆ +n"); printf("+ 1、play +n"); printf("+ 0、exit +n"); printf("+---------------------------------+n"); } void game() { char mine[ROWS][COLS] = { 0 }; char print[ROWS][COLS] = { 0 }; int chose_m = 0; int ret = 0; printf("请选择模式(1、简单 2、困难):>");//选择游戏难易程度,产生不同大小的棋盘和雷数 scanf("%d", &chose_m); switch (chose_m) { case 1: { init_board(mine, ROWS, COLS, '0');//初始化雷盘 init_board(print, ROWS, COLS, '@'); set_mine(mine, _ROW, _COL, EASY_COUNT);//布雷 // disp_board(mine, _ROW, _COL);//打印雷盘 disp_board(print, _ROW, _COL); int ret = find_mine(mine, print, _ROW, _COL, EASY_COUNT);//扫雷,踩到雷返回1,没有踩到雷返回0 while (1)//循环扫雷 { if (ret == 0)//若返回0则胜利 { disp_board(print, _ROW, _COL); printf("WOW~ YOU WIN!nn"); break; } if (ret)//若返回1则失败 { disp_board(mine, _ROW, _COL);//打印雷盘 printf("GAME OVER!n"); break; } disp_board(print, _ROW, _COL);//打印玩家棋盘 } break; } case 2: { init_board(mine, ROWS, COLS, '0');//初始化雷盘 init_board(print, ROWS, COLS, '@'); set_mine(mine, ROW, COL, HARD_COUNT);//布雷 // disp_board(mine, ROW, COL);//打印雷盘 disp_board(print, ROW, COL); while (1)//循环扫雷 { int ret = find_mine(mine, print, ROW, COL, HARD_COUNT);//扫雷,踩到雷返回1,没有踩到雷返回0 if (ret == 0)//若返回0胜利 { disp_board(print, ROW, COL); printf("WOW~ YOU WIN!nn"); break; } if (ret)//若返回1失败 { disp_board(mine, ROW, COL);//打印雷盘 printf("GAME OVER!n"); break; } disp_board(print, ROW, COL);//打印玩家棋盘 } break; } default: { printf("输入错误!n"); break; } } } void text() { srand((unsigned int)time(NULL));//产生随机值发生器 int chose = 0;//选择是否开始游戏 do { menu();//菜单 printf("请选择:>"); scanf("%d", &chose); switch (chose) { case 1: game();//开始游戏 break; case 0: printf("退出游戏n"); break; default: printf("输入错误,没有该选项n"); break; } } while (chose); } int main() { text(); system("pause"); return 0; }
游戏效果图
①开始选择菜单、难易模式选择
②两种难度扫雷
↓9×9雷盘 10颗雷
↓16×16雷盘 40颗雷
③演示标记
④GAME OVER 玩家失败演示
⑤WIN 玩家成功演示
最后
以上就是现实黑猫最近收集整理的关于C语言实现扫雷小游戏(扩展版可选择游戏难度)的全部内容,更多相关C语言实现扫雷小游戏(扩展版可选择游戏难度)内容请搜索靠谱客的其他文章。
发表评论 取消回复