扫雷在游戏史上也是一个十分悠久的经典了,我用我所学知识制作了一个简易的扫雷小游戏,来跟着我一起去看看吧。
目录
一.所需文件
哪怕是简易的扫雷也不是一个相当简易的代码,所以在制作过程中我采用了分模块化的思想,将功能的实现分开来存放
好处
1.方便调试纠错
2.功能块更直观
二.创建菜单
创建一个函数将菜单放入
void menu() //菜单
{
printf("------------\n"
"---1.开始---\n"
"---0.结束---\n"
"------------\n");
}
使用while循环来实现菜单使用,用switch语句进行功能选择
int main()
{
//依靠时间来创建随机值
srand((unsigned int)time(NULL));
int a;
while (1)
{
menu();
scanf("%d", &a);
switch (a)
{
case 1:
game();
break;
case 0:
printf("结束游戏\n");
return 0;
default:
printf("无效数字,无法启动\n");
}
}
return 0;
}
三.游戏核心内容实现
我这里简化过了代码默认是9*9的棋盘和默认10个地雷
game()函数中存放所有的功能函数
1.创建两个二维数组进行棋盘的建立
char ture[rets][rots] = { 0 };
char shadow[rets][rots] = { 0 };
将它们打印出来是这样的
图一:用于雷的布置(ture)
图二:给玩家看的棋盘(shadow)
因为行和列会频繁使用,所以在saolei1.h头文件中进行宏定义
这里为了防止边界布置雷的问题设置了了一个11*11的棋盘
#define ret 9
#define rot 9
#define rets ret+2
#define rots rot+2
在saolei1.h头文件中声明创建棋盘
void load(char load[rets][rots], int RETS, int ROTS, char q);
注:这里的RETS和rets一样都是11,大写只是为了区分
在saolei2.c文件中进行创建
void load(char load[rets][rots], int RETS, int ROTS, char q) //创建棋盘
{
int i,j;
for (i = 0; i <RETS; i++)
{
for (j = 0; j <ROTS; j++)
{
load[i][j] = q;
}
}
}
在saolei.c中实现调用
后面的'0'和'*'可以自行更改,但棋盘也会跟着变化
//棋盘初始化
load(ture,rets,rots,'0');
load(shadow, rets, rots,'*');
2.初始化棋盘
在saolei1.h头文件中声明打印棋盘
//打印
void print(char print[rets][rots], int RET, int ROT);
在saolei2.c文件中实现打印函数
使用for循环进行打印,同时注意打印列时要从0开始打印否则会出现列数对不准的情况
void print(char print[rets][rots], int RET, int ROT) //打印初始化
{
int i, j;
printf("-------- 扫雷 --------\n");
for (i = 0; i <= RET; i++) //打印列
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= RET; i++)
{
printf("%d ", i); //打印行
for (j = 1; j <= ROT; j++)
{
printf("%c ", print[i][j]);
}
printf("\n");
}
}
3.布置雷
在头文件saolei1.h中声明place函数,并定义arr代表雷的个数
进行宏定义雷数方便雷数的更改
#define arr 10
//地雷
void place(char place[rets][rots], int RET, int ROT);
调用rand()需要srand()
注:srand需要包含<stdlib.h>头文件才可以使用
time需要包含<time.h>头文件才可以使用
srand((unsigned int)time(NULL));
在saolei2.c文件中实现布置雷函数
除余加一是必要的这样可以控制布置雷的范围避免出现布置雷到棋盘外的情况
void place(char place[rets][rots], int RET, int ROT) //地雷添加
{
int count = arr;
while (count)
{
int i = rand() % RET+ 1; //时间创建随机值除余是加一是为了控制布置雷的范围
int j = rand() % ROT+ 1;
if (place[i][j] == '0')
{
place[i][j] = '1';
count--;
}
}
}
在saolei.c中实现函数调用
//布置雷
place(ture, ret, rot);
4.排查雷和插旗
在saolei1.h头文件中声明排查雷
//查找
void look(char ture[rets][rots],char shadow[rets][rots], int RET, int ROT);
在saolei2.c文件中实现排查雷和插旗功能
void look(char ture[rets][rots],char shadow[rets][rots], int RET, int ROT) //排查雷
{
int a = 0, b = 0, l = 0;
while (l<RET*ROT-arr)
{
l = bumb(ture, shadow, RET, ROT);
printf("输入坐标:");
scanf("%d %d", &a, &b);
getchar();
if (shadow[a][b] != '*')
{
printf("这个坐标已经被查过了\n");
}
int c = 0;
printf("选择插旗还是排雷(a/b):");
printf("\n");
scanf("%c", &c);
if (c == 'a') //选择排雷
{
if ((a >= 1) && (a <=ret) && (b >= 1) && (b <= rot))
{
if (ture[a][b] == '1')
{
printf("很遗憾你被炸死哩\n");
print(ture, ret, rot);
break;
}
else
{
open(ture,shadow,a,b);
print(shadow, ret, rot);
}
}
}
else if(c=='b') //选择插旗
{
flag(shadow, a, b);
}
else
{
printf("输入无效坐标\n");
}
}
if (l == RET * ROT - 10)
{
printf("恭喜排雷成功\n");
print(ture, ret, rot);
}
}
同时在look()函数内包含flag()函数来达到插旗的效果
void flag(char shadow[rets][rots], int x, int y)
{
if (shadow[x][y] != '!')
{
shadow[x][y]='!'; //设置!为旗子
print(shadow, ret, rot);
}
else if(shadow[x][y] == '!')
{
printf("该位置已经被插旗了\n");
}
}
在saolei.c中实现函数调用
将ture数组和shadow数组还有ret(9),rot(9)传入look()
//查找雷
look(ture,shadow, ret, rot);
5.展开一片雷和计算旁边有多少雷
在saolei1.h头文件中声明展开雷
//展开
void open(char ture[rets][rots], char shadow[rets][rots], int RET, int ROT);
在saolei2.c文件中实现展开雷和计算旁边有多少雷
通过递归的方式进行反复查询,直到空白处旁边的九宫格内有雷就停下来
void open(char ture[rets][rots],char shadow[rets][rots], int x, int y)
{
if ((x >= 1) && (x <= ret) && (y >= 1) && (y <= rot))
{
int count = capture(shadow, ture, x, y);
if (count != 0)
{
shadow[x][y] = count + '0';
}
//使用与与操作符进行判定
else if ((shadow[x][y] != ' ')&&(shadow[x][y]!='!'))
{
shadow[x][y] = ' ';
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
//使用递归反复进行
open(ture, shadow, i, j);
}
}
}
else
{
return;
}
}
}
计算雷函数capture()
这里样使用递归进行九宫格查询,并将以自身为中心的九宫格内雷的数量打印在自身的位置
int capture(char shadow[rets][rots], char ture[rets][rots], int x, int y)
{
int i,a = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (ture[i][j] == '1')
{
a++;
}
//使用递归反复进行
capture(shadow, ture, i, j);
}
}
return a;
}
在saolei.c中实现函数调用
//展开
open(ture, shadow, ret, rot);
6.查看是否查完雷
在saolei1.h头文件中声明查雷
//查雷
int bumb(char ture[rets][rots], char shadow[rets][rots], int RET, int ROT);
在saolei2.c文件中实现查雷
int bumb(char ture[rets][rots], char shadow[rets][rots], int x, int y)
{
int i, j,z=0;
for (i = 0; i <= x; i++)
{
for (j = 0; j <= y; j++)
{
if (shadow[i][j] != '*'|| shadow[i][j] ==' ')
z++;
}
}
return z;
}
在saolei.c中实现函数调用
bumb(ture, shadow, ret, rot);
7.扫雷实现功能过程
进入菜单
选择开始打印雷和玩家所视棋盘(打印雷方便演示功能,其中设置1为地雷)
输入坐标进行插旗和排雷
被炸死回到菜单同时输出有雷界面
输入零结束游戏
四.全部代码
saolei1.h
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define ret 9
#define rot 9
#define rets ret+2
#define rots rot+2
//初始化定义
void load(char load[rets][rots], int RETS, int ROTS, char q);
//打印
void print(char print[rets][rots], int RET, int ROT);
#define arr 10
//地雷
void place(char place[rets][rots], int RET, int ROT);
//查找
void look(char ture[rets][rots],char shadow[rets][rots], int RET, int ROT);
//展开
void open(char ture[rets][rots], char shadow[rets][rots], int RET, int ROT);
//查雷
int bumb(char ture[rets][rots], char shadow[rets][rots], int RET, int ROT);
saolei.c
#include"saolei1.h"
//游戏函数
void game()
{
char ture[rets][rots] = { 0 };
char shadow[rets][rots] = { 0 };
//棋盘初始化
load(ture,rets,rots,'0');
load(shadow, rets, rots,'*');
//布置地雷
place(ture, ret, rot);
print(ture, ret, rot);
print(shadow, ret, rot);
//查找雷
look(ture,shadow, ret, rot);
//展开
open(ture, shadow, ret, rot);
//查雷
bumb(ture, shadow, ret, rot);
}
int main()
{
//依靠时间来创建随机值
srand((unsigned int)time(NULL));
int a;
while (1)
{
menu();
scanf("%d", &a);
switch (a)
{
case 1:
game();
break;
case 0:
printf("结束游戏\n");
return 0;
default:
printf("无效数字,无法启动\n");
}
}
return 0;
}
saolei2.c
#include"saolei1.h"
void menu() //菜单
{
printf("------------\n"
"---1.开始---\n"
"---0.结束---\n"
"------------\n");
}
void load(char load[rets][rots], int RETS, int ROTS, char q) //创建棋盘
{
int i,j;
for (i = 0; i <RETS; i++)
{
for (j = 0; j <ROTS; j++)
{
load[i][j] = q;
}
}
}
void print(char print[rets][rots], int RET, int ROT) //打印初始化
{
int i, j;
printf("-------- 扫雷 --------\n");
for (i = 0; i <= RET; i++) //打印列
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= RET; i++)
{
printf("%d ", i); //打印行
for (j = 1; j <= ROT; j++)
{
printf("%c ", print[i][j]);
}
printf("\n");
}
}
void place(char place[rets][rots], int RET, int ROT) //地雷添加
{
int count = arr;
while (count)
{
int i = rand() % RET+ 1;
int j = rand() % ROT+ 1;
if (place[i][j] == '0')
{
place[i][j] = '1';
count--;
}
}
}
int capture(char shadow[rets][rots], char ture[rets][rots], int x, int y)
{
int i,a = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (ture[i][j] == '1')
{
a++;
}
//使用递归反复进行
capture(shadow, ture, i, j);
}
}
return a;
}
//插旗
void flag(char shadow[rets][rots], int x, int y)
{
if (shadow[x][y] != '!')
{
shadow[x][y]='!';
print(shadow, ret, rot);
}
else if(shadow[x][y] == '!')
{
printf("该位置已经被插旗了\n");
}
}
//展开雷
void open(char ture[rets][rots],char shadow[rets][rots], int x, int y)
{
if ((x >= 1) && (x <= ret) && (y >= 1) && (y <= rot))
{
int count = capture(shadow, ture, x, y);
if (count != 0)
{
shadow[x][y] = count + '0';
}
//使用与与操作符进行判定
else if ((shadow[x][y] != ' ')&&(shadow[x][y]!='!'))
{
shadow[x][y] = ' ';
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
//使用递归反复进行
open(ture, shadow, i, j);
}
}
}
else
{
return;
}
}
}
//查雷
int bumb(char ture[rets][rots], char shadow[rets][rots], int x, int y)
{
int i, j,z=0;
for (i = 0; i <= x; i++)
{
for (j = 0; j <= y; j++)
{
if (shadow[i][j] != '*'|| shadow[i][j] ==' ')
z++;
}
}
return z;
}
void look(char ture[rets][rots],char shadow[rets][rots], int RET, int ROT) //排查雷
{
int a = 0, b = 0, l = 0;
while (l<RET*ROT-arr)
{
l = bumb(ture, shadow, RET, ROT);
printf("输入坐标:");
scanf("%d %d", &a, &b);
getchar();
if (shadow[a][b] != '*')
{
printf("这个坐标已经被查过了\n");
}
int c = 0;
printf("选择插旗还是排雷(a/b):");
printf("\n");
scanf("%c", &c);
if (c == 'a') //选择排雷
{
if ((a >= 1) && (a <=ret) && (b >= 1) && (b <= rot))
{
if (ture[a][b] == '1')
{
printf("很遗憾你被炸死哩\n");
print(ture, ret, rot);
break;
}
else
{
//l=bumb(ture, shadow,RET,ROT);
open(ture,shadow,a,b);
print(shadow, ret, rot);
}
}
}
else if(c=='b') //选择插旗
{
flag(shadow, a, b);
}
else
{
printf("输入无效坐标\n");
}
}
if (l == RET * ROT - arr)
{
printf("恭喜排雷成功\n");
print(ture, ret, rot);
}
}
五.总结
首先谢谢读到这里的读者,如果有不对的地方望指出,我会积极得去改正
同时该游戏还有很多可以拓展的地方,比如:加上计时器,更改难度等等