目录
一.前言
二.游戏主体框架
三.游戏内部实现
1.给出一个ROW*COL的棋盘
2.初始化两棋盘
3.随机生成雷
4.反馈信息
5.排雷过程
6.优化部分
四.结语
这里是趣味小游戏第二篇,扫雷想必也是大家以前电脑上必备的休闲游戏,那么今天我就来带大家用代码的方式来回忆并实现它吧!
要实现游戏我们首先要搭建出一个主体游戏框架。
#include
void menu()
{
printf("********************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("********************\n");
}
void test()
{
int input = 0;
do
{
menu();//给出一个游戏的开始菜单
printf("欢迎来到扫雷游戏\n");
printf("输入1开始游戏,输入0退出游戏\n");
printf("请输入:");
scanf("%d", &input);
switch (input)//对于不同输入值给出不同结果
{
case 1:
game();//由此进入游戏
break;
case 0:
printf("游戏已退出\n");//由此退出游戏
break;
default:
printf("输入错误,请重新输入:");//输入其他值时给出反馈,增强代码健壮性
break;
}
} while (input);//输入为真(1)时则进入do里的循环输入为假(0)时即退出循环
}
int main()
{
test();//主体内容
return 0;
}
在搭建好主体后,我们就进入游戏实现阶段。
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void game()
{
//1.给出一个ROW*COL的棋盘
char mine[ROWS][COLS];
char show[ROWS][COLS];
//2.初始化两棋盘
IniBoard(mine, ROWS, COLS, '0');
IniBoard(show, ROWS, COLS, '*');
//3.随机生成雷
SetMine(mine, ROW, COL);
//4.反馈信息
//PrintBoard(mine, ROW, COL);
PrintBoard(show, ROW, COL);
//5.排雷过程
FindMine(mine, show, ROW, COL);
}
我们在这里假定ROW=COL=9,那么目标就是产生这样一个9*9的棋盘。
而在我们统计一个格子周围雷的个数时,会出现一些格子统计雷的数量比较困难(如9 9坐标),因此我们将其行和列拓宽两行得到如下棋盘
因此我们便创建两个11*11的二维数组,一个用来作为生成雷的数组(mine),一个用来作为玩家看与猜测的数组(show)。
在成功生成两棋盘后,我们对其分别进行初始化(即将所有的元素化成同一元素)。
在此,我们将mine数组初始化为'0'(后面会解释),将show数组初始化为'*'(方便玩家观察该数组)。因此,我们编写一个函数来实现此目的。
void IniBoard(char board[ROWS][COLS], int row, int col, char n)
{
int i = 0;
for (i = 0; i < row; i++)//得到每行数据
{
int j = 0;
for (j = 0; j < col; j++)//得到每列数据
{
board[i][j] = n;//将每个元素都转变成字符n
}
}
}
在初始化两数组后,我们便开始在mine数组中随机布置雷。
(注意:可供我们操作的范围为1 1——9 9的范围内,因此雷的生成位置也应该处于1 1——9 9之间)
#define EASY 15
//简单模式的雷的个数
void SetMine(char mine[ROWS][ROWS], int row, int col)
{
srand((unsigned int)time(NULL));//用时间戳来创造伪随机数
int count = EASY;
while(count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;//因为行数与列数确定,因此需要保证x,y都处于[1,9]这个区间
if (mine[x][y] == '0')//判断该位置是否已经被布置了雷
{
mine[x][y] = '1';
count--;//布置成功则使需要布置的雷的个数减少一个
}
}
}
在游戏进行的过程中,我们作为玩家需要不断地知道自己输入坐标后的结果怎么样,所以便离不开显示棋盘这一操作,因此,我们便设计一个函数来进行这个操作。
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷游戏--------\n");//上分割行
for (j = 0; 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 ", board[i][j]);//打印每个元素
}
printf("\n");
}
printf("---------扫雷游戏--------\n");//下分割行
}
我们用它来打印show数组,得到如下的效果
在看到棋盘后,想必大家已经手痒痒了吧,别急,让我来带各位进入正戏环节。
还是老样子,我们编写一个函数来实现这一步骤。
void Change(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int i = 1;
for (i = 1; i <= row; i++)
{
int j = 1;
for (j = 1; j <= col; j++)
{
if (mine[i][j] == '1')
{
show[i][j] = '#';//将mine棋盘中的雷表现为show数组中的#
}
}
}
}
int MineCount(char board[ROWS][COLS], int x, int y)
{
return board[x-1][y-1] + board[x-1][y] + board[x-1][y+1] + board[x][y-1]
+ board[x][y+1] + board[x+1][y-1] + board[x+1][y] + board[x+1][y+1] -8*'0';//根据ASCII码表将所有1的个数相加并返回
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
printf("输入排查位置(如5 5)\n");
int count = ROW * COL - EASY;//用count代表场上除去雷的剩余位置
while(1)//无法具体确定判断条件,所以用1作为条件,后面完成行为后用break跳出
{
int x = 0;
int y = 0;
printf("请输入你想排查的坐标\n");
printf("请输入:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的位置是否在棋盘内,如果不在内就反馈给玩家
{
if (mine[x][y] == '1')
{
printf("很遗憾,你碰到了炸弹,游戏结束\n");
Change(show, mine, row, col);//在判定玩家已输的情况下,将情况反映到show数组上
PrintBoard(show, row, col);//让玩家知道自己如何输的
break;
}
else if (show[x][y] != '*')//如果玩家手误,可以借此重新输入
{
printf("该位置已被排查过,请重新输入\n");
}
else
{
int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
PrintBoard(show, row, col);//将结果反馈给玩家
count--;
}
}
else
{
printf("输入坐标非法,请重新输入\n");
}
if (count == 0)//当除雷以外的剩余个数为0时,那么棋盘上只剩下雷,即玩家获胜
{
printf("恭喜你,排雷成功\n");
PrintBoard(mine, row, col);//将最后结果反馈给玩家
break;
}
}
}
在经过了上述流程后,虽然我们体验到了扫雷游戏,但是总感觉玩起来不是很方便,此时我们的心头便会冒出一些想法:可不可以在周围有0个雷的时候直接展开一片?在排查完雷之后可不可以自己标记雷的位置?在标记后可不可以显示自己主观认为的剩余的雷的个数?
答案是:当然!
我们先对展开方面进行优化
我们在反馈前先构建一个Cls函数进行对0的清除与展开,即改为如下这样
else
{
int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
Cls(mine, show, row, col, x, y);
PrintBoard(show, row, col);//将结果反馈给玩家
count--;
}
下面具体实现Cls函数
void Cls(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
if (show[x][y] == '0')//如果此元素已经为'0'才进入此函数
{
show[x][y] = ' ';//将其修改为' '便于观查
int i = x - 1;
for (; i <= x + 1; i++)
{
int j = y - 1;
for (; j <= y + 1; j++)//得到该元素的从左上到右下的全部下标
{
if (show[i][j] == ' ')//如果已经有元素被替换为' ',则无需再次进行下面的操作
{
continue;
}
if (i >= 1 && i <= row && j >= 1 && j <= col)//判断该元素是否在棋盘内
{
if (show[i][j] != '*')//如果该元素不是'*',即已经被赋值了数字就无需进行下面的操作
{
continue;
}
int ret = MineCount(mine, i, j);//对该元素进行判定
show[i][j] = '0' + ret;
Cls(mine, show, row, col, i, j);//不断迭代,直到不再出现0或数字为止
}
}
}
}
}
成功展开后,我们再来对标记进行操作与优化
else
{
int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
Cls(mine, show, row, col, x, y);
PlayerJudge(show, row, col);
PrintBoard(show, row, col);//将结果反馈给玩家
count--;
}
同样的我们编写一个PlayerJduge函数来实现此操作
void Judge(char show[ROWS][COLS], int row, int col)
{
printf("坐标操作:\n");
printf("1 - 不确定是否为雷\n");
printf("2 - 确定是雷\n");//根据ASCII码表将1和2转换成对应的?与#
printf("3 - 取消标记\n");
printf("请输入你想标记的坐标(如5 5) :\n");
int x = 0;
int y = 0;
while(1)
{
printf("请输入:");
scanf("%d %d", &x, &y);
printf("请输入你想对该坐标进行的操作\n");
int c = 0;
scanf("%d", &c);
if (show[x][y] == '*')
{
if (c == 1)
{
c = 63;//将1转换成?所对应的ASCII值
}
if (c == 2)
{
c = 35;//将2转换成#所对应的ASCII值
}
char ch = c;
show[x][y] = ch;
printf("标记成功!\n");//给出反馈
PrintBoard(show, row, col);
break;//结束本次标记
}
else if (show[x][y] == '?' || show[x][y] == '#' && c == 3)//如果输入3还要判断该元素是否已经被标记
{
show[x][y] = '*';
printf("取消成功!\n");//给出反馈
PrintBoard(show, row, col);
break;
}
else
{
printf("输入位置不合法,请重新输入\n");//输入不合理
}
}
}
void PlayerJudge(char show[ROWS][COLS], int row, int col)
{
int m = 0;
printf("请问你是否想标记某位置\n");
printf("是:1 否:0\n");
do
{
printf("请输入(如已标记完毕请输入0,想继续标记请输入1):");//在进行完一次标记后,玩家需要确定是否进行下一次标记
scanf("%d", &m);
switch (m)
{
case 1:
Judge(show, row, col);//具体实现标记操作
break;
case 0:
break;
default:
printf("输入有误,请重新输入:\n");
break;
}
} while (m);
}
最后呢,我们再对雷量的剩余做一个LeaveMine函数来反馈
else
{
int ret = MineCount(mine, x, y);//将该位置周围的雷的数量做一个统计
show[x][y] = '0' + ret;//将该数字通过ASCII码表还原成对应字符
Cls(mine, show, row, col, x, y);
PlayerJudge(show, row, col);
int res = LeaveMine(show, row, col);//show数组中#的个数
printf("剩余雷量:%d\n", EASY - res);//反馈剩余的雷的个数
PrintBoard(show, row, col);//将结果反馈给玩家
count--;
}
下面是具体的实现办法
int LeaveMine(char show[ROWS][COLS], int row, int col)
{
int i = 1;
int num = 0;
for (i = 1; i <= row; i++)
{
int j = 1;
for (j = 1; j <= col; j++)
{
if (show[i][j] == '#')//统计所有元素中#的个数
{
num++;
}
}
}
return num;//返回#的个数
}
至此,我们便完成了扫雷游戏的优化!
以上就是扫雷游戏的全部实现过程了,希望各位程序员能够玩得开心,同时也希望能从本文中得到灵感与启发!路过的点个赞吧,谢谢!