Z_002 扫雷游戏
Posted TRistbl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Z_002 扫雷游戏相关的知识,希望对你有一定的参考价值。
目录
242.子函数 ( 展开1圈 ):
game.h ( 头文件,game.c的函数声明,宏定义,引用库函数 )
【分析已有案例】
【1】数字代表的含义:
11.有数字:
附近8个格子中有几个雷
12.无数字:
无数字则附近8个格子没有雷,展开一片
【2】玩游戏时
21.没点到雷:
展开该格子,显示附近雷的个数
22.点到雷:
游戏结束,并向玩家展示所有的雷,让玩家死的明明白白
【3】显示
31.游戏开始显迷雾状,需要点击揭开该格子
32.玩家看不到雷,雷是隐藏的
【基本思路】
【1】: 前戏
1.菜单
2.选择 ( 是否进入游戏 )
【2】: 游戏实现
1. 基本
布雷棋盘:( 不给玩家看 )
展示棋盘:( 展示给玩家看,展开后:显示附近雷的个数 )
宏定义:( 便于维护和修改 )
ROW 行
COL 列
MineCnt 雷的个数 ( 游戏难度 )
2 . 函数
初始化棋盘
打印棋盘
布雷
扫雷
【1】前戏
1.1菜单:
提示玩家进行选择,是否玩游戏
void menu()
{
printf("*********************\\n");
printf("****** 1.play *****\\n");
printf("****** 0.exit *****\\n");
printf("*********************\\n");
}
1.2选择:
不喜欢复制粘贴,我们分为三段来理解:
第【1】段:提示,输入
{
int input = 0; //(1)创建变量input
printf("请选择(1/0):"); //(2)提示玩家选择 ( 1 0 )
scanf("%d", &input); //(3)输入
}
第【2】段:选择
switch (input) //(0)我们前面输入的 input ,代入这里
{
case 1:
printf("进入游戏\\n\\n"); //(1)玩游戏,调用游戏模块( game.c )
game();
break;
case 0:
printf("退出游戏\\n\\n"); //(2)不玩,退出
break;
default:
printf("选择错误\\n\\n"); //(3)相当于 else ,重新输入
break;
}
(9) 也可也用 if 语句做代替
第【3】段:循环
int input = 0;
do
{
//menu();
//printf("请选择(1/0):");
//scanf("%d", &input);
//switch (input)
//{
//case 1:
// printf("进入游戏\\n\\n");
// game();
// break;
//case 0:
// printf("退出游戏\\n\\n");
// break;
//default:
// printf("选择错误\\n\\n");
// break;
}
} while (input); //(0) input在这里再次做判断条件,输入0时,循环结束
(1) 把前面所说的代码,扔进do while循环里就好了
(2) do while 的执行流程是:
不管条件是[3x7=21],先执行1次里面的语句,然后再进行判断,要不要循环
完整的代码:
void test() //(0) 封装成函数 test , 由 main 函数调用
{
int input = 0;
do
{
menu();
printf("请选择(1/0): ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("进入游戏\\n\\n");
//game(); //(1) 由于我们还没有写游戏函数,先注释掉
break;
case 0:
printf("退出游戏\\n\\n");
break;
default:
printf("选择错误\\n\\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
测试一下:
输入:
1:进入游戏
0:退出游戏
其他:选择错误 ( 循环 > 然后重新输入 )
太棒了!没有问题!我们接下来写游戏实现的部分~
【2】游戏实现
20 分块:
1.1切记不要把所有代码写在主程序中,上面的代码已经够多啦
1.2实现游戏的部分,我们单独开一个 [ game.c ] 来写,然后通过主程序来调用他
1.3 然后再开一个 头文件[ game.h ] ,相当于 [ game.c ] 的说明书,声明一些跨文件的函数,如果不声明的话,主程序运行的时候,是不认识他们的哦,所以会报错
131.头文件也可以放一些常用的 [ 库函数 ] ,< stdio.h >之类的
132.或者将:常用的数字进行 [ 宏定义 ] ,以便于后期修改
代码如下,我是这样写的:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h> //标准输入输出
#include<stdlib.h> //生成随机数函数需要
#include<time.h> //利用 [ 时间戳 ] 生成随机数
然后在test.c中,写一个函数game(),它负责调用 game.c 里的功能
引入头文件 "game.h"
#include"game.h"
void game()
{
}
之后我们写的代码,都是与游戏有关的,放进这个函数内就好了
21. 基本
棋盘(雷阵):
中间9*9是有效范围
多1圈是防止下棋越界的安全距离
实现代码:2维数组
(1)在 test.c 中:
void game()
{
char show[ROWS][COLS]; // 展示的棋盘
char mine[ROWS][COLS]; // 布雷的棋盘
char set = '0'; // 雷的字符
}
(2)在 game.h 中,定义几个常用的宏:
//下棋的有效范围
#define ROW 9
#define COL 9
//棋盘大小
#define ROWS ROW+2
#define COLS COL+2
(9)此时 show mine 2个棋盘的大小都为:11*11
2 . 函数
21.初始化棋盘:
我们有了棋盘,接下来要初始化棋盘
(1)game.c 中,加入:
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
board[i][j] = set; //(0)将棋盘的每1个元素,设置成set(外部传入)
}
}
(2)test.c中,加入:
InitBoard(show, ROWS, COLS, '*'); //展示棋盘:初始化为:*
InitBoard(mine, ROWS, COLS, '0'); //布雷棋盘:初始化为:字符0
22.打印棋盘:
220. 9*9的棋盘,那么大,下棋的时候很容易下错坐标
221. 我们需要打印出 [ 行号 ] 和 [ 列号 ] ,方便玩家下棋
222. 我们只打印中间的有效区域,多出的1圈不打印
代码挺复杂的,这里我分2段写:
第(1)段:输出 列号 1 ~ 9
{
int i, j;
printf(" "); //非重点,格式需要
for (i = 1; i <= col; i++) //(1)利用循环变量 i ,输出1~9,中间有1个空格隔开
printf("%d ", i);
printf("\\n"); //非重点,格式需要
}
第(2)段:
1.每行输出:对应行号
2.每行输出:数据 ( 由棋盘中存放的 [ 字符 ] 来决定 )
展示棋盘: 没揭开的格子,周围雷的数量
布雷棋盘: 雷 / 不是雷
{
for (i = 1; i <= row; i++)
{
printf("%d ", i); //每行的行号
for (j = 1; j <= col; j++)
printf("%c ", board[i][j]); //输出数据
printf("\\n"); //输出一行后,换行
}
printf("\\n");
}
完整代码:
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i, j;
printf(" ");
for (i = 1; 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 ", board[i][j]);
printf("\\n");
}
printf("\\n");
}
写完后,不要忘了在头文件中加入声明哦
和创建函数时,一模一样的参数就好,头文件其实就是告诉程序,要传给这个函数什么类型的参数的才能使用
在头文件 game.h 中,加入
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
23.布雷:
220. 让雷随机的分布在棋盘中( 时间戳,取余生成随机数 )
221. 1个坐标不会重复多次布雷
我们分为3段写:
(1) 随机坐标x,y,随机数 % 9 的范围是 0~8 ,那么我们 +1 就是 1~9 了( 棋盘的有效范围 )
int x, y;
x = rand() % row + 1; //(0~8)+1 = 1~9
y = rand() % col + 1;
//要使用rand()函数,就需要做下面的操作
(01) test.c中,设置随机数生成器,只需执行1次,永久受益
srand((unsigned int)time(NULL));
(02) 另外需要在头文件 game.h 中,引入2个库函数
#include<stdlib.h>
#include<time.h>
(2)
这里的set是字符'0',也就是初始化数组的值,代表棋盘是空的 ( 没有雷 )
如果坐标 x,y 的坐标是空的,那么就下棋
把棋盘中 x,y 的内容替换成字符'1' ( set+1 )
if (mine[x][y] == set)
{
mine[x][y] = set + 1;
cnt--;
}
(3)套上循环,放进函数内,我们的布雷函数就完成了
void SetMine(char mine[ROWS][COLS], int row, int col, char set)
{
int x, y;
int cnt = MineCnt; //(1) MineCnt 是宏定义,雷的数量
while (cnt) //(2) 没有雷可放了,中止循环
{
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == set)
{
mine[x][y] = set + 1;
cnt--; //(3) 每成功放1个雷,就会使cnt -1
}
}
}
24.排雷:
踩雷 ( 游戏结束 )
没踩雷:1.显示周围雷的个数
2.如果周围的雷数是0,自动展开1片
我们分为3段写:
(1) 输入坐标,并检查是否越界
int x, y;
printf("请输入坐标:");
scanf("%d%d", &x, &y); //(1) 输入坐标
if (x >= 1 && x <= row && y >= 1 && y <= col) // (2)是否越界?
{
//游戏代码暂时屏蔽
}
else
printf("坐标越界\\n");
(2) 踩雷了,坐标是'1'(雷)
if (mine[x][y] == set + 1)
{
printf("很遗憾,你踩雷了!\\n");
DisplayBoard(mine, ROW, COL); //打印雷区,让玩家死得明白
break; //游戏失败了,跳出循环 ( 退出 )
}
(3) 没踩雷,1 和 2 封装成函数
else
{
show[x][y] = IsCntMine(mine, x, y); //(1) 周围雷的个数,返回值赋到 show 棋盘
Open(show, mine, x, y); //(2) 展开空白区
DisplayBoard(show, ROW, COL); //(3) 打印 show 棋盘,给玩家反馈实时状况
cnt++;
}
(4) 前面有代码出现过 cnt++ ,意思是:如果下棋的次数 = 棋盘 - 所有的雷,那么游戏胜利
int cnt = 0;
if (cnt == row * col - MineCnt)
{
printf("排雷成功!\\n\\n");
break;
}
完整代码 ( 直接粘贴就没有食欲了,这就是要分块讲的原因 ) :
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, char set)
{
int x, y;
int cnt = 0;
while (1)
{
printf("请输入坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //if 坐标合法
{
if (mine[x][y] == set + 1) //if 踩雷
{
printf("很遗憾,你踩雷了!\\n");
DisplayBoard(mine, ROW, COL);
break;
}
else //else 没踩雷
{
show[x][y] = IsCntMine(mine, x, y);
Open(show, mine, x, y);
DisplayBoard(show, ROW, COL);
cnt++;
}
}
else //else 坐标越界
printf("坐标越界\\n");
if (cnt == row * col - MineCnt) //排光所有雷,游戏胜利
{
printf("排雷成功!\\n\\n");
break;
}
}
}
241.子函数 ( 计算附近的雷 ):
为什么雷区设置的是'0'和'1',而不是其他字符?
因为数字减去字符'0',就是数字本身 ( ASCII码值 )
所以,把附近一圈 ( 8个坐标 ) 加起来,再删除8个 字符' 0 '.就是我们所需要的雷的个数
fx(1)计算附近有几个雷,返回雷的个数,如果没有雷 ( 计算结果是‘0’ ) 则返回空格
int IsCntMine(char mine[ROWS][COLS], int x, int y)
{
int n = //除了自身之外的,8个坐标的和
mine[x - 1][y - 1] +
mine[x - 1][y + 1] +
mine[x - 1][y] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y + 1] +
mine[x + 1][y] -
8 * '0' + // - 8个'0': 雷数量(整形)
'0'; // + '0': 雷数量(字符形式)
if (n == '0')
return ' ';
else
return n;
}
242.子函数 ( 展开1圈 ):
若xy不是雷, 则展开附近1圈, 数值为0的棋盘
fx(2) 如果当前坐标是空的,那么周围1圈 ( 8个格子 )再调用附近有几个雷的函数
int Open(char show[ROWS][COLS], char mine[ROWS][COLS],int x, int y)
{
if (show[x][y] == ' ')
{
show[x - 1][y - 1] = IsCntMine(mine, x - 1, y - 1);
show[x - 1][y + 1] = IsCntMine(mine, x - 1, y + 1);
show[x - 1][y] = IsCntMine(mine, x - 1, y);
show[x][y - 1] = IsCntMine(mine, x, y - 1);
show[x][y + 1] = IsCntMine(mine, x, y + 1);
show[x + 1][y - 1] = IsCntMine(mine, x + 1, y - 1);
show[x + 1][y + 1] = IsCntMine(mine, x + 1, y + 1);
show[x + 1][y] = IsCntMine(mine, x + 1, y);
}
return 0;
}
好啦,大功告成!那么我们测试一下
老实说,无法递归性的展开一片,对玩家来说非常的不友好,以至于我找雷都得好多步才找出来, 我自己都不太想玩,就这么滴把!还是很垃圾,得继续加油!
【3】代码分享
test.c ( 主程序 )
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void game()
{
char show[ROWS][COLS]; // 展示的棋盘
char mine[ROWS][COLS]; // 布雷的棋盘
char set = '0'; // 雷的字符
InitBoard(show, ROWS, COLS, '*'); //初始化棋盘 = *
InitBoard(mine, ROWS, COLS,'0'); //初始化棋盘 = '0'
DisplayBoard(show, ROW, COL); //展示棋盘
SetMine(mine, ROW, COL,set); //设置雷
FindMine(show, mine, ROW, COL,set); //排雷(内置循环,输赢/是否继续,在内部判断)
}
void menu()
{
printf("*********************\\n");
printf("****** 1.play *****\\n");
printf("****** 0.exit *****\\n");
printf("*********************\\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择(1/0): ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("进入游戏\\n\\n");
game();
break;
case 0:
printf("退出游戏\\n\\n");
break;
default:
printf("选择错误\\n\\n");
break;
}
} while (input);
}
int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}
game.c ( 游戏的主要功能 > 都集成于此 )
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//展示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i, j;
printf(" ");
for (i = 1; 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 ", board[i][j]);
printf("\\n");
}
printf("\\n");
}
//设置雷
void SetMine(char mine[ROWS][COLS], int row, int col, char set)
{
int x, y;
int cnt = MineCnt;
while (cnt)
{
x = rand() % row + 1; //(0~8)+1 = 1~9
y = rand() % col + 1;
if (mine[x][y] == set)
{
mine[x][y] = set + 1;
cnt--;
}
}
}
//附近有几个雷?
int IsCntMine(char mine[ROWS][COLS], int x, int y)
{
int n =
mine[x - 1][y - 1] +
mine[x - 1][y + 1] +
mine[x - 1][y] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y + 1] +
mine[x + 1][y] -
8 * '0' + //-8set:雷数量(整形)
'0'; //+'0':雷数量(字符形式)
if (n == '0')
return ' ';
else
return n;
}
//展开空白区域,再检查附近1圈 ( 8个格子 ) 分别有几个雷
int Open(char show[ROWS][COLS], char mine[ROWS][COLS],int x, int y)
{
if (show[x][y] == ' ')
{
show[x - 1][y - 1] = IsCntMine(mine, x - 1, y - 1);
show[x - 1][y + 1] = IsCntMine(mine, x - 1, y + 1);
show[x - 1][y] = IsCntMine(mine, x - 1, y);
show[x][y - 1] = IsCntMine(mine, x, y - 1);
show[x][y + 1] = IsCntMine(mine, x, y + 1);
show[x + 1][y - 1] = IsCntMine(mine, x + 1, y - 1);
show[x + 1][y + 1] = IsCntMine(mine, x + 1, y + 1);
show[x + 1][y] = IsCntMine(mine, x + 1, y);
}
return 0;
}
//排雷
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, char set)
{
int x, y;
int cnt = 0;
while (1)
{
printf("请输入坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == set + 1)
{
printf("很遗憾,你踩雷了!\\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
show[x][y] = IsCntMine(mine, x, y);
//空白展开
Open(show, mine, x, y);
DisplayBoard(show, ROW, COL);
cnt++;
}
}
else
printf("坐标越界\\n");
if (cnt == row * col - MineCnt)
{
printf("排雷成功!\\n\\n");
break;
}
}
}
game.h ( 头文件,game.c的函数声明,宏定义,引用库函数 )
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//下棋的有效范围
#define ROW 9
#define COL 9
//棋盘大小
#define ROWS ROW+2
#define COLS COL+2
//雷的数量
#define MineCnt 10
//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//展示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布雷
void SetMine(char mine[ROWS][COLS], int row, int col, char set);
//扫雷
void FindMine(char show[ROWS][COLS],char mine[ROWS][COLS], int row,int col,char set);
88,谢读,有缘再见 ~
以上是关于Z_002 扫雷游戏的主要内容,如果未能解决你的问题,请参考以下文章