前言
基于上一次制作的c语言小游戏——三子棋,这次是一个更难一点的游戏:扫雷。其中运用到了c语言初级阶段几乎所有内容且难度有所提升,让我们一起来研究一下如何用c语言实现扫雷游戏逻辑。
一、扫雷游戏怎么玩?
众所周知,扫雷是在早期的windows系统中就存在的经典解密小游戏,其玩法也是十分有趣,在9*9的方形格子里防置有多个“ 炸弹 ”,鼠标点击某个格子,若此处就是炸弹埋放点,那么游戏就会失败,若不是,则会显示数字,这个数字代表了在离这个格子最近的一圈八个格子内有几个炸弹。
例如:
这个就代表在所选格子周围有两个炸弹。
二、如何用代码实现?
1. 打印游戏界面
由于玩游戏的过程和do while语句执行过程更贴近(直接执行,一轮结束后再来询问是否进行下一轮),所以我们选择do while语句作为游戏循环框架。
int main()
{
int input = 0;
do
{
menu();
printf("请选择:_\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);//0直接跳出,1继续玩。
return 0;
}
运行结果正确:
2.扫雷游戏逻辑
分析:根据经验我们知道扫雷类似于三子棋,也需要类似的 “ 二维数组棋盘 ” ,和三子棋有所不同的是,制作扫雷游戏主要有两个步骤:
1.布置雷——存放雷的棋盘
2.排查雷——排查雷的棋盘
既然是布置雷,那么就需要一个存放雷的棋盘,并且要为布置雷的地方做好标记,如果是用 1 表示有雷 0 表示没有雷,那么当玩游戏时,选中一处刚好它是没有雷的,但是它的周围是有一颗雷的,此时,用 1 作为雷的标记点就出现了第一个问题——这个 1 到底是雷还是周围雷的信息?
解决办法:
再给一个二维数组,一个数组用来存放布置好雷的信息(有雷放入字符 1 ,没有雷,放置字符 * 达到视觉上的遮盖效果);另外一个数组存放排查雷周围的信息(使用字符0-8)。为了保持两数组相同,都选择char型。
此时出现了第二个问题:如果刚好是最边上的不是雷呢?排查雷的信息按照原来的方式会出现【越界访问现象】
解决办法:
为了能统一排查雷的计算方法,应当将“ 棋盘 ”扩大两行两列,例如下图 9*9的数组扩大至11 * 11,这样子,如果访问9 * 9内任意一个元素,都可以利用扩大的数组循环访问得到它周围一圈雷的信息,
3.数组初始化
game.h:
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//数组初始化
InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
game.c:
InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
test.c:
//扫雷游戏框架
void game()
{
char mine[ROWS][COLS];//存放布置雷的信息
char show[ROWS][COLS];//存放周围雷的信息
InitBoard(mine, ROWS, COLS,'0');//全都初始化为‘*’
InitBoard(show, ROWS, COLS,'*');//全都初始化为‘0’
这里用了一个比较明智的写法是要初始化的字符‘0’和‘*’,也当作参数传给
了函数,这种写法的目的是为了在实现初始化的函数中只需要设置一次即可
实现对两个数组的初始化。详见game.c
}
4.打印扫雷棋盘
上面我们提到过为了优化排查边缘雷周围信息的问题,我们将 9 * 9的棋盘扩大至 11 * 11,所以,在这里要注意的是【打印棋盘】只需要打印里面的 9 * 9 ,外面一圈并不需要打印出来给用户看到。但是我们本来就是在 11 * 11 的数组上操作,所以传的参数格式才为:
DisplayBoard(mine, ROW, COL);//只需要打印中间9*9的
DisplayBoard(show, ROW, COL);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//数组仍是11*11所以传参ROWS COLS
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("-----扫雷游戏------\n");
int i = 0;
for (i = 0; i <= col; i++)//打印行号
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ", i);//打印列号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-----扫雷游戏------\n");
}
5.布置雷
SetMine(mine, ROW, COL);//在11*11的棋盘上布置雷,mine传参ROWS COLS
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = 10;//10个雷
while (count)//放10次,count为0时全部布置完,刚好结束while循环
{
//随即下标
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
设置随机数起点:
srand((unsigned int)time(NULL));
6.排查雷
要在mine中排查,排查后的信息放在show中,然后打印出来。
如何排查?
1.输入排查的坐标
2.检查坐标是不是雷(是雷——炸死,游戏结束;不是雷——统计坐标周围有几个雷,并将结果存放到show中的对应坐标上,游戏继续)
//排查雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
printf("请输入要排查雷的坐标:_\n");
int x = 0;
int y = 0;
int num = 0;
while (num<=row*col-10)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//先判断坐标合法性
{
if (mine[x][y] == '1')
{
printf("很遗憾,炸死了\n");
DisplayBoard(mine, ROW, COL);//打印失败棋盘
break;
}
else
{
//不是雷,统计坐标周围有几个雷
int count = get_mine(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);//显示排查出的信息
num++;
}
}
else
printf("坐标不合法,请重新输入\n");
}
if (num == row * col - 10)
{
printf("恭喜你!排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
> 代码重点是:统计非雷处坐标周围雷的信息。
> **※** 假设棋盘中任一坐标为x,y,那么它周围一圈的坐标可以表示为(x-1,y)
> (x-1,y-1) (x-1,y+1)(x,y-1)(x,y+1)(x+1,y)(x+1,y-1)(x+1,y+1)
>
> **※** 而且棋盘内容均为字符,此时mine布置雷的棋盘只有字符0 和字符1,而且
> 我们知道 0 -9对应 ASCII码差值均为 48,所以字符0减去字符0就等于数字0,字符
> 1减去字符0等于数字1,
> **※** 所以,我们只要将八个坐标之和减去8倍的字符0,通过得出的数字即可知道这一周有几个雷
>
> int get_mine(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x - 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y] +
mine[x + 1][y - 1] +
mine[x + 1][y + 1]) - 8 * '0';
}
三、总结
至此,一个能够实现扫雷游戏基本逻辑的小游戏便制作完成了,当然c语言版的扫雷
仍有较大的优化空间,如:用递归算法实现一定条件下展开一片无雷区、标记功能等
,后期再来补充。这次制作最主要的是要学会一个项目从无到有的搭建过程,同时注
意写代码时函数传参问题,除此之外,整个游戏大量使用字符,要注意数字和字符相
互区别。
本次c语言制作扫雷游戏就介绍到这里,如有不足,欢迎批评指正。