今天又学习到一个有趣的东西,用C语言实现扫雷游戏,下面就让小编带领大家一步一步入手吧。
(1).首先给定一个雷盘
(2).在雷盘上任意点击一个格子,若点到雷,则游戏结束,若不是雷则继续选择
(3).注意棋盘上面的数字,如下:
表示以此位置为中心,周围八个格子中有两个雷
(4).还有一个规则:如果所选择的该位置周围的格子的周围的八个格子中没有雷,则会自动显示,以此类推,直到遇到周围八个格子中至少有一个雷则停止显示。
(5).当雷盘上面只有雷未显示时,游戏胜利。
(1).对于雷盘,很容想到用一个二维数组来表示,(这里我们以9*9表示),并且我们看到没点击一个位置,就会有相应雷盘显示,所以我们想到用两个二维字符数组。
数组一mine:用于布置雷的信息。
数组二show:用于显示排查出的雷的信息。
(2).棋盘设置好后就该存放雷,因为位置随机,所以采用随机数的知识。
(3).排查雷:由玩家输入数组坐标,然后进行判断,直到游戏结束。
1.此次我们依旧采用多文件的方式进行实现,如下:
(1).main函数内容放入文件test.c,并且考虑到玩游戏可以重复进行,所以采用do while()循环套用switch case语句作为主体结构,如下:
#include"game.h"
void menu()
{
printf("***************************\n");
printf("******* 1.play *******\n");
printf("******* 2.exit *******\n");
printf("***************************\n");
}
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印棋盘
//Display(mine, ROW, COL);
Display(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//Display(mine, ROW, COL);
//Display(show, ROW, COL);
//排查雷
FineMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
(2).各函数的声明放入文件game.h,考虑到各.c文件中引用此.h文件,所以对于一些库函数,define定义的常量,我们可以放入此game.h文件,这样就不用重复使用了,如下:
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#define Mine_count 10
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9
void InitBoard(char board[ROWS][COLS], int row, int col, char ret);//初始化
void Display(char board[ROWS][COLS], int row, int col);//打印
void SetMine(char board[ROWS][COLS], int row, int col);//布置雷
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int GetMineCount(char mine[ROWS][COLS],int x, int y);//返回坐标(x,y)周围的雷数
(3).各函数的定义,我们放入game.c文件,如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)//初始化
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ret;
}
}
}
void Display(char board[ROWS][COLS], int row, int col)//打印
{
int i = 0;
printf("------扫雷游戏------\n");
for (i = 0; i < col + 1; i++)//列号
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i < row+1; i++)
{
printf("%d ", i);//行号
int j = 0;
for (j = 1; j < col+1; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)//布置雷
{
int count = Mine_count;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查
{
int x = 0;
int y = 0;
int count1 = 0;
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x > 0 && x < row + 1 && y>0 && y < col + 1)
{
if (mine[x][y] != '1')
{
int count = GetMineCount(mine,x,y);
show[x][y] = count+'0';
Display(show, ROW, COL);
count1++;
}
else
{
printf("很遗憾,你被炸死了!\n");
system("pause");
system("cls");
break;
}
}
else
{
printf("输入错误,请重新输入\n");
}
if (count1 >= row * col - Mine_count)
{
printf("恭喜你,游戏胜利!\n");
Display(show, ROW, COL);
system("pause");
system("cls");
break;
}
}
}
int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回坐标(x,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';
}
2.由上,我们定义了两个二维字符数组,所以接下来我们要考虑初始化的问题。
(1).数组一mine:用来存放雷的信息。
我们想到此数组中用‘0’表示非雷,‘1’表示雷,(这样的做的好处将在之后介绍),所以刚开始mine数组初始化为‘0’。
(2).数组二show:用来存放排查雷的信息。
我们想到刚开始初始化为‘*’,作用就是单纯为了美观与显眼,并在玩家选择位置之后显示相应的雷的信息。
3.值得注意的是:
对于以上这种情况,处于边缘的位置的格子,只需要考虑雷盘以内的地方,而盘外的地方是没有雷的,所以我们对于9*9的雷盘,我们想到将其长宽扩大一格,即用一个11*11的数组来表示,而多出来的格子默认为非雷,即初始化为‘0’ / ‘*’,并且在显示雷盘时,只显示中间9*9的区域。这样做的目的是为了后续方便计算边缘的位置周围的雷的个数。
4.因为此处我们再次会应用到数组的行和列,所以再次用define宏定义行列,一个9*9,一个11*11,以及雷的个数,方便分开使用,如下:
#define Mine_count 10
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9
5.开始初始化雷盘,这里有个小细节,mine和show数组是同行同列的,所以两者只需要使用同一个初始化函数,将自己的内容也作为一个参数,即可分开初始化,即创建一个这样的函数(void InitBoard(char board[ROWS][COLS], int row, int col, char ret)),对于二维数组的初始化,我们很容易想到双重for循环,代码实现如下:
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)//初始化
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ret;
}
}
}
6.雷盘初始化后,我们还需要打印雷盘(二维数组的打印也是用到双重for循环),由上我们说明到mine和show数组的作用,所以我们在选择位置时所看到的的应该是show函数,并且为了美观与方便,我们可以将行列号也打印出来,代码实现如下:
void Display(char board[ROWS][COLS], int row, int col)//打印
{
int i = 0;
printf("------扫雷游戏------\n");
for (i = 0; i < col + 1; i++)//列号
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i < row+1; i++)
{
printf("%d ", i);//行号
int j = 0;
for (j = 1; j < col+1; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
效果如图:
7.雷盘打印实现后我们就开始布置雷,创建SetMine函数实现。据上,此时我们操作的是mine数组
(1).因为雷的位置不定,所以我们使用到随机数,随机产生一些坐标布置雷,并且雷的个数为自定义的
(2).坐标产生后还应该判断一下此处是否已经布置过雷了,若布置过了,则重新生成坐标,否则将此处的‘0’换成‘1’,以表示布置雷,成功布置一次,计数器减1,用于最后出控制循环
代码实现如下:
void SetMine(char board[ROWS][COLS], int row, int col)//布置雷
{
int count = Mine_count;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
8.雷布置好后,玩家就可以选择坐标了,所以创建FineMIne函数用于实现,并且操作的是show函数用于展示雷的信息。
(1).首先是玩家输入坐标(x,y)
(2).然后我们应该判断该坐标合不合法
1).因为我们要操作的是数组中间的9*9的区域,所以若x>0且x 2).当坐标合法后,我们就该判断该坐标处是否是雷,而判断是否是雷就应该在mine数组里面判断,此处为'1'则为雷,游戏结束;此处'0’,则玩家继续输入。 3).当不是雷时,我们应该显示雷盘信息,即此位置有多少个雷,(操作的是show数组),这时前面提到的用‘0’ / ‘1’的好处就来了,如下图: 所以这就是运用'1' / '0'的好处所在,所以现在我们只需要想办法求(x,y)周围的八个数之和。 因此我们创建GetMineCount函数来求和,实参:(mine,x,y),如下: 很显然我们很容发现(x,y)与其周围八个坐标的关系: 所以我们只需返回这八个数之和,这里有个小细节,因为'1' / '0'都是字符,所以我们应该转化为数字进行求和: 一般情况数字与字符数字之间的转化公式为 : 数字+‘0’=字符数字,所以代码实现如下: 细心的小伙伴应该发现,以此坐标进行展开功能还没有实现,下面给出几点注意事项: (1).该坐标不是雷 (2).该坐标周围八个位置没有雷 (3).该坐标没有被排查过 满足以上三点即可展开,以加快游戏进度 ,这里涉及到函数的递归等相关知识,小编不多以叙述,感兴趣的小伙伴可以自行思考。 此次知识到此就结束了,拜拜!int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回坐标(x,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';
}
(四)、以上操作效果如下:
结尾: