#前言
大家好,扫雷这个小游戏想必大家都不陌生,在初高中的信息课上这可是霸屏的小游戏,那么今天我就来带大家有C语言来实现一下这个小游戏。当然,笔者的水平水平有限,目前无法完整的复刻这个经典游戏,所以这次给大家带来的只是扫雷这个游戏的初级版。
#实现思路
首先我们要明确目的,开发一个游戏当然是为了给人玩的,所以让玩家读懂游戏是很重要的。所以在操作之前不要一昧的就开始写游戏的具体实现细节,而应该先实现游戏的菜单界面,先写出大概框架后在将具体实现细节填入对应菜单上的功能。
#关于菜单
一个游戏的菜单需要有什么功能,想必玩过一些游戏的老铁都应该有所了解。首先,我们需要暴露给玩家我们这个游戏的入口与出口,即玩家可以选择进入游戏与退出游戏。由于我们这个游戏比较简单,所以关于菜单也不必太过复杂,一个进入与一个退出即可。代码如下:
void menu()
{
printf("***********************\n");
printf("**** 0.exit 1.play ****\n");
printf("***********************\n");
}
#玩家选择
说完了菜单,我们就需要想想,玩家选择这个菜单上的选项后我们的程序对应要进行怎样的操作。玩家的选择有3种,除去0.exit 与1.play外,玩家还可能输入其他菜单中不存在的数字。此时我们就应该提醒玩家要重新输入选项。当玩家顺利进入游戏并且结束游戏后玩家是可以选择重复玩游戏还是选择0.exit退出游戏。上述就是我们这个程序的答题框架了,说到这了,不知道你有没有什么好的想法来实现这个框架呢?如果没有的话请在想想这几句话,开始时让玩家选择,选择错误后重新选择,并且可以重复选择。怎么样是不是一下就想到了do while循环来实现,开始时的选择与重复选择(因为do while循环是必定会执行一次的),之后的选项可以有if语句或者用switch语句解决,此处我们采用switch语句。具体代码如下:
do {
menu();
printf("请输入你的选择: ");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戏。\n");
break;
case 1:
printf("进入游戏。\n");
game();
break;
default:
printf("选项不存在,请重新输入!!\n");
break;
}
} while (input);
此时请注意观察while的执行条件与我们菜单的功能选项,不知你是否感觉到了我们之前为什么要把0作为退出游戏的选项而1作为进入游戏的选项呢?这是因为当玩家想结束游戏是如果选择0,此时将同时跳出while循环,不再进行选择,这让代码变得简洁。
#游戏相关功能及其实现
1.游戏框架
当游戏开始时,呈现给玩家的就是一个9*9的雷阵,玩家需要在雷阵中选择坐标进行排雷。如果玩家踩到了我们在游戏开始前布置的雷则游戏结束,反之则在雷阵上显示此坐标周围有多少可能存在的雷。那么要实现以上功能我们需要用那些函数来完成呢?首先,我们需要二维数组来构建雷阵,其次就是完成雷的布置与玩家选择坐标时的判定。那么具体函数框架如下:
char board1[ROWS][COLS] = { 0 };
char board2[ROWS][COLS] = { 0 };
Init_Board(board1, ROWS, COLS,'0');
Init_Board(board2, ROWS, COLS,'*');
Set_Mine(board1, ROWS, COLS);
Display_Board(board1, ROW, COL);
Display_Board(board2, ROW, COL);
Player_Move(board1, board2, ROW, COL);
2.雷阵的初始化
看过游戏框架的同学可能会有疑问,我们玩扫雷时屏幕上不是只有一个雷阵吗?为什么要创建两个二维数组?其实创建两个二维数组是很有必要的,因为我们需要在雷阵中进行布雷,如果只有一个数组那么我们初始化后在进行布雷这样就会覆盖之前的初始化符号,这样显然不好。所以我们就创建两个数组,一个用于布雷且判断输赢一个用于给玩家排雷。看到这里也许你还会有疑惑,那为什么又要有两种行与列呢?是的,在图中框架上我们定义了俩种行与列,其中ROWS与COLS均为11,ROW与COL均为9。为什么怎么做呢,主要是为了判定输赢时的便利,这个我们之后在谈。说了这么多,那么雷阵的初始化也很简单,代码如下:
void Init_Board(char board[][COLS], int row, int col, char c)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = c;
}
}
}
2.布雷
布雷,即我们需要在以初始化好的雷阵中进行随机布10个雷,如上文所诉,我们布雷的雷阵是不展示给玩家看的borad1并且用字符0进行初始化,这点很重要请牢记。由于我们的数组是11行11列的,但是我们只展示给玩家9*9的雷阵,所以布雷时要注意随机数的值。同时还要注意如果如果布雷的位置先前已被布置好,那么就需要重新生成随机数直到选到没有雷的坐标。具体代码如下:
void Set_Mine(char board[][COLS], int row, int col)
{
int count = 0;
while (count < 10)
{
int x = rand_x;//值为1-9
int y = rand_y;//值为1-9
if (board[x][y] != '1')
{
board[x][y] = '1';
count++;
}
}
}
3.展示雷阵
在布好雷阵后,我们需要在屏幕上展示另一个用“*”初始化的数组board2。因为,这个雷阵中并没有任何雷的信息,倘若将布雷矩阵展示给玩家看那么这个游戏也就失去了意义。同样的,矩阵为11*11但我们只需要展示9*9的矩阵即可,同时在雷阵的左侧和上端打印对应的序列,方便玩家选择坐标。具体代码如下:
void Display_Board(char board[][COLS], int row, int col)
{
for (int i = 0; i <= row; i++)
printf("%d ", i);
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i );
for (int j = 1; j <= col; j++)
printf("%c ", board[i][j]);
printf("\n");
}
}
具体效果如上图。
4.玩家排雷与胜负判定
在雷阵的布置与展示都完成后,玩家就可以选择排雷了。因为我们初始化的数组为11*11但是我们只是打印了9*9,所以此时玩家输入的1 1就是代表所展示矩阵的第一行第一列而非第二行第二列。同时在玩家选择坐标时如果踩到雷即与我们在board1中布置的雷的坐标相同那么即判定玩家失败。反之,我们则需要判定该坐标周围共有多少雷,之前叫大家牢记的知识点就派上用场了。因为我们是用0与1来初始化数组的,所以我们只需要将该坐标周围的数字相加即可得到雷数。那么问题又来了,我们是用字符初始化的该怎么返回整形呢?很简单,根据ASCII码表,我们只需要将一个字符减去’0‘就可以得到它对应的整形了。具体代码如下:
int Numebers_Mine(char board1[][COLS],int x, int y)
{
return (board1[x - 1][y - 1] + board1[x - 1][y] + board1[x - 1][y + 1]
+ board1[x][y - 1] + board1[x][y + 1] +
board1[x + 1][y - 1] + board1[x + 1][y] + board1[x + 1][y + 1] - 8 * '0');
}
因为周围有8个坐标所以减去8*‘0’,同时注意该函数不必在头文件中声明,只需在游戏实现的.c文件中实现即可,因为它只需要用来判定雷的数目并不需要暴露。而后我们将返回的整型数值在加上‘0’将其赋给board2上玩家选择的坐标即可。那么现在只需解决玩家胜利的条件即可完成这个小游戏了,由于雷阵中有10个雷,玩家每排一次雷就需要输入一次坐标(因为是初级版所以不具备自动清除一部分非雷坐标的功能),所以我们只需设定一个变量来进行计数即可,注意在将变量定义为static,当count为71时即玩家胜利。最后为了保证玩家输入的坐标为合法坐标我们最好嵌套一个循环来保证玩家输入坐标的合法性,具体代码如下:
int Player_Move(char board1[][COLS],char board2[][COLS], int row, int col)
{
int x;
int y;
static int count = 0;
while (1)
{
printf("请玩家选择坐标:");
scanf("%d %d", &x, &y);
if (x <= row && x > 0 && y > 0 && y <= col)
{
if (board1[x][y] == '1')
{
printf("踩到地雷游戏失败!。\n");
Display_Board(board1, ROW, COL);
return 0;
}
else
{
board2[x][y] = Numebers_Mine(board1, x, y) + '0';
count++;
break;
}
}
else
printf("坐标不存在,请重新输入!!\n");
}
if (count == 71)
{
printf("游戏胜利!\n");
return 1;
}
return -1;
}
至于函数中的返回值将由其在另一个.c文件中接受并通过其返回值来判断是否继续进行游戏,具体代码如下:
void game()
{
char board1[ROWS][COLS] = { 0 };
char board2[ROWS][COLS] = { 0 };
Init_Board(board1, ROWS, COLS,'0');
Init_Board(board2, ROWS, COLS,'*');
Set_Mine(board1, ROWS, COLS);
//Display_Board(board1, ROW, COL);
while (1)
{
Display_Board(board2, ROW, COL);
ret = Player_Move(board1, board2, ROW, COL);
if (ret == 0 || ret == 1) //ret为0则代表玩家失败,为1则代表胜利
break;
}
}
#写在最后
希望小伙伴们给个赞呀,具体代码在:https://gitee.com/ye-yukun/test.git