1分析
我们需要写三个部分,分别是game.h game.c 扫雷.c三个文件,其中game.c实现扫雷函数的实行,而扫雷.c则是宏观的具体框架,game.h则是我们声明函数的地方
2game.h
#define _CRT_SECURE_NO_WARNINGS 1
#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 rows,int cols,char set);//这个字符set可以实现show初始化'0'mine初始化*
void DisplayBoar(char board[ROWS][COLS], int row, int col);//打印函数
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int *win);
//展开一片的前提
//1该坐标不是雷
//2该坐标周围不是雷
//扩展功能实现
//1展开一片
//2标记雷
//3显示剩余雷的个数
ROW表示行COL表示列
InitBoard表示初始化游戏,我们使用的是二维数组,DisplayBoard是玩家选择坐标,SetMine是埋雷,FindMine是找雷,expand是扩展功能实现当周围8个格子都没有雷的时候我们可以直接展开。后续还可以完善标记雷的功能。
3game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-------扫雷-------\n");
//控制列号
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行数
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
//假设布置10个雷
int count = EASY_COUNT;
while (count)
{
int x = rand() % row+1;//1~9
int y = rand() % col+1;//1~9
if (mine[x][y] == '0')
{
mine[x][y] ='1';
count--;
}
}
}
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[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] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win<row*col-EASY_COUNT)
{
printf("请输入要排查的坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y]!='*')
{
printf("该坐标被排查过了\n");
continue;
}
if (mine[x][y] == '1')
{
printf("哈哈哈,小石你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
expand(mine, show, x, y, &win);
system("cls");//清屏(看需要)
DisplayBoard(show, ROW, COL);
//统计坐标周围有几个雷
int n=get_mine_count(mine,x,y);
show[x][y] = n+'0';//此处我们要输入的是字符n n+'0'就是我们对应的字符
DisplayBoard(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == (row * col - EASY_COUNT))
{
//system("cls");//看需要是否想清屏
printf("小石,恭喜你,排雷成功了\n");
DisplayBoard(mine, ROW, COL);
}
}
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int*win)
{
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
int count = get_mine_count(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
int i = 0;
for (i = x - 1; i < x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else
{
show[x][y] = count + '0';
}
(* win)++;
}
}
值得注意的是expand的win我们需要取地址,只有这样才能修改win的值,不然就会出现我们已经成功通关,但是游戏还是没有结束的情况。
关于随机设置雷的问题,我们其实在五子棋游戏中就有讲到,需要用到srand((unsigned int)time(NULLL))利用时间戳来得到随机值,头文件是#include<stdlib.h> #include<time.h>
初始化部分,我们首先需要两个二维数组,我们设置了两个define的常量一个是11 一个是9
这样做的原因是防止溢出,两个二维数组,一个是mine[][]也就是雷的数组,一个是show[][]也就是展示的数组,我们在初始化数组的时候,我们把雷的数组全部初始化为字符‘0’,把show[][]数组初始化*,这个时候为了方便初始化,所以我们在最后还新增了一个接受量,利用这个接受量就避免的写两个循环分别初始化这两个数组。
打印函数,我们打印了横坐标和纵坐标的标号,然后我们使用一个循环嵌套打印数组。
设置雷函数,我们使用rand随机数设置雷的数量,然后我们雷的值赋值成1,然后把值赋值给我们的mine[][]数组
找雷函数,首先为了防止我们排查过已经排查过的坐标,我们需要一个检查功能,如果不等于*那么就说明已经被排查过了,如果等于*说明还没有排查,然后我们需要实现一个显示周围8个格子雷的数量的功能,为了实现这个功能我们加入了get_mine_count函数,特别需要注意的是,我们使用都是字符,所以我们加和之后需要减去的是8*'0'才能得到对应的int值,int值变成对应的字符值也很简单只需要加上'0'就可以了
此时游戏已经可以初步玩了,但是如果是这样玩一把的失败率太高,时间也太久,所以我们还需要
一个expand函数用来展开,当周围没有雷的时候,直接自动排除,为了写出expand函数,我们需要使用递归,而且为了不出现排雷成功但是游戏还没结束的情况,我们需要使用&取地址符,只有这样我们的win++的值才会作用到比较的win上。
此时基本功能和函数都已经完善,可以正常的游戏啦!
3扫雷.c
最后是扫雷.c 其实我们在写代码的时候最先写的是扫雷.c 完善菜单和选择模块,然后我们才开始去思考需要什么函数,函数该如何实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("******************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("******************\n");
}
void game()
{
//1存放布置好的雷的信息,存放排查出的雷的信息,我们需要两个二维数组
//排查坐标的时候,为了防止坐标越界,我们给数组的行和列都增加2
//mine雷 show 默认希望show数组都是* 默认希望mine数组都是字符0
char mine[ROWS][COLS];
char show[ROWS][COLS];
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印棋盘
DisplayBoard(show, ROW, COL);
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);//这是游戏外挂,如果你注销该注释,即可拥有游戏外挂
//排雷
FindMine(mine,show,ROW,COL);
}
void test()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:>");
scanf ("%d", &input);
switch (input)
{
case 1:game();
break;
case 0:printf("退出游戏\n");
break;
default :
printf("输入错误,请重新输入\n");
}
} while (input);
}
int main(void)
{
test();
return 0;
}