1.用C语言实现扫雷游戏
本博客和三子棋游戏比较大的区别是,三子棋游戏是写完了再总结的,本博客是边代码实现边编辑博客,所以本博客会比较详细的po出每一步骤,在每实现一个小功能的时候我们都先验证下效果,再继续下一步。
2.思路
总体的思路和三子棋游戏是一样的,我们把游戏实现部分放在game.c和game.h中,把游戏的测试代码放到test.c中。main函数在test.c中。
小tips:我们把需要包含的头文件都放在game.h中,game.c和test.c中只需要包含我们的game.h一个头文件即可。
2.1 test.c函数实现
2.1.1 main()函数和test()函数实现
在main函数中只简单调用我们的test()函数,在test()函数中打印游戏菜单,实现让玩家选择开始游戏还是结束游戏。
- game.h中的代码实现
#pragma once //这个是我创建.h文件的时候自动出现的,不是自己手敲的
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
- test.c中的代码实现
#include "game.h"
void manu()
{
printf("===========================\n");
printf("========1:开始游戏=========\n");
printf("========0:结束游戏=========\n");
printf("===========================\n");
}
void test()
{
int input = 0;
do
{
manu();
printf("请输入您的选择:\n");
scanf("%d", &input);
switch(input)
{
case 1:
//game(); 因为还没有实现game()所以这里我屏蔽掉了。
break;
case 0:
printf("游戏结束,欢迎下次再来!\n");
break;
default:
printf("您的输入有误,请重新输入:\n");
}
} while (input);
}
int main()
{
test();
return 0;
}
- 验证
2.1.2 test.c中的game()函数实现思路
设计扫雷游戏之前,先进行思考
- 创建地图,9*9的二维数组,元素类型是什么?
- 每个格子的状态有以下状态
a)未翻开(草地)
b)已翻开(数字)
c)是地雷
d)不是地雷
四种状态是两大类,我们用两个二维数组表示。
第一个数组表示翻开状态showMap:
char类型表示,如果是 * 表示未翻开 ; 如果是阿拉伯数字,就表示翻开
第二个数组表示是否地雷状态mineMap:
char 类型表示 如果是 【1】:表示是地雷 【0】:表示不是地雷 、 也可以int 类型表示 如果是数字1:地雷; 数字0:不是地雷 - 这里还有个需要考虑的是虽然9×9数组是我们的目标,但是在边角位置,我们要对周围8个位置进行数雷的个数,非常容易数组越界,这里的解决思路有两个,其一是每次都需要判定我们的翻开位置是否是边角位置,这样比较繁琐。其二是直接创建两个11×11的数组,这样我们使用我们9*9的地图的时候就没有这个风险。这里使用的是第二个思路。
扫雷游戏的思路
- 先创建地图(两个地图一个是我们放雷的地图mineMap,一个是我们的展示地图showMap),并进行初始化
a)针对showMap 默认初始化全是 *,表示未知,等待玩家翻开
b)针对mineMap 默认初始化都是字符’0’,之后我们初始化10个地雷(根据随机位置来摆放) 没地雷用字符’0’ 表示,地雷位置用字符’1’表示- 打印地图showMap
- 给mineMap地图布置雷。
- 排查雷
a)提示玩家输入要翻开的位置坐标,并进行校验
b) 判定当前位置是否是雷,如果是雷则游戏失败,游戏结束
c) 如果当前位置是最后一个“不是雷的格子”那么游戏胜利,游戏结束
d)如果不是雷,更新翻开的当前位置,把【* 】替换成一个数字(就把showMap中对应位置的 * 修改成一个具体的数字)这个数字要根据当前位置周围8个格子的地雷数目来决定。- 下一步从2继续开始,直到游戏结束。
2.2 test.c中的game()函数和game.c中的函数实现
2.2.1 地图创建
- 在test.c中的game()函数中创建两个char类型的二维数组,需要设置两个全局变量,我们放在game.h中,这里我们先设置两个为9的行和列,由于我们为了避免数组越界所以又定义了一个,MAX_ROW和MAX_COL变量,这样设置,之后打印的时候不需要写很多个9了,直接用这个全局变量。
//game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9
#define COL 9
#define MAX_ROW ROW+2
#define MAX_COL COL+2
#include <stdio.h>
//test.c
void game()
{
//1.地图创建
char showMap[MAX_ROW][MAX_COL] = {
0 };
char mineMap[MAX_ROW][MAX_COL] = {
0 };
}
2.2.2 地图初始化
- game函数中只需要调用init函数,init函数的实际的实现在game.c中。
void game()
{
//1.地图创建
char showMap[MAX_ROW][MAX_COL] = {
0 };
char mineMap[MAX_ROW][MAX_COL] = {
0 };
//2.地图初始化
init(showMap, MAX_ROW,MAX_COL,'*');
init(mineMap, MAX_ROW, MAX_COL, '0');
}
- init函数具体实现
先在game.h中声明
//game.h
void init(char showMap[MAX_ROW][MAX_COL], int row, int col, char i);
然后在game.c中具体实现
可以用for循环,也可以使用库函数memset,这里因为我们需要初始化两个数组,但是两个数组的内容不同,所以给init函数添加了一个参数,用来传入我们要初始化的值。
//game.c
//1.地图初始化
void init(char board[MAX_ROW][MAX_COL], int row, int col, char set)
{
#if 0
int i = 0;
int j = 0;
for (i = 0; i < col; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;
}
}
#endif
memset(board, set, row * col * sizeof(char));
}
2.2.3 地图打印
- game函数中只需要调用Print函数,Print函数的实际的实现在game.c中。
//test.c
void game()
{
//1.地图创建
char showMap[MAX_ROW][MAX_COL] = {
0 };
char mineMap[MAX_ROW][MAX_COL] = {
0 };
//2.地图初始化
init(showMap, MAX_ROW,MAX_COL,'*');
init(mineMap, MAX_ROW, MAX_COL, '0');
//3. 地图打印
Print(showMap, ROW, COL); //打印地图我们只需要9行9列
Print(mineMap, ROW, COL);
}
- Print函数具体实现
先在game.h中声明
//game.h
//1.地图初始化
void init(char showMap[MAX_ROW][MAX_COL], int row, int col, char set);
//2.地图打印
void Print(char board[MAX_ROW][MAX_COL], int row, int col);
在game.c中具体实现
第一版本
//game.c
//2.地图打印
void Print(char board[MAX_ROW][MAX_COL], int row, int col)
{
//我们只需要打印9*9的,但是我们必须用11*11的接收,
// 因为数组的存储方式决定了,我们不可能只单单把中间元素取出来,只能按顺序取
int i = 0;
int j = 0;
for (i = 1; i < row-1; i++)
{
for (j = 1; j < col