今天晚上总算是把扫雷给写完了
深夜写代码,我也是要羽化成仙了。。。。
后面的一点一大片空白的递归思路我也会讲
总之先看吧
分三个模块
test.c 用来存放主函数 并调用自定义函数实现功能
game.h 用来存放头文件 预处理语句define定义 还有函数声明
(俗称 include大杂烩)
game.c 用来实现主函数要调用的自定义函数
那么开始
test.c
#include"game.h"
void game()
{
/*游戏函数中实现的函数分4个
1 构建 mine(布雷棋盘) show(排雷棋盘)
2 打印棋盘
3 布置雷
4 判断输赢,以及布雷棋盘对附近雷的数目显示*/
//定义棋盘数组
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//构建棋盘
initpoard(mine, ROWS, COLS, '0');
initpoard(show, ROWS, COLS, '*');
//打印棋盘
print_porad(mine, ROWS, COLS);
printf("\n");
print_porad(show, ROWS, COLS);
printf("\n");
//布雷
layout_mine(mine, ROWS, COLS);
//打印棋盘
print_porad(mine, ROWS, COLS);
printf("\n");
print_porad(show, ROWS, COLS);
printf("\n");
//进行扫雷,判断输赢
mine_clear(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int nut = 0;
do
{
printf("******************************************\n");
printf("*******1、开始游戏******0、退出游戏*******\n");
printf("******************************************\n");
scanf_s("%d", &nut);
switch (nut)
{
case 1:
printf("开始游戏\n");
game();//进入游戏函数
break;
case 0:
break;
default:
printf("输入错误,请重新输入\n");
}
} while (nut);
return 0;
}
game.h
#include<stdio.h>
#include <stdlib.h>
#include<time.h>
#define ROW 15
#define COL 15
#define ROWS ROW+2
#define COLS COL+2
#define DIFFICULTY 15
void initpoard(char arr[ROWS][COLS], int rows, int cols, char set);
void print_porad(char arr[ROWS][COLS], int rows, int cols);
void layout_mine(char mine[ROWS][COLS], int rows, int cols);
void mine_clear(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols);
game.c
#include"扫雷2game.h"
void initpoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
//构建棋盘
int i = 0; int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
arr[i][j] = set;
}
}
}
//打印棋盘
void print_porad(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0; int j = 0;
for (i = 0; i <=ROW; i++)
{
printf("%-2d ", i);
}
printf("\n");
for (i = 1; i <= ROW; i++)
{
printf("%-2d ", i);
for (j = 1; j <= COL; j++)
{
printf("%-2c ", arr[i][j]);
}
printf("\n");
}
}
//布雷
void layout_mine(char mine[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
int nut = DIFFICULTY;
while (nut)
{
i = rand() % ROW + 1;
j = rand() % COL + 1;
if (mine[i][j] == '0')
{
mine[i][j] = '1';
nut--;
}
}
}
void explore(char mine[ROWS][COLS], char show[ROWS][COLS], int i, int j)
{
if (show[i][j] == ' ' || ROW < i || i < 1 || COL < j || j < 1)
{
goto end;
}
char s = 0;
s = mine[i - 1][j - 1] +
mine[i - 1][j] +
mine[i - 1][j + 1] +
mine[i][j - 1] +
mine[i][j + 1] +
mine[i + 1][j - 1] +
mine[i + 1][j] +
mine[i + 1][j + 1] - 8 * '0';
show[i][j] = s + '0';
int p = i - 1;
int g = j - 1;
int maxi = i + 1;
int maxj = j + 1;
if (s + '0' == '0')
{
show[i][j] = ' ';
for (p = i - 1; p <= maxi; p++)
{
for (g = j - 1; g <= maxj; g++)
{
explore(mine, show, p, g);
}
}
}
//进行函数的递归 对周围没有雷的空地蔓延
end:
;
}
//进行扫雷,判断输赢
void mine_clear(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols)
{
int i = 0; int j = 0; int cont = 0;
while (ROW * COL - DIFFICULTY > cont)
{
scanf_s("%d%d", &i, &j);
if (1 <= i && i <= ROW && 1 <= j && j <= COL)
{
if (mine[i][j] == '1')
{
printf("很遗憾,你被炸死了\n");
break;
}
else
{
explore(mine, show, i, j);//探查当前位置周围还有没有雷,有几个
cont++;
}
//打印棋盘
print_porad(mine, ROWS, COLS);
printf("\n");
print_porad(show, ROWS, COLS);
printf("\n");
}
else
printf("输入错误,请重新输入\n");
}
if (ROW * COL - DIFFICULTY == cont)
printf("你赢了\n");
}
这是完整的代码
前面的就不详细讲了,因为现在已经很晚了。。。
来讲排雷函数吧
void explore(char mine[ROWS][COLS], char show[ROWS][COLS], int i, int j)
{
if (show[i][j] == ' ' || ROW < i || i < 1 || COL < j || j < 1)
{
goto end;
}
char s = 0;
s = mine[i - 1][j - 1] +
mine[i - 1][j] +
mine[i - 1][j + 1] +
mine[i][j - 1] +
mine[i][j + 1] +
mine[i + 1][j - 1] +
mine[i + 1][j] +
mine[i + 1][j + 1] - 8 * '0';
show[i][j] = s + '0';
int p = i - 1;
int g = j - 1;
int maxi = i + 1;
int maxj = j + 1;
if (s + '0' == '0')
{
show[i][j] = ' ';
for (p = i - 1; p <= maxi; p++)
{
for (g = j - 1; g <= maxj; g++)
{
explore(mine, show, p, g);
}
}
}
//进行函数的递归 对周围没有雷的空地蔓延
end:
;
}
[i - 1][j - 1] [i - 1][j + 1] [i - 1][j]
[i][j - 1] [i][j] [i][j + 1]
[i + 1][j - 1] [i + 1][j] [i + 1][j + 1]
当mine布置雷数组[ i ][ j ] 所在的位置 数值为‘0’的时候
说明周围的八个 位置肯定是没用雷的 也就是没用‘1’
我们的目的:由递归完成对周围的扫荡 如果扫荡的坐标周围的 八个位置中有雷,则停止继续递归,留下当前位置对检测到的地雷数量
讲的有点抽象
我们画图来看看
———>( )标示进入explore函数传参
[i-1][j-1]在传参之后 代替之前的 i j 成为传参后分支函数中的 i 与 j 再次进行一次如上所示的传参 也就是递归
检查周围是否有雷(字符‘1’)
运用算式
s 是char类型 mine数组也是char类型 对char类型的变量而言 进行操作符的运算时,char类型变量都会以ascii码值进行运算 和储存,打印则是以ascii码值对应的字符而打印
所以如果周围没有雷的话,此时的show【i】【j】就会等于‘0’字符零;有的话就会显示相对应个数的字符数字
当然如果递归中 再这个算式里算得不是字符‘0’ 递归就会停止(不再继续传参下去了)
函数就会返回上一层,进去另外的分支函数递归操作
递归的判断条件1 不是‘0’不给进
但是又有一个问题
当[i-1][j-1]这层再次进行递归时,原本的i 和j 所在的位置又要进行一次传参(图案如上所示)
这样的话不就重复了吗
那么就将已经传参过的mine[i][j]赋值为‘ ’(空格符)
当有一次重复传参到用过的 i 与 j
判断 mine[ i ][ j ]=‘ ’ 直接跳出函数结束,不传参了,不就行了
用goto语句到到函数末尾(当然在这里也不能让它越界了,所以要加上防止越界条件)
再运用||操作符,只要满足其中一个条件,直接pass
像不像生活中那些筛人的场景
只要有任何一方面不达标直接pass
不说了。。。都是泪