我们前面已经学习了c语言的函数与数组,那么我们能不能用我们所学的知识复原童年游戏呢?当然可以!今天我将带大家一起回味童年,解构扫雷。
我们首先整理一下我们的思路,我们需要制作一个可以一直玩的游戏框架,这就与我们前期制作的猜数字游戏一个原理,可以使用do...while循环,再打印一个菜单,输入相应数字进行开始或结束游戏。
int main() {
int a;
do {
menu();//菜单
scanf("%d",&a);
switch (a) {
case 1:
game();//游戏设计
case 0:
printf("游戏结束\n");
break;
default :
printf("输入有误,请重新输入:\n");
}
} while (a);
}
我们在做一个项目时有时候多人协作,代码都写在一个文件上很降低我们的效率,这时候我们可以将项目分在不同的文件。在扫雷项目中我将分为三个文件,分别是头文件game.h用于存放需要的各种头文件,源文件game.c用于存放游戏设计,test.c用于放我们的主函数以及测试。在源文件中我们要引用我们自定义的头文件game.h
下面我们来学习游戏设计
首先我们要制作我们的扫雷棋盘,这里我们可以用二维数组实现,同时将里面统一初始化;但是我们需要存放的雷不被玩家看到,这时候我们就需要制作一个让玩家看到的棋盘,所以这里我们要制作两个数组棋盘,我们命名为mine和show数组。
char mine[ROWS][COLS];
char show[ROWS][COLS];
我们再做一个函数,任务是初始化数组
void InitBoard(char arr[ROWS][COLS], int r, int c, char set) {
for (int i = 0;i < r;i++) {
for (int j = 0;j < c;j++) {
arr[i][j] = set;
}
}
}
//ROWS和COLS代表着行和列,我们可以在头文件中定义
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//在我们的数组中需要9*9的,但是我们如果要进行排雷时边缘会没有格子,就要特殊计算,为降低复杂度,我们干脆创建一个11*11的,只要保证雷不在四周就可以
//我们在mine数组中存放雷的信息,在show数组中显示给玩家看,所以两个数组的初始化是不同的,我们加上字符set可以对于不同的数组做出不同的赋值
//接下来在test.c的game函数中调用即可
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
初始化数组后我们还要打印棋盘
void printboard(char arr[ROWS][COLS], int r, int c) {
printf("-----扫雷------\n");
for (int i = 0;i <= r;i++) {
printf("%d ", i);
}
printf("\n");
for (int i = 1;i <= r;i++) {
printf("%d ", i);
for (int j = 1;j <=c;j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
//我们在打印棋盘前加上扫雷的提示会显得更加美观
//由于我们制作的是简易扫雷,所以在每行每列前面加上行数列数便于玩家查找
打印完棋盘后我们接下来在我们的mine数组中埋雷
void setboard(char arr[ROWS][COLS], int r, int c) {
int count = numlei;//与我们的ROW一样,常数可以在头文件中进行定义赋值,也便于修改
while (count) {
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (arr[x][y] != '1') {//在mine数组中,我们用字符'1'表示雷,'0'表示不是雷
arr[x][y] = '1';//如果不是雷就随机赋值为雷,如果已经是雷则不做处理,直到布置十个雷
count--;
}
}
}
//由于我们是随机埋雷,就需要用到前面的rand函数知识点,同样在test.c文件中种下“种子”
int main() {
int a;
srand((unsigned int)time(NULL));//强制类型转换
}
布置完雷后我们就准备开始最刺激的排雷了,这个游戏的核心!
int lei(char mine[ROWS][COLS], int x, int y) {//lei函数用于/判断周围有多少个雷
int n=0;
for (int i = -1;i <= 1;i++) {//我们只需要判断我们的四周8个位置,可以确定x,y坐标不是雷,所以可以直接用循环
for (int j = -1;j <= 1;j++) {
if (mine[x+i][y+j] == '1') {
n++;
}
}
}
return n;
}
void findboard(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c) {
int x, y;//雷的坐标
int n=0 ;
do {
printf("请输入雷的坐标:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= r && y >= 1 && y <= c) {//用于判断输入的坐标是否合法
if (mine[x][y] == '1') {
printf("很遗憾,你踩到雷了\n");
printf("如果想继续玩可重新选择\n");
printboard(mine, ROW, COL);//在玩家游戏失败后展示一下哪些位置有雷,不然死不瞑目了
break;
}
else {
if (show[x][y] == '*') {//判断该位置是否已经被排查过
int c = lei(mine, x, y);//lei函数用于/判断周围有多少个雷
show[x][y] = c + '0';//由于我们存放的是字符,用到我们的ACSII码值,字符数字是连续排序的
printboard(show, ROW, COL);//将排查后的棋盘重新打印一遍
n++;
}
else {
printf("此坐标已经被排查过\n");
}
}
}
else {
printf("坐标输入有误,请重新输入:\n");
}
} while (n<r*c- numlei);//当所有不是雷的位置都排查出来则跳出循环
if (n == r * c - numlei) {
printf("恭喜你,排雷成功,你简直是神!!!\n");//此处我们可以随便输入一些有趣的
printf("如果想继续玩可重新选择\n");
}
}
这样我们的game.c的整个设计都设计好了,前面可知我们每个函数和常量的定义都在我们的头文件game.h中,所以在game.c创建的函数都要在game.h里声明,以下就是我们需要用到的头文件以及函数声明,这样在test.c文件里直接调用game.c里的函数
#include<stdio.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define numlei 10
void InitBoard(char arr[ROWS][COLS],int r,int c,char set);//初始化
void printboard(char arr[ROWS][COLS], int r, int c);//打印
void setboard(char arr[ROWS][COLS], int r, int c);//设置雷
void findboard(char mine[ROWS][COLS],char show[ROWS][COLS], int r, int c);//查找雷
以上就是我们整个基础扫雷的所有代码了,当然游戏还有很多可以改进的地方,在主包后续更新更多的编程知识大家也可以进行改良与创新,主包也会与你们一同进步共同学习的!
在线扫雷游戏:http://www.minesweeper.cn/