文章目录
一.游戏玩法和基本思路介绍
首先是关于游戏玩法:输入坐标进行排雷,如果所输入坐标不是雷,那么就会告诉我们周围8个格子中地雷的总数。如果周围8个格子都没有地雷,那就自动扩大安全区域。效果如下。而如果能够成功排完全部地雷,那就胜利
二.游戏实现和步骤介绍
1.准备阶段
步骤讲解
游戏开始之前,我们需要简略地制作一下游戏封面,效果图如下这个功能的实现并不复杂,直接上代码
步骤代码
void menu()
{
printf("*****************************\n");
printf("****** 扫雷 *****\n");
printf("****** 1.play *****\n");
printf("****** 0.exit *****\n");
printf("*****************************\n");
}
int main()
{
int input = 0;
do
{
menu(); //菜单函数
scanf("%d", &input);
switch (input)
{
case 1: game();
break;
case 0:
printf("退出游戏\n");
break;
default :printf("输入数值无效\n");
break;
}
} while (input); //输入1则进入游戏,输入0则直接退出,否则便会一直循环
}
2.设计和初始化棋盘
步骤讲解
1.首先我们需要设计像下面这样9×9的棋盘,并且我们需要一个棋盘来记录地图上地雷的位置信息,另一个棋盘是专门给用户游玩的棋盘,也就是展示棋盘。
2.其次,考虑到我们需要实现统计周围地雷数量的函数,而当统计的位置在地图的边角的时候,统计起来就会很麻烦,也许要分情况,也许会越界访问。
3.为了解决2问题,我们可以考虑直接将棋盘扩大一圈,也就是11*11。但是!我们的主要功能都是在两个棋盘的9×9区域实现的
所以我们先设计一些常量,并定义上述两个棋盘
//11×11
#define ROWS 11
#define COLS 11
//9×9
#define ROW 9
#define COL 9
char mine[ROWS][COLS] = { 0 }; //扫雷信息的棋盘
char show[ROWS][COLS] = { 0 }; //展示棋盘,即游戏棋盘
诶那这完事之后,我们就要开始对这两个棋盘动手动脚了(初始化)
4.首先我们先初始化一下扫雷信息的棋盘(mine数组),为了再次方便后续统计周围区域地雷数量,我们可以把字符1表示有地雷,而字符0表示没有地雷,这样子的话,周围有多少个字符1,就能够代表有多少个地雷,所以我们只需要两个循环把他全部初始化成字符0即可
5.然后就轮到了展示棋盘(show数组),因为是展示给用户看的,所以我们全部初始化成星号 * 就行,也就是两个循环全部初始化成星号。
6.这时候眼尖的小伙伴就发现了,这两个棋盘的初始化都是两个循环直接全部赋值成某个字符,那么我们就可以直接把这个字符当做参数传入函数,这样就能一个函数初始化两个棋盘
步骤代码
//函数实现
void InitBoard(char(*arr)[COLS],int row,int col,char set)
{// 11 11
int i = 0, j = 0;
for (i = 0; i < row; i++) //11×11的棋盘一起初始化
{
for (j = 0; j < col; j++)
{
arr[i][j] = set; //这个set就是我们想要初始化的字符
}
}
}
//函数体
InitBoard(mine, ROWS, COLS, '0'); //初始化棋盘
InitBoard(show, ROWS, COLS, '*');
3.打印棋盘
步骤讲解
1.由于是要给用户玩的,所以我们在打印的时候打印中间9×9的棋盘就可以。
2.其次,为了后续用户方便输入坐标,我们还需要在棋盘上打印一下行和列。效果如下
步骤代码
void ShowBoard(char (*arr)[COLS],int row,int col)
{// 9 9
int i = 0, j = 0;
//打印列的序号
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i); //打印棋盘的行的序号
for (j = 1; j <= col; j++) //打印星号*
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
4.布置雷
步骤讲解
1.接下来我们就要对mine数组(地雷信息数组)放点地雷进去了,我们先定义一个常量来表示我们要安放的地雷数量
#define MINE_NUM 10 //雷的个数
2.然后我们就要将这些地雷随机放到9×9的棋盘中,我们可以整个while循环,定义一个conut=MINE_NUM,然后每放一个地雷,conut就减一
步骤代码
void SetMine(char(*arr)[COLS], int row, int col)
{ // 9 9
srand((unsigned int)time(NULL));//随机布置雷
int count = MINE_NUM; //要布置的雷的数量
while (count)
{// 9
int x = rand() % ROW + 1;
int y = rand() % COL + 1;
if (arr[x][y] == '0') //如果是字符0,代表没有地雷,让他变成字符1即可
{
arr[x][y] = '1';
count--;
}
}
}
5.排雷
前菜
1.前菜:既然是扫雷,那就会有胜利的时候,也就是我们要实现胜利的功能,我们可以定义一个全局变量int win = 0;
来表示我们安全区域的个数,那么就会有:win+雷的总个数=总格子数(81),而我们不断排雷的过程就是个循环。 所以我们可以先得出这个框架
void ClearMine(char(*mine)[COLS], char(*show)[COLS], int row, int col)
{ //这一步是在9×9的棋盘中操作的 9 9
int x = 0, y = 0;
int sum = ROW * COL;//总格子数81
while (win + MINE_NUM < sum) //如果雷的数量和没踩到雷的数量=81,那就胜利
{
printf("请输入扫雷坐标(x,y)\n");
scanf("%d%d", &x, &y);
……
}
}
2.而用户输入的坐标需要判断合法性 if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
3.如果输入的坐标是雷(字符1),也就简单了,打印一句游戏结束,就直接return
4.相反,如果不是雷,我们就要获取周围地雷数量,并更新展示棋盘(show)。
获取周边地雷数
5.为了获取坐标所在九宫格的地雷数量,我们一个循环就可以实现,我们定义int cnt=0;
来统计雷的数量,由于坐标本身不是雷(即字符0),所以我们循环只需要cnt+=mine[x][y]-'0'
即完成任务。
6.得到周围地雷数量后我们还要同步更新show[x][y]
获取周边地雷数代码
int GetAroundNum(char(*mine)[COLS], int x, int y)
{
int cnt = 0;
int i = 0, j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
cnt += mine[i][j] - '0';
}
}
return cnt;
}
步骤完整代码
void ClearMine(char(*mine)[COLS], char(*show)[COLS], int row, int col)
{ //是在9×9的棋盘上操作的 9 9
int x = 0, y = 0;
int sum = ROW * COL;//总格子数
while (win + MINE_NUM < sum) //如果雷的数量和没踩到雷的数量=81,那就胜利
{
printf("请输入扫雷坐标(x,y)\n");
scanf("%d%d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//坐标合法
{
if (mine[x][y] == '1')
{
printf("踩雷!\n游戏结束!\n");
/*ShowBoard(mine, row, col);
system("pause");
system("cls");锦上添花部分,可加可不加*/
return;
}
else
{
int num = GetAroundNum(mine, x, y);//得到周边地雷数量
win++; //是安全区域,win++
if (num==0)//如果周围的地雷数量为0
{
Expand(mine, show, x, y);//那就开始拓展,下面有详讲
}
else
{
show[x][y] = num + '0'; //更新show数组
}
ShowBoard(show, row, col);
}
}
else
{
printf("坐标输入值非法\n");
}
}
printf("排雷成功!\n"); //如果能够完成这个循环,那就排雷成功
/*system("pause");
system("cls");可加可不加*/
}
//获得周围地雷个数
int GetAroundNum(char(*mine)[COLS], int x, int y)
{// x和y即坐标
int cnt = 0;
int i = 0, j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
cnt += mine[i][j] - '0'; //减去'0'得到整数
}
}
return cnt;
}
6.递归拓展安全区域
步骤讲解
1.如果周围存在地雷,那就让show[x][y]等于地雷数量对应的字符即可(比如周围有3个雷,就要让show[x][y]=‘3’,实现方法也很简单,让3+‘0’就好)
2.如果周围没有地雷,那就要让周围8个地雷都做一次GetAroundNum(获得周围地雷个数)的动作。
3.得到周围地雷个数后,再判断是否为0,不是0,那就直接更新show[x][y],是0,那就这个坐标周围8个再进行一次GetAroundNum(获取周围地雷个数)……以此类推,根据1,2步骤循环即可
步骤代码
void Expand(char(*mine)[COLS], char(*show)[COLS], int x, int y)
{
if (x < 1 || x>9 || y < 1 || y>9)//判断坐标合理性
{
return;
}
if (show[x][y] != '*') //如果已经被排查了
{
return;
}
int num = GetAroundNum(mine, x, y); //周围雷的数量
if (num > 0) //如果不是零,直接更新数量就行
{
show[x][y] = num + '0';
win++; //安全区域++
return;
}
else //如果是0
{
show[x][y]='0'; //此处还要更新
win++; //安全区域++
//周围8个都要递归
Expand(mine, show, x + 1, y - 1);
Expand(mine, show, x - 1, y);
Expand(mine, show, x - 1, y+1);
Expand(mine, show, x, y - 1);
Expand(mine, show, x, y + 1);
Expand(mine, show, x + 1, y - 1);
Expand(mine, show, x + 1, y);
Expand(mine, show, x + 1, y+1);
}
}
三.完整代码
game.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9
#define MINE_NUM 10 //雷的个数
//棋盘
void menu();
//初始化棋盘
void InitBoard(char (*arr)[COLS],int row,int col,char set);
//展示棋盘
void ShowBoard(char(*arr)[COLS], int row, int col);
//布置雷
void SetMine(char(*arr)[COLS], int row, int col);
//排雷
void ClearMine(char (*mine)[COLS],char(*show)[COLS],int row,int col);
//得出周围雷的数量
int GetAroundNum(char(*mine)[COLS], int x, int y);
//拓展周围是不是雷的区域
void Expand(char(*mine)[COLS], char(*show)[COLS], int x, int y);
game.c
#include"game.h"
int win = 0;//表示不是雷的数量
void menu()
{
printf("*****************************\n");
printf("****** 扫雷 *****\n");
printf("****** 1.play *****\n");
printf("****** 0.exit *****\n");
printf("*****************************\n");
}
void InitBoard(char(*arr)[COLS],int row,int col,char set)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = set;
}
}
}
void ShowBoard(char (*arr)[COLS],int row,int col)
{
int i = 0, j = 0;
printf("----------------扫雷----------------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i); //打印棋盘的行和列序号
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void SetMine(char(*arr)[COLS], int row, int col)
{
srand((unsigned int)time(NULL));//随机布置雷
int count = MINE_NUM; //雷的数量
while (count)
{
int x = rand() % ROW + 1;
int y = rand() % COL + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
void ClearMine(char(*mine)[COLS], char(*show)[COLS], int row, int col)
{
int x = 0, y = 0;
int sum = ROW * COL;//总格子数
while (win + MINE_NUM < sum) //如果雷的数量和没踩到雷的数量=81,那就胜利
{
printf("请输入扫雷坐标(x,y)\n");
scanf("%d%d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//坐标合法
{
if (mine[x][y] == '1')
{
printf("踩雷!\n游戏结束!\n");
ShowBoard(mine, row, col);
system("pause");
system("cls");
return;
}
else
{
//递归
int num = GetAroundNum(mine, x, y);
win++;
if (num==0)//如果周围的地雷数量为0
{
Expand(mine, show, x, y);//那就开始拓展
}
else
{
show[x][y] = num + '0';
}
ShowBoard(show, row, col);
}
}
else
{
printf("坐标输入值非法\n");
}
}
printf("排雷成功!\n");
system("pause");
system("cls");
}
int GetAroundNum(char(*mine)[COLS], int x, int y)
{
int cnt = 0;
int i = 0, j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
cnt += mine[i][j] - '0';
}
}
return cnt;
}
void Expand(char(*mine)[COLS], char(*show)[COLS], int x, int y)
{
if (x < 1 || x>9 || y < 1 || y>9)//判断坐标合理性
{
return;
}
if (show[x][y] != '*') //如果已经被排查了
{
return;
}
int num = GetAroundNum(mine, x, y); //周围雷的数量
if (num > 0) //如果不是零,直接写出周围的数字就行
{
show[x][y] = num + '0';
win++;
return;
}
else
{
show[x][y]='0';
win++;
Expand(mine, show, x + 1, y - 1);
Expand(mine, show, x - 1, y);
Expand(mine, show, x - 1, y+1);
Expand(mine, show, x, y - 1);
Expand(mine, show, x, y + 1);
Expand(mine, show, x + 1, y - 1);
Expand(mine, show, x + 1, y);
Expand(mine, show, x + 1, y+1);
}
}
test.c
#include"game.h"
void game()
{
char mine[ROWS][COLS] = { 0 }; //扫雷信息的棋盘
char show[ROWS][COLS] = { 0 }; //展示棋盘,即游戏棋盘
InitBoard(mine, ROWS, COLS, '0'); //初始化棋盘
InitBoard(show, ROWS, COLS, '*');
SetMine(mine,ROW,COL); //布置雷
ShowBoard(mine, ROW, COL); //展示棋盘
ShowBoard(show, ROW, COL);
//ShowBoard(mine, ROW, COL); //展示棋盘
ClearMine(mine, show, ROW, COL); //排雷
}
int main()
{
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1: game();
break;
case 0:
printf("退出游戏\n");
break;
}
} while (input);
}