引言:在我们已经学习了前两篇内容(数组和函数)的情况下,我们也可以尝试写一些小游戏来对我们学习的知识予以运用。接下来我们就来请出这次的“lucky dog”——扫雷。
目录
一、扫雷游戏的分析
游戏说明:游戏可以通过菜单实现继续玩或者退出游戏,扫雷游戏的基础版本是9×9的格子,默认随机布置10个雷。玩家需要对雷的位置进行排查,如果所排查的位置不是雷,就会显示周围有几个雷;如果排查的位置是雷,就会被炸死从而游戏结束。当然游戏结束的情况有两种可能:一就是前面说的被炸死结束游戏,二就是把除10个雷之外的所有非雷都找出来,则排雷成功,游戏结束。
这是扫雷游戏的基础版本:
二、游戏所需文件(多文件形式)
1.test.c(主测试文件)
2.game.c和game.h(游戏逻辑)
仔细观察游戏说明发现:游戏肯定不止玩一次;游戏肯定要有菜单以及游戏内容,那么这菜单以及游戏内容肯定需要函数来封装吧,是不是感觉有那么一点熟悉,没错,就是我们前面所介绍的猜数字游戏,里面有大同小异之处,感兴趣的可跳转:C语言——猜数字游戏与关机程序(详细版)_c语言 猜数游戏-CSDN博客
三、游戏核心内容实现
1.简要问题概述
那么开始我们的第一步:主测试文件的大体框架
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu()
{
printf("****************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("****************************\n");
}
void game()
{
printf("扫雷\n");
}
int main()
{
int input = 0;
do
{
menu(input);
printf("请选择你的数字:");
scanf("%d", &input);
switch (input)
{
case 1:
game();//玩游戏的逻辑与内容
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,就俩数,重新输入\n");
}
} while (input);
return 0;
}
初始运行结果如下:
接下来第二步我们就需要在game函数里完善扫雷游戏的代码
void game()
{
//完善扫雷游戏内容
}
那么我们应该如何完成这个游戏内容呢?
扫雷扫雷,我们首先需要做的是什么?是不是布置雷?布置完后是不是还要将其存储起来,那么我们就可以使用二维数组来对其进行存储。那么我们应该如何区分是雷和不是雷呢?我们可以定义“1”是雷,“0”不是雷。
注:这里的0和1皆为字符0和字符1。
两个数组皆为字符数组。
但是这样又会存在一个问题,这里的“1”究竟是雷的个数是1,还是说就是这个位置是雷呢?这样会产生歧义。所以为了让我们更加容易辨认,我们应该使用两个数组来进行区分。
这个用来部署雷的信息:
这个用来部署排查的雷的信息:
但是当我们排查时会发现一个问题:越界。
所以9×9的格子已经无法满足,我们需要加以改进:引用11×11的格子。这样在排查如图所示的两个位置时就不会出现越界了。
2.棋盘的打印及初始化
我们专门给一个棋盘(对应一个数组mine)用来存放给布置好的雷的信息,给另一个棋盘(对应一个数组show)用来存放排查出的雷的信息。
test.c
//打印棋盘
DisplayBoard(mine, row, col);//我们真正需要的是中间的9*9格子
DisplayBoard(show, row, col);
接下来要做的就是打印这两个棋盘,观察有无错误。
game.c
void DisplayBoard(char board[rows][cols], int r, int c)
{
int i = 0;
printf("-------扫雷-------\n");
//打印列号
for (i = 0;i <= c;i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1;i <= r;i++)
{
int j = 0;
printf("%d ", i);
for (j = 1;j <= c;j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
随后进行棋盘的初始化:
test.c
//棋盘初始化
//mine数组初始化为全'0',表示还没有布置雷
//show数组初始化全为'*',表示所有位置都没有被排查
InitBoard(mine, rows, cols,'0');
InitBoard(show, rows, cols,'*');
game.c
#include "game.h"//自定义函数头文件用双引号
void InitBoard(char board[rows][cols], int r, int c,char set)
{
int i = 0;
for (i = 0;i < r;i++)
{
int j = 0;
for (j = 0;j < c;j++)
{
board[i][j] = set;
}
}
}
3.布置雷
test.c
//布置雷
SetMine(mine, row, col);
game.c
void SetMine(char mine[rows][cols], int r, int c)
{
//随机布置10个雷
int count = EASY_COUNT;
while (count)
{
//随机生成坐标
//x,y的范围是都是1~9
int x = rand() % r + 1;
int y = rand() % c + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
4.排查雷
test.c
//排查雷
FindMine(mine, show, row, col);
game.c
void FindMine(char mine[rows][cols], char show[rows][cols], int r, int c)
{
int x = 0;
int y = 0;
int win = 0;
while (win<r*c-EASY_COUNT)
{
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
//判断坐标的合法性
if (x >= 1 && x <= r && y >= 1 && y <= c)
{
//判断是否为雷
if (mine[x][y] == '1')
{
printf("sorry,你被炸死了\n");
DisplayBoard(mine, r, c);
break;
}
else
{
if (show[x][y] == '*')
{
//统计mine数组中,x,y坐标周围有几个雷
int c = GetMineCount(mine, x, y);
show[x][y] = c + '0';
DisplayBoard(show, row, col);
win++;
}
else
{
printf("该坐标已经被排查,请重新输入坐标:\n");
}
}
}
else
{
printf("输入的坐标非法,请重新输入\n");
}
}
if (win = r * c - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(show, r, c);
}
}
5.确定雷的个数
game.c
int GetMineCount(char mine[rows][cols],int x,int y)
{
// 法一:return mine[x][y+1]+mine[x-1][y+1]+minr[x-1][y]+mine[x-1][y-1]+mine[x][y-1]+mine[x+1][y-1]+mine[x+1][y]+mine[x+1][y+1]-8*'0';
int i = 0;
int j = 0;
int c = 0;
for (i = -1;i <= 1;i++)
{
for (j = -1;j <= 1;j++)
{
if (mine[x + i][y + j] == '1')
c++;
}
}
return c;
这里的法一原理如下图所示:
如图所示,我们想要知道箭头所指之处周围有几个雷应该怎么办呢?我们将其设为x,y,故周围参数依次得出。那么我们只需要将其求和(字符求和)减去8*'0'就可以得到雷的个数了。
法二本质上就是把法一写成两个循环。
四、三个文件的总列出
接下来我会将三个总文件(一些问题均以在代码中进行标注)逐个列出:
test.c:
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{
printf("****************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("****************************\n");
}
void game()
{
//完善扫雷游戏内容
char mine[rows][cols] = { 0 };//存放布置好的雷的信息
char show[rows][cols] = { 0 };//存放排查出的雷的信息
//棋盘初始化
//mine数组初始化为全'0',表示还没有布置雷
//show数组初始化全为'*',表示所有位置都没有被排查
InitBoard(mine, rows, cols,'0');
InitBoard(show, rows, cols,'*');
//布置雷
SetMine(mine, row, col);
//打印棋盘
DisplayBoard(mine, row, col);//我们真正需要的是中间的9*9格子
DisplayBoard(show, row, col);
//排查雷
FindMine(mine, show, row, col);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//game.c中的随机布置雷
do
{
menu(input);
printf("请选择你的数字:");
scanf("%d", &input);
switch (input)
{
case 1:
game();//玩游戏的逻辑与内容
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,就俩数,重新输入\n");
}
} while (input);
return 0;
}
game.c:
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"//自定义函数头文件用双引号
void InitBoard(char board[rows][cols], int r, int c,char set)
{
int i = 0;
for (i = 0;i < r;i++)
{
int j = 0;
for (j = 0;j < c;j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[rows][cols], int r, int c)
{
int i = 0;
printf("-------扫雷-------\n");
//打印列号
for (i = 0;i <= c;i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1;i <= r;i++)
{
int j = 0;
printf("%d ", i);
for (j = 1;j <= c;j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char mine[rows][cols], int r, int c)
{
//随机布置10个雷
int count = EASY_COUNT;
while (count)
{
//随机生成坐标
//x,y的范围是都是1~9
int x = rand() % r + 1;
int y = rand() % c + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
int GetMineCount(char mine[rows][cols],int x,int y)
{
// 法一:return mine[x][y+1]+mine[x-1][y+1]+minr[x-1][y]+mine[x-1][y-1]+mine[x][y-1]+mine[x+1][y-1]+mine[x+1][y]+mine[x+1][y+1]-8*'0';
int i = 0;
int j = 0;
int c = 0;
for (i = -1;i <= 1;i++)
{
for (j = -1;j <= 1;j++)
{
if (mine[x + i][y + j] == '1')
c++;
}
}
return c;
}
void FindMine(char mine[rows][cols], char show[rows][cols], int r, int c)
{
int x = 0;
int y = 0;
int win = 0;
while (win<r*c-EASY_COUNT)
{
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
//判断坐标的合法性
if (x >= 1 && x <= r && y >= 1 && y <= c)
{
//判断是否为雷
if (mine[x][y] == '1')
{
printf("sorry,你被炸死了\n");
DisplayBoard(mine, r, c);
break;
}
else
{
if (show[x][y] == '*')
{
//统计mine数组中,x,y坐标周围有几个雷
int c = GetMineCount(mine, x, y);
show[x][y] = c + '0';
DisplayBoard(show, row, col);
win++;
}
else
{
printf("该坐标已经被排查,请重新输入坐标:\n");
}
}
}
else
{
printf("输入的坐标非法,请重新输入\n");
}
}
if (win = r * c - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(show, r, c);
}
}
game.h:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//定义常量的符号
#
#define row 9
#define col 9
#define rows row+2
#define cols col+2
#define EASY_COUNT 10
//函数声明
//初始化棋盘
void InitBoard(char board[rows][cols], int r, int c,char set);
//打印棋盘信息
void DisplayBoard(char board[rows][cols],int r,int c);//实际上打印的是11*11的棋盘
//布置雷
void SetMine(char mine[rows][cols], int r, int c);
//排查雷
void FindMine(char mine[rows][cols], char show[rows][cols], int r, int c);
结语:以上就是本次扫雷游戏的流程代码以及总代码,希望可以对你有所帮助,提升学习C语言的兴趣。 后续会接着为大家更新相关内容。