- 一.游戏的功能设计
- 1.游戏的玩法介绍
- 2.游戏的功能介绍
- 二.游戏效果展示
- 1.显示该点坐标周围的雷数
- 2.开局模拟扫雷游戏设置一片空白
- 3.有标记功能和取消标记的功能
- (1)标记功能的实现的效果展示
- (2)取消标记的功能实现效果展示
- (3)排到雷的效果展示
- (4)排雷成功的效果展示
- 三.游戏的整体设计思路
- (1)游戏菜单的显示
- (2)雷盘大小的问题
- (3)对两个数组的初始化
- (4)雷盘的打印
- (5)雷区的设置
- (6)统计输入坐标周围雷的个数
- (7)开局爆炸式空白
- (8)扫雷函数
- (9)标记功能
- (10)取消标记功能
- (11)游戏胜负的判断
- (12)整合
- 四.总结与反思
我们的扫雷用不是鼠标,而是键盘
(1)显示该点坐标周围的雷数
(2)开局模拟扫雷游戏设置一片空白
(3)有标记功能和取消标记的功能
不整有的没的,直接给大家附上效果图
1.显示该点坐标周围的雷数
通过图中看的出,开局完全模拟了扫雷游戏的直接设置的一片空白和显示该点坐标周围的雷数
如上图所示
3.有标记功能和取消标记的功能 (1)标记功能的实现的效果展示注意看坐标(5,4)的变化
注意看坐标(2,7)的前后变化
游戏的玩法很简单,通过键盘输入来模拟鼠标点击,虽然不是鼠标点击,但也是很有趣!
(1)游戏菜单的显示简单写了个游戏菜单,如果大家有想法,可以加模式上去,我就不加了,初学者嘛。。。我写出的代码如下
void menu()
{
printf("***************************\n");
printf("**********0.exit***********\n");
printf("**********1.play***********\n");
printf("***************************\n");
}
效果的展示如下图
我们需要两个数组来完成雷盘的布置,一个设置雷区的雷盘,一个是显示给玩家的雷盘,我这里设计的是99的雷盘,但是需要两个为1111的二维数组,为什么要这样呢?因为我们在设计算法时需要统计坐标周围8个方位雷的个数,假如要统计边界坐标周围雷的个数,那么就会有数组越界的问题,那我们就要在99的边界多给上一圈元素,也就要定义1111的数组元素,这些元素我们不需要打印出来,自己清楚就行。
#define ROW 9 //设置雷盘的大小的数字,用宏定义,方便以后改动
#define COL 9
#define ROWS ROW+2//设置数组的大小,防止访问越界的问题
#define COLS COL+2
我这里用的编译器是vs2019,需要的头文件和源文件如下图所示
我们在第一步说过雷盘的问题,因此我们需要两个数组,一个是用来显示的数组,一个是用来设置雷区的数组。在显示数组上,我默认用 ’ * ’ 作为显示的内容,而设置雷区的数组,我用字符 ’ 0 ’ 代表不是雷,字符 ’ 1 ’ 代表是雷。代码内容如下
void array_init(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col)//数组初始化
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
dis[i][j] = '*';//dis数组是用来显示数组
mine[i][j] = '0';//mine数组是用来设置雷区的数组
}
}
}
(4)雷盘的打印
对于雷盘的打印,我不仅要在雷盘上显示 ’ * ',还要给玩家一个坐标的参考,因此考虑到格式的问题,我写出的代码如下
void display(char dis[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf(" ");
for (j = 1; j <=col; j++)//给玩家列坐标的参考
{
printf(" %d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)//在雷盘上打印' * '
{
printf("%d", i);//给玩家行坐标的参考
for (j = 1; j <=col; j++)
{
printf(" %c ", dis[i][j]);
}
printf("\n");
}
}
效果的展示,(少打个换行。。。不要在意)
如果大家都玩过扫雷游戏,都知道游戏的雷区是随机设置的,所以我这里也模拟了扫雷游戏,写出了随机雷的布置代码。
void setmine(char mine[ROWS][COLS])//雷区设置函数
{
int num =count;
while (num)
{
int x = rand() % 9 + 1;//随机数,他们对9取模,数值为0~8,我这里加上1,所以数值为1~9
int y = rand() % 9 + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';//设置雷为字符1
num--;
}
}
}
两次不同游戏的雷区展示图
直接用的上面的,就不再重开一把了,希望谅解
扫雷游戏是每点击一个不是雷的区域,就显示该区域周围雷的个数,如果没有雷则显示空白,我在这里也写出了相应的代码
int get_count(char mine[ROWS][COLS], int row, int col)//统计周围一圈雷的个数
{
int x = row;
int y = col;
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +//第一行的统计
mine[x][y - 1] + mine[x][y + 1] +//第二行的统计
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';//第三行的统计
}
我这里给大家讲解一下
然后是在每次坐标输入的时候,统计该坐标周围的雷的个数,所以具体代码截图如下
每次统计所得的结果加上字符’ 0 ',使其再次变回字符型数据,得以在棋盘上显示,同时通过用选择结构判断,如果该点周围没有雷,则显示空格,即显示空白,如果该点周围有雷,则显示雷的个数,效果的展示开局就有,所以就不再给大家展示了。
扫雷游戏开局都是显示一片空白,我也写了个函数,使其开局也是一片空白
代码如下:
void setblank(char dis[ROWS][COLS], char mine[ROWS][COLS])//开局直接设置一片空白
{
while (1)
{
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (mine[x][y] == '0')
{
judgearound(dis, mine, x, y);
break;
}
else
{
continue;
}
}
}
void judgearound(char dis[ROWS][COLS], char mine[ROWS][COLS], int x, int y)//生成爆炸式空白的函数
{
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (dis[i][j] != ' ' && i != 0 && i != ROWS - 1 && j!=0 && j != COLS - 1)//判断该坐标是否是空格以及判断坐标是否合法
{
int num= get_count(mine, i, j);//统计x,y坐标周围一圈是否有雷
dis[i][j] = num + '0';//有雷则给dis数组显示雷的个数
if (dis[i][j] == '0'&&mine[i][j]=='0')//如果x,y坐标周围一圈没有雷并且自己本身不是雷
{
dis[i][j] = ' ';//则将该坐标变成空格
judgearound(dis, mine, i, j);//再递归判断
}
}
}
}
}
一个是模拟开局空白的函数,利用随机产生的坐标,然后利用迭代加递归来写一个函数来判断随机产生的坐标周围是否符合爆炸式空白的条件。效果图上面就有,在这就不再展示了
(8)扫雷函数扫雷在我这里是通过键盘输入坐标来实现扫雷,同时增加了标记与取消标记的功能,也都是通过键盘输入坐标来实现的,代码如下:
void inputdata(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col)//游戏胜负的判断
{
int x = 0;
int y = 0;
int cnt = 0;
cnt = get_nomine(dis);
do
{
printf("请输入坐标:>");//给提示信息
scanf("%d%d", &x, &y);//输入坐标
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)//判断坐标的合法性
{
if (dis[x][y] != '*'&&dis[x][y]==' ')//如果该坐标已被排除
{
printf("该坐标已被排查过,请重新输入坐标:>\n");//则给出提醒信息
}
else
{
if (mine[x][y] != '1')//如果不是雷
{
cnt++;
int num= get_count(mine, x, y);//统计该坐标周围雷的个数
dis[x][y] = num + '0';
if (dis[x][y] != '0')
{
printf("\n");
display(dis, row, col);//更新显示
}
else if (dis[x][y] == '0')
{
dis[x][y] = ' ';
printf("\n");
display(dis, row, col);//更新显示
}
setflag(dis, mine, row, col);//是否选择标记的函数
cancelflag(dis, mine, row, col);//是否选择取消标记的函数
printf("\n");
display(dis, row, col);//更新显示
}
else
{
printf("很不幸,你被炸死了!游戏结束\n");
display(mine, row, col);
break;
}
}
}
else
{
printf("坐标非法,请重新输入\n");
}
} while (cnt < row * col-count);
if (cnt == row * col - count)
{
printf("排雷成功,游戏结束\n");
display(mine, row, col);
}
}
其实上面的代码都是一些逻辑的问题,讲解起来不太好讲的清楚,主要是读者需要熟练掌握函数的调用即可清楚明白我这里写的意思,效果图开局就有动态图显示,请移步网上看哈
(9)标记功能我们都知道扫雷是有标记功能的,所以代码如下:
void setflag(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int input = 0;
do
{
printf("是否选择标记 ? 0.不标记 1.标记\n");
scanf("%d", &input);
if (input == 1)
{
setsymbol(dis, mine, row, col);
display(dis, row, col);
}
else
break;
} while (1);
}
void setsymbol(char dis[ROWS][COLS],char mine[ROWS][COLS],int row,int col)//标记雷的函数
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入要标记的坐标\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)//判断坐标是否合法
{
if (dis[x][y] != '!')//如果该坐标未被标记
{
dis[x][y] = '!';
break;
}
else if(dis[x][y]=='!')//如果该坐标已被标记,如果不加上判断条件,系统会把带'*'的也当做已标记
{
printf("该坐标已被标记,请选择其他坐标\n");//已被标记则给出提示信息
}
}
else//坐标非法,则给出提示信息,重新输入
{
printf("坐标非法,请重新输入需要标记的坐标\n");
}
}
}
我这里写了两个函数,一个决定是否要标记,一个是决定要标记后,通过函数的调用来通过键盘的输入,决定标记哪个坐标,效果图开篇就有,请移步
(10)取消标记功能这里也是模仿扫雷写了个取消标记的功能,代码如下:
void cancelflag(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col)//取消标记的函数
{
int input = 0;
do
{
printf("是否选择取消标记 ? 0.否 1.是\n");
scanf("%d", &input);
if (input == 1)
{
cancelsymbol(dis, mine, row, col);
display(dis, row, col);
}
else
break;
} while (1);
}
void cancelsymbol(char dis[ROWS][COLS],char mine[ROWS][COLS],int row,int col)//取消标记雷的函数
{
int x = 0;
int y = 0;
int flag = 0;
while (1)
{
printf("请输入要取消标记的坐标\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)//判断坐标是否合法
{
if (dis[x][y] == '!')//如果该坐标未被标记
{
dis[x][y] = '*';
break;
}
else if(dis[x][y]!='!')//如果该坐标已被标记,如果不加上判断条件,系统会把带'*'的也当做已标记
{
printf("该坐标尚未被标记,无法取消标记,请选择其他坐标\n");//已被标记则给出提示信息
}
}
else//坐标非法,则给出提示信息,重新输入
{
printf("坐标非法,请重新输入需要标记的坐标\n");
}
}
}
也是跟标记功能一样的,就不再讲解了
(11)游戏胜负的判断我的思路是雷盘总共有81个坐标,通过设置10个雷放在雷区,我们还需要输入71个坐标,每一次输入坐标,只要该点不是雷,则成功次数加1,只要成功次数达到71次则游戏胜利,而如果途中判断错误,则被雷炸死
代码如下:
void inputdata(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col)//游戏胜负的判断
{
int x = 0;
int y = 0;
int cnt = 0;
cnt = get_nomine(dis);
do
{
printf("请输入坐标:>");//给提示信息
scanf("%d%d", &x, &y);//输入坐标
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)//判断坐标的合法性
{
if (dis[x][y] != '*'&&dis[x][y]==' ')//如果该坐标已被排除
{
printf("该坐标已被排查过,请重新输入坐标:>\n");//则给出提醒信息
}
else
{
if (mine[x][y] != '1')//如果不是雷
{
cnt++;
int num= get_count(mine, x, y);//统计该坐标周围雷的个数
dis[x][y] = num + '0';
if (dis[x][y] != '0')
{
printf("\n");
display(dis, row, col);//更新显示
}
else if (dis[x][y] == '0')
{
dis[x][y] = ' ';
printf("\n");
display(dis, row, col);//更新显示
}
setflag(dis, mine, row, col);//是否选择标记的函数
cancelflag(dis, mine, row, col);//是否选择取消标记的函数
printf("\n");
display(dis, row, col);//更新显示
}
else
{
printf("很不幸,你被炸死了!游戏结束\n");
display(mine, row, col);
break;
}
}
}
else
{
printf("坐标非法,请重新输入\n");
}
} while (cnt < row * col-count);
if (cnt == row * col - count)
{
printf("排雷成功,游戏结束\n");
display(mine, row, col);
}
}
都是一些逻辑上的问题,由于博主的能力不足,写的代码较为冗长,希望读者耐心观看,并给予批评与建议
(12)整合通过上述的函数,我们在这里把它们整合到一块,就可以实现扫雷游戏了,在这里给出主函数的代码部分
#include "game.h"
int main()
{
srand((unsigned int)time(NULL));
char dis[ROWS][COLS] = { 0 };
char mine[ROWS][COLS] = { 0 };
int input = 0;
menu();
printf("请选择输入:>");
scanf("%d", &input);
while (input)
{
switch (input)
{
case 1:
printf(" 扫雷游戏\n");
game(dis, mine, ROW, COL);
break;
case 0:
break;
default:
break;
}
menu();
printf("请选择输入:>");
scanf("%d", &input);
}
return 0;
}
另外附上头文件
#pragma once
#include
#include
#include
#define ROW 9 //设置雷盘的大小的数字,用宏定义,方便以后改动
#define COL 9
#define ROWS ROW+2//设置数组的大小,防止访问越界的问题
#define COLS COL+2
#define count 10
void game(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col);//游戏逻辑函数
void menu();//菜单显示函数
void display(char a[ROWS][COLS], int row, int col);//显示雷盘的函数
int get_count(char a[ROWS][COLS], int row, int col);//统计周围一圈雷的个数
void inputdata(char a[ROWS][COLS], char b[ROWS][COLS], int row, int col);//游戏坐标的输入以及胜负的判断
void inputdata(char a[ROWS][COLS], char b[ROWS][COLS], int row, int col);//游戏胜负的判断
void array_init(char a[ROWS][COLS],char b[ROWS][COLS], int row, int col);//数组初始化函数
void setsymbol(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col);//标记雷的函数
void setmine(char mine[ROWS][COLS]);//雷区设置函数
void setflag(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col);//是否继续标记
void judgearound(char dis[ROWS][COLS], char mine[ROWS][COLS], int x, int y);//生成爆炸式空白的函数
void cancelflag(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col);//取消标记的函数
void cancelsymbol(char dis[ROWS][COLS], char mine[ROWS][COLS], int row, int col);//取消标记雷的函数
四.总结与反思
通过测试,代码实现是没有问题的,实现扫雷的各个逻辑,我们通过开局一片空白,就可以推测下一步我们要走哪个坐标,但是这里还是有个弊端,就是我的开局空白函数是随机的,有时候随机的坐标并不是很好的推测出下一步,这也是博主的能力不足,希望大家能在评论下给点意见让我改进。
这个扫雷游戏很适合初学者去编写,它的难度不是很大,简易版的很容易写,只需要初学熟练掌握函数,数组以及控制结构方面的知识即可编写,博主也希望大家能针对博主的问题给出建议与批评,欢迎大家在讨论区评论!!!祝大家天天开心!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)