本文将详细介绍如何实现一个简单的扫雷游戏。
一.实现前提
需要大家能够熟悉运用以下知识:
一维数组、二维数组,自定义函数,for循环,do-while循环,if语句,#define定义符号常量,rand配合srand设置随机种子来生成随机数。
二.游戏整体思路
1. 游戏初始化
- 首页显示:通过
homepage函数打印游戏的首页,提供 “开始游戏” 和 “退出游戏” 两个选项供玩家选择。 - 数组初始化:在
game函数中,创建了三个二维数组:arr1用于存储地雷分布状态,arr2用于显示给玩家的界面,vis用于标记已经检查过的位置。使用Initialize函数将arr1初始化为'0',表示初始时没有地雷;将arr2初始化为'*',表示所有位置都未被翻开。 - 地雷设置:调用
Set_bombs函数,使用rand函数随机生成地雷的位置,将arr1中相应位置设置为'1',直到设置的地雷数量达到BOMBS_NUM。
2. 游戏核心逻辑
- 玩家操作:在
game函数的while循环中,只要未翻开的非地雷格子数量大于BOMBS_NUM,游戏就继续进行。每次循环中,打印arr2供玩家查看当前游戏界面,并提供三个操作选项:标记地雷、取消标记、翻开格子。 - 标记地雷:玩家选择标记地雷时,输入要标记的位置,程序检查该位置是否合法且未被翻开,如果是则将该位置标记为
'!'。 - 取消标记:玩家选择取消标记时,输入要取消标记的位置,程序检查该位置是否合法且已被标记,如果是则将该位置恢复为
'*'。 - 翻开格子:玩家选择翻开格子时,输入要翻开的位置,程序检查该位置是否合法且未被翻开。如果该位置是地雷(
arr1[x][y] == '1'),则游戏失败,打印地雷分布状态并结束游戏;如果不是地雷,则调用chain函数进行连锁反应。
3. 连锁反应机制
- 判断周围地雷数量:
judge_bomb函数用于计算指定位置周围的地雷数量,并将结果存储在arr2中。 - 连锁翻开:
chain函数实现了连锁反应机制。如果翻开的格子周围没有地雷(arr2[x][y] == '0'),则递归地翻开其周围的格子,直到遇到有地雷的格子为止。同时,使用vis数组标记已经检查过的位置,避免重复检查。
4. 游戏结束判断
- 胜利条件:当未翻开的非地雷格子数量等于
BOMBS_NUM时,说明玩家已经成功排除了所有地雷,游戏胜利,打印地雷分布状态并输出胜利信息。 - 失败条件:当玩家翻开的格子是地雷时,游戏失败,打印地雷分布状态并输出失败信息。
三.分部分注释解释代码
1.头文件包含
#include <stdio.h>//printf,scanf等库函数的头文件
#include <stdlib.h>//rand,srand等库函数的头文件
#include <time.h>//提供时间戳
#define LEN 9//定义扫雷棋盘长度
#define WID 9//定义扫雷棋盘宽度
#define LENGTH LEN+2//定义扫雷棋盘实际长度
#define WIDTH WID+2//定义扫雷棋盘实际宽度
#define BOMBS_NUM 10//定义雷的数量
由于代码的执行中会涉及到九宫格内的雷数查找,为防止越界,所以在此处设定数组的实际长度和宽度,若无法理解,可见下图:

此图的黄色部分表示扫雷棋盘的大小,而红色部分加黄色部分则是实际扫雷棋盘大小。由图可知,实际棋盘可以让我们在对边界坐标进行九宫格内雷数查找的时候不出现越界的情况。
2.菜单编写
我们可以利用printf来创建一个简易的菜单函数:
void homepage()
{
printf("********************\n");
printf("*** 1.play ***\n");
printf("*** 2.exit ***\n");
printf("********************\n");
printf("请输入操作序号-->");
}
3.主函数编写
int main()
{
int choose = 0;
//使用do-while循环让玩家玩完后若还想玩可以继续
//并且do-while函数至少执行一次符合我们菜单选择的要求
do {
//引入菜单函数打印菜单
homepage();
//让玩家选择菜单选项
scanf("%d", &choose);
//玩家输入1,也就是玩,那么直接进入游戏函数(详情见二.4)
if (choose == 1)
{
game();
}
//玩家输入2,说明玩家不想玩了,则退出函数
else if (choose == 2)
{
break;
}
//玩家输入不是菜单中的选项,则提醒玩家输入错误,让玩家重新输入
else
{
printf("输入错误,请重试\n");
}
} while (choose != 2);//只要玩家输入的数不是退出游戏则循环继续
return 0;
}
4.游戏函数编写
void game()
{
//数组创建
char arr1[WIDTH][LENGTH];//建立炸弹位置数组
char arr2[WIDTH][LENGTH];//建立显示数组
int vis[WIDTH][LENGTH]={0};//建立标记数组,并初始化
//数组初始化,使用数组初始化函数(二.5)
Initialize(arr1, '0');//将状炸弹位置数组初始化为0(我们用0表示没有炸弹,1表示有炸弹)
Initialize(arr2, '*');//将显示数组初始化为*
//布置炸弹
Set_bombs(arr1);//使用炸弹布置函数(详情见二.6)
int screened_num = 0;
//用while循环若没有踩到雷则继续排查,若只剩下雷则游戏胜利
while (screened_num < WID * LEN - BOMBS_NUM)
{
print(arr2);//使用打印函数(详情见二.7)打印数组
int choose = 0;
//若玩家觉得某处有炸弹,让玩家可以标记该处
printf("1.标记炸弹\n");
//若玩家觉得标记的地方没有炸弹,让玩家可以取消该地标记
printf("2.取消标记\n");
//若玩家觉得某处无炸弹,让玩家可以排除该处
printf("3.排除炸弹\n");
//让玩家选择要进行的操作
printf("请选择:");
//获取玩家的选择
scanf("%d", &choose);
if (choose == 1)//玩家选择标记
{
//让玩家输入想要标记的位置
printf("请输入您要标记的位置(行 列)-->");
int x = 0, y = 0;
scanf("%d %d", &x, &y);
//判断该处是否合法
if (x > 0 && x <= WID && y > 0 && y <= LEN)
{
//判断该地是否未排除或未标记过
if (arr2[x][y] == '*')
//是则成功标记
arr2[x][y] = '!';
//判断该地是否标记过
else if (arr2[x][y] == '!')
//是则提示玩家此处已经标记过了
printf("该地已标记\n");
//其余情况说明该地以排除过
else
//提示玩家此处以排除过了
printf("该地已排除\n");
}
else
{
//不合法,提示玩家输入的位置不合法
printf("您输入的位置不合法,请重新输入\n");
}
}
else if (choose == 2)//玩家选择取消标记
{
//让玩家输入想要取消标记的目标
printf("请输入您要取消标记的位置(行 列)-->");
int x = 0, y = 0;
scanf("%d %d", &x, &y);
//判断该处是否合法
if (x > 0 && x <= WID && y > 0 && y <= LEN)
{
//判断该地是否标记过
if (arr2[x][y] == '!')
//是则取消成功
arr2[x][y] = '*';
//判断该地是不是未标记过
else if (arr2[x][y] == '*')
//是则提示玩家该地没有标记过
printf("该地未标记过\n");
//其余情况说明该地以排除过
else
//提示玩家该地已排除
printf("该地已排除\n");
}
else
{
//不合法,提示玩家输入的位置不合法
printf("您输入的位置不合法,请重新输入\n");
}
}
else if(choose==3)//玩家选择排除
{
//让玩家输入想要排查的目标
printf("请输入您要排除的位置(行 列)-->");
int x = 0, y = 0;
scanf("%d %d", &x, &y);
//判断该处是否合法
if (x > 0 && x <= WID && y > 0 && y <= LEN)
{
//判断该地是否未排查过
if (arr2[x][y] == '*')
{
//判断该地是不是雷,若是则结束游戏
if (arr1[x][y] == '1')
{
//提示玩家踩到炸弹了
printf("嘣,很遗憾,炸弹炸啦!\n");
//将炸弹位置数组打印出来,让玩家知道哪里有雷
print(arr1);
//退出此次游戏
break;
}
//其余情况则无雷
else
{
//连锁显示函数(详情见二.8)
chain(arr1, arr2, vis, x, y, screened_num);
}
}
//其他情况则是已排查过的
else
{
//提示玩家该处已排除
printf("该地已排查,请排除其他处\n");
}
}
else
{
//提示玩家该处不合法
printf("您输入的位置不合法,请重新输入\n");
}
}
else
{
printf("没有该选项,请重新输入\n");
}
}
//判断是否胜利
if (screened_num == WID * LEN - BOMBS_NUM)
{
//提示玩家赢了
printf("恭喜你排除了所有的雷,游戏胜利\n");
//将炸弹位置数组打印出来
print(arr1);
}
}
5.数组初始化函数编写
//将char类型的数组初始化为指定的字符
void Initialize(char arr[WIDTH][LENGTH], char m)
{
for (int i = 0;i < WIDTH;i++)
{
for (int j = 0;j < LENGTH;j++)
{
arr[i][j] = m;
}
}
}
6.炸弹布置函数编写
void Set_bombs(char arr[WIDTH][LENGTH])
{
int bombs = BOMBS_NUM;
//使用当前时间作为随机数生成器的种子
srand((unsigned int)time(NULL));
//使用while循环,若炸弹设置完了就结束循环
while (bombs)
{
//随机取一个的位置将其设置为炸弹
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
//判断该处是否未安置炸弹
if (arr[x][y] == '0')
{
//是则将该处设为炸弹
arr[x][y] = '1';
//并将未设炸弹的数量-1
bombs--;
}
}
}
7.打印函数编写
void print(char arr[WIDTH][LENGTH])//打印棋盘
{
//打印列号
for (int j = 0;j <= LEN;j++)
{
printf("%2d", j);
}
printf("\n");
for (int i = 1;i <= WID;i++)
{
//打印行号
printf("%2d", i);
for (int j = 1;j <= LEN;j++)
{
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
以上printf中的%后的2是为了使棋盘更美观,确定每一个字符的长度。
8.连锁显示函数编写
//若该地九宫格内没有雷,我们可以将附近九宫格内没有雷位置的九宫格也显示出来
void chain(char arr1[WIDTH][LENGTH], char arr2[WIDTH][LENGTH], int vis[WIDTH][LENGTH], int x, int y, int* num)
{
//排查过的位置对应的标记数组为1,筛选避免无限递归情况发生
if (vis[x][y] == 1)
return;
//利用标记数组标记该位置,以便筛选
vis[x][y] = 1;
//将显示数组的该位置改为该位置九宫格内雷的数量
judge_bomb(arr1, arr2, x, y);//九宫格雷数统计函数(详情见二.9)
//排除过的数量+1
(*num)++;
//由于显示数组的该处已改为九宫格雷数,如果不为0,那么就不需要进行下面的连锁操作了
if (arr2[x][y] != '0')
return;
//经过筛选,剩下的为九宫格内无雷的,那么启动连锁
//由于九宫格内无雷,那么可将该处九宫格的九宫格雷数都显示出来
for (int i = x - 1;i <= x + 1;i++)
{
for (int j = y - 1;j <= y + 1;j++)
{
//判断该处是否合法,以避免越界的情况
if (i >= 1 && i <= WID && j >= 1 && j <= LEN)
{
//利用递归,显示九宫格的雷数,并连锁九宫格没有雷的位置
chain(arr1, arr2, vis, i, j, num);
}
}
}
}
9.九宫格雷数统计函数
void judge_bomb(char arr1[WIDTH][LENGTH], char arr2[WIDTH][LENGTH], int x, int y)
{
//定义变量九宫格内雷数
int bombs = 0;
for (int i = x - 1;i <= x + 1;i++)
{
for (int j = y - 1;j <= y + 1;j++)
{
//判断是否为雷
if (arr1[i][j] == '1')
{
//是让九宫格内雷数+1
bombs++;
}
}
}
//由于arr2数组为char类型,所以让数字加上0的ASCLL码值就等于该数字对应的ASCLL码值
arr2[x][y] = '0' + bombs;
}
以上就是简易版扫雷游戏的详细介绍,若介绍中有错误,希望大家能够帮我指出,谢谢!
183

被折叠的 条评论
为什么被折叠?



