扫雷游戏的实现
前言:相信大家小时候都应该玩过扫雷游戏,既然我们学习了c语言,那么我们也可以用现在所学的编程知识来自己写一份简化版的扫雷游戏
(一)合理规划
扫雷游戏不像我们之前练习的代码,只有短短几行,想要将它完整写下来会有上百行,所以如果将这上百行代码都放在一个文件里会显得非常拥挤,也不方便我们编写,所以我们可以将这上百行代码分开。我们可以将它们分为3个部分
(1)新建一个game.h头文件:
我们可以将这段代码中所需包含的头文件放在里面,比如我们会用到随机值的生成,用来随机布置地雷,还有最基本的输入输出的头文件,这时就需要以下头文件
#include <stdio.h> #include <time.h> #include <stdlib.h>(2)新建一个test.c的源文件:
我们将游戏运行逻辑的代码和主函数放在里面,在这里面我们要编写游戏运行的框架,比如说首先要将整个程序运行的主体写下来,就像上篇写的猜数字游戏,让用户选择是否开始游戏,里面要用到switch语句来实现分支语句,要包括菜单函数menu()和游戏函数game(),这时主函数的代码就如下所示
int main() { int num = 0; do { menu(); printf("请选择>="); scanf("%d", &num); switch (num) { case 1: game(); break; case 0: printf("游戏结束"); break; default: printf("输入错误,请重新输入"); break; } } while (1); return 0; }接下来先编写menu()函数,如下所示
void menu() { printf("********************\n"); printf("***** 1.star *****\n"); printf("***** 0.over *****\n"); printf("********************\n"); }然后我们要完成game()函数:
前提:我们要知道扫雷游戏的玩法,比如说简单模式,给我们一个9×9的棋盘和10个雷,我们选择任意一个位置,如果不是雷的话,它会自己向外扩散,并且显示周围有多少个雷,如下所示
了解扫雷游戏的规则,我们就可以开始一步步编写game()函数了
1.扫雷的棋盘数组的创建:
首先我们需要将扫雷的棋盘打印出来呈现给玩家,如网页版的游戏所示
这边我们选择9×9的格子和10个雷。为了后续方便,我们可以在game.h中定义常量雷的值,我们用Easy来表示简单模式下雷的个数,game.h补充如下所示
#include <stdio.h> #include <time.h> #include <stdlib.h> #define Easy 10我们可以用数组来创建棋盘,首先,因为最后呈现给玩家的是9×9的格子,所以很多人会想到创建9×9的数组,但我在前提中提到,当你选择任意一个位置后,它会统计周围一圈雷的个数,如果我们只创建9×9的数组,那么当我们选择的位置在边缘时,因为周围是空的,所以可能会出现错误,所以这时我们就可以创建11×11的数组,这样在选择边缘位置的时候就不会出现错误。我们要创建两个数组,第一个是准备用来存放雷的数组,第二个是呈现给玩家的数组,它会把格子里的内容给遮盖住,从而让玩家来排查雷的位置,如果用我们之前的方法可能会创建如下数组
char mine[11][11];//存放雷 char show[11][11];//展示给玩家但这里我们要多次涉及到行和列,并且在编写布置雷的函数时,我们是要在9×9的格子中完成的,最外面一圈不布置任何雷,所以为了方便改写行和列,我们可以在这里用到定义常量的知识,在game.h中创建常量,最后game.h补充如下
#include <stdio.h> #include <time.h> #include <stdlib.h> #define Easy 10 #define Lie 9 #define Hang 9 #define Lies Lie+2 //Lies为11 #define Hangs Hang+2 //Hangs为11创建完成之后,我们就可以将上述数组的创建改为如下所示
char mine[Hangs][Lies]; char show[Hangs][Lies];2.棋盘的初始化:
创建完成后我们要将数组里面的内容全部初始化,我们先将准备存放雷的数组的内容全部初始化一个值,因为现在还没有往里面存放雷,所以就让它全部初始化为字符‘0’,‘0’代表这个位置不是雷,而后面我们就可以将雷的位置设置为字符‘1’。然后将呈现给玩家的表格全部初始化为字符‘*’,之前我们自己学过如何怎么将二维数组的内容全部打印出来,这时我们就可以利用for循环,将‘0’和‘*’分别赋值给两个数组,如下所示
//初始化棋盘; InitBoard(mine,Hangs,Lies,'0'); InitBoard(show,Hangs,Lies,'*');void InitBoard(char Board[Hangs][Lies], int hangs, int lies, char set) { int i = 0; for (i = 0; i < hangs; i++) { int j = 0; for (j = 0; j < lies; j++) { Board[i][j] = set; } } }打印出来我们可以看到效果如下
但是这样看太简单了,而且也不方便知道每个位置的具体的坐标,所以我们可以顺带将行号和列号一并打印出来,如下图所示
void DisplayBoard(char Board[Hangs][Lies], int hang, int lie) { int j = 0; for (int i = 0; i <= lie; i++) { printf("%d ", i); } printf("\n"); for (int i = 1; i <= hang; i++) { printf("%d ", i); for (j = 1; j <= lie; j++) { printf("%c ", Board[i][j]); } printf("\n"); } }最后呈现的效果就如下图所示
3.布置雷
完成了最基本的内容之后,我们就要开始布置雷了,10个雷的位置是随机的,这时候就要涉及到随机数的创建,我们这时可以在主函数中补充上随机数的生成,最后主函数补充如下
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("游戏结束"); break; default: printf("输入错误,请重新输入"); break; } } while (input); return 0; }这时在game()函数中直接用就可以了。那怎么布置雷呢,我们可以随机创建两个变量x,y,让他们代表随机行号和列号,然后将雷的信息,也就是让字符‘1’赋值给这个位置,随机赋值10次,将雷全部布置完,然后结束循环。
注意⚠️:这里要注意,每次随机选取位置赋值时可能会位置重复,这时我们可以用if语句来判断位置是否重复。这时布置雷的函数具体如下
void Setmime(char Board[Hangs][Lies], int hang, int lie) { int i = 0; int count = Easy; while(count) { int x = rand() % 9 + 1;//随机生成1-9行 int y = rand() % 9 + 1;//随机生成1-9列 if (Board[x][y] == '0') { Board[x][y] = '1'; count--; } } }4.排查雷
布置好雷之后,我们就要开始排查雷的位置,也就是需要玩家做的事情。玩家需要输入需要排查位置的坐标,如果那个位置不是雷的话,要显示那个位置周围一圈有多少雷,然后再继续排查,循环往复,直到雷被排查完,如果刚好踩到雷的话就要给结束游戏,并且将存放雷的数组给玩家看。具体排雷函数如下所示
void Findmine(char mine[Hangs][Lies],char show[Hangs][Lies], int hang, int lie) { int x = 0; int y = 0; int win = 0; while (win<Hang*Lie-Easy) { pirntf("请输入要排查的坐标:"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (show[x][y] != '*') { printf("该坐标已被排查,请重新输入\n"); } if (mine[x][y] == '1') { printf("很遗憾,你被炸死了"); DisplayBoard(mine, Hang, Lie); break; } else { int num = Getminecount(mine, x, y); show[x][y] = num + '0'; DisplayBoard(show, Hang, Lie); win++; } } else { printf("坐标非法,请重新输入"); } } if (win == hang * lie - Easy) { printf("恭喜你,排雷成功"); DisplayBoard(mine, Hang, lie); } }我们可以发现在排雷函数中还有一个寻找周围雷个数的函数Getmintmine(),我们要先把寻雷函数完成。如果你选的位置坐标是x,y,那么这个位置周围一圈的坐标就分别为以下所示
mine[x-1][y-1] mine[x][y-1] mine[x+1][y-1] mine[x-1][y] mine[x+1][y] mine[x-1][y+1] mine[x][y+1] mine[x][y+1]那怎么返回雷个数的数值呢,其实很简单,我们之前用字符‘1’来表示雷,而字符‘0’表示无雷,而观察下方ASCLL码表可知
字符‘1’为49,字符‘0’是48,所以只要分别将周围一圈位置数组的值减去字符‘0’就可以得到所选位置周围雷的个数,具体代码如下
int Getminecount(char mine[Hangs][Lies], int x, int y) { return (mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x - 1][y] + mine[x + 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x][y + 1]-8*'0'); }这时,我们扫雷游戏的代码已经基本完成了,接下来让我们将代码完整写出来
1.game.h部分
#include <stdio.h> #include <time.h> #include <stdlib.h> #define Lie 9 #define Hang 9 #define Lies Lie+2 #define Hangs Hang+2 #define Easy 10 void InitBoard(char Board[Hangs][Lies], int hangs, int lies, char set); void DisplayBoard(char show[Hangs][Lies], int hang, int lie); void Setmine(char Board[Hangs][Lies], int hang, int lie); void Findmine(char mine[Hangs][Lies], char show[Hangs][Lies], int hang, int lie); int Getminecount(char mine[Hangs][Lies], int x, int y);2.test.c部分
#include "game.h" void game() { char mine[Hangs][Lies]; char show[Hangs][Lies]; //初始化棋盘; InitBoard(mine,Hangs,Lies,'0'); InitBoard(show,Hangs,Lies,'*'); //打印棋盘; DisplayBoard(show, Hang, Lie); //布置雷; Setmine(mine, Hang, Lie); //排查雷; Findmine(mine,show,Hang,Lie); } void menu() { printf("********************\n"); printf("***** 1.star *****\n"); printf("***** 0.over *****\n"); printf("********************\n"); } 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("游戏结束"); break; default: printf("输入错误,请重新输入"); break; } } while (input); return 0; }3.game.c部分
#include "game.h" void InitBoard(char Board[Hangs][Lies], int hangs, int lies, char set) { int i = 0; for (i = 0; i < hangs; i++) { int j = 0; for (j = 0; j < lies; j++) { Board[i][j] = set; } } } void DisplayBoard(char Board[Hangs][Lies], int hang, int lie) { int j = 0; for (int i = 0; i <= lie; i++) { printf("%d ", i); } printf("\n"); for (int i = 1; i <= hang; i++) { printf("%d ", i); for (j = 1; j <= lie; j++) { printf("%c ", Board[i][j]); } printf("\n"); } } void Setmine(char Board[Hangs][Lies], int hang, int lie) { int count = Easy; while(count) { int x = rand() % 9 + 1; int y = rand() % 9 + 1; if (Board[x][y] == '0') { Board[x][y] = '1'; count--; } } } int Getminecount(char mine[Hangs][Lies], int x, int y) { return (mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x - 1][y] + mine[x + 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x][y + 1]-8*'0'); } void Findmine(char mine[Hangs][Lies],char show[Hangs][Lies], int hang, int lie) { int x = 0; int y = 0; int win = 0; while (win<Hang*Lie-Easy) { printf("请输入要排查的坐标:"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (show[x][y] != '*') { printf("该坐标已被排查,请重新输入\n"); } if (mine[x][y] == '1') { printf("很遗憾,你被炸死了"); DisplayBoard(mine, Hang, Lie); break; } else { int num = Getminecount(mine, x, y); show[x][y] = num + '0'; DisplayBoard(show, Hang, Lie); win++; } } else { printf("坐标非法,请重新输入"); } } if (win == hang * lie - Easy) { printf("恭喜你,排雷成功"); DisplayBoard(mine, Hang, lie); } }