一、优化内容的理解
在前篇的基础上做一下两点扩展和一些细节优化
1.第一次点击,不炸死(如果哪个倒霉蛋第一下就点到了雷,我得让电脑偷偷把这个雷挪走)
2.如果坐标周围没雷,可以实现展开。展开的意思:
上篇(一)中讲到,当我面对这样的一个雷阵时
输入一个坐标,该坐标不为雷,我应该通过该坐标展现出的数字知道该坐标周边八个位置的雷个数,
(实现过程见函数char geshu(char c[ROW][COL], int row, int col,int x,int y))
如图中数字3表示周边的这八个位置中有三个位置有雷
并且扫一个位置展开一大片
(实现过程见函数)int zhan_kai(char * c[ROW][COL], char* d[ROW][COL], int row, int col, int *x, int *y)
每个方向展开到某一坐标,该坐标周边雷数不为0时,停下来显示数字。
二、思路整理的实现过程
几点说明:
1.关于一个项目中的不同文件:
该扫雷项目中共有三个文件,game.h、game.c、test.c
game.h中存放的是之后主函数运行过程中所有要使用到的自定义函数的函数名,将需要引的头文件也一并放置。test.c和game.c在引用时只需在前加“#include”game.h””即可。
2.数组说明
(1)、char xian[ROW][COL]
(2)、char ying[ROE][COL]
(3)、实际行列
3.展开函数解析
递归思路解析
//计算ying数组中为0(即该点无雷)的位置周围的雷个数
char geshu(char c[ROW][COL], int row, int col,int x,int y)
{
int i = 0, j = 0;
int count = '0'; //定义一个计数器
for (i = x-1; i <=x+1; i++) //过一遍某一坐标周边八个位置,判断是否有雷,从而计算要赋值的个数
{
for (j = y-1; j <=y+1; j++)
{
if (c[i][j] == '1')
count ++;
}
}
return count;
}
//展开函数
int flag[ROW][COL] = { 0 }; //定义一个全局变量作用为标记,无需传参
int zhan_kai(char c[ROW][COL], char d[ROW][COL],int row, int col, int x, int y) //递归函数
{
if ((geshu(d, row, col, x, y) !='0')||(x==0||x==row+1||y==0||y==col+1)) //跳出条件
{
c[x][y] = geshu(d, row, col, x, y);
return 0;
}
else //从坐标处向外展开不为0停下
{
c[x][y] = '0';
if (flag[x][y] == 0)
{
flag[x][y] = 1; //标记已经展开过的坐标,避免重复展开造成死循环
zhan_kai(c, d, row, col, x - 1, y - 1); //递归实现
zhan_kai(c, d, row, col, x - 1, y);
zhan_kai(c, d, row, col, x - 1, y + 1);
zhan_kai(c, d, row, col, x, y - 1);
zhan_kai(c, d, row, col, x, y + 1);
zhan_kai(c, d, row, col, x + 1, y - 1);
zhan_kai(c, d, row, col, x + 1, y);
zhan_kai(c, d, row, col, x + 1, y + 1);
}
}
三、实现过程
//game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h> //输入输出的头文件
#include<stdlib.h>
#include<time.h> //埋雷函数中用到了time函数
#define ROW 11 //(一)篇中讲过定义数组行列个数的含义,此时定义的11导致玩家看到的雷阵为9*9。同如果把该位置的11改为6,将呈现出4*4的雷阵,以此类推。
#define COL 11
void menu(); //菜单函数
int choice(); //选择是否进入游戏的函数
void mai_lei(char d[ROW][COL], int row, int col);
void da_yin(char c[ROW][COL], int row, int col);
int suan_lei(char c[ROW][COL], int row, int col);
char geshu(char c[ROW][COL], int row, int col, int x, int y);
int zhan_kai(char *c[ROW][COL], char* d[ROW][COL], int row, int col, int *x, int *y);
//game.c
#include"game.h"
//先写菜单和选择
void menu() //打印菜单
{
printf("--------扫雷---------\n");
printf("| 1.play |\n");
printf("| 0.exit |\n");
printf("---------------------\n");
}
int choice() //从外部接收数字
{
int num = 0;
printf("请输入是否开始游戏~\n");
scanf("%d", &num);
switch (num)
{
case 1:
printf("开始游戏\n");
return 1;
break;
case 0:
printf("游戏已结束\n");
return 0;
break;
default:
printf("输入错误,请重新输入\n");
choice();
break;
}
}
//埋雷
void mai_lei(char d[ROW][COL], int row, int col)
{
int i = 0,j = 0,x = 0,y =0;
while (1)
{
x = rand() % (row - 1); //使生成a[1][1]~a[9][9]之间的数
y = rand() % (row - 1);
if (d[x][y] == '0')
{
d[x][y] = '1';
break;
}
}
}
void da_yin(char c[ROW][COL], int row, int col)
{
int i = 0, j = 0;
printf(" ");
for (j = 1; j < col - 1; j++)
{
printf(" %d ", j);
}
printf("\n");
for (i = 1; i < row-1; i++)
{
printf("%d",i);
for (j = 1; j < col-1; j++)
{
printf(" %c ",c[i][j]);
}
printf("\n");
}
printf("\n");
}
//统计剩余雷的个数
int suan_lei(char c[ROW][COL], int row, int col)
{
int i = 0, j = 0, count = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (c[i][j] == '*')
{
count++;
}
}
}
return count;
}
//关于展开,计算一个ying[]数组中为0的位置周围的雷个数
char geshu(char c[ROW][COL], int row, int col,int x,int y)
{
int i = 0, j = 0;
int count = '0';
for (i = x-1; i <=x+1; i++)
{
for (j = y-1; j <=y+1; j++)
{
if (c[i][j] == '1')
count ++;
}
}
return count;
}
int flag[ROW][COL] = { 0 };
int zhan_kai(char c[ROW][COL], char d[ROW][COL],int row, int col, int x, int y)
{
if ((geshu(d, row, col, x, y) !='0')||(x==0||x==row+1||y==0||y==col+1))
{
c[x][y] = geshu(d, row, col, x, y);
return 0;
}
else //从坐标处向外展开不为0停下
{
c[x][y] = '0';
if (flag[x][y] == 0)
{
flag[x][y] = 1;
zhan_kai(c, d, row, col, x - 1, y - 1);
zhan_kai(c, d, row, col, x - 1, y);
zhan_kai(c, d, row, col, x - 1, y + 1);
zhan_kai(c, d, row, col, x, y - 1);
zhan_kai(c, d, row, col, x, y + 1);
zhan_kai(c, d, row, col, x + 1, y - 1);
zhan_kai(c, d, row, col, x + 1, y);
zhan_kai(c, d, row, col, x + 1, y + 1);
}
}
}
//test.c扫雷
#include"game.h"
int main()
{
int i = 0, j = 0, row = ROW, col = COL, x = 0, y = 0;
char xian[ROW][COL];
char ying[ROW][COL];
srand((unsigned)time(NULL));
for (i = 1; i < row-1; i++)
{
for (j = 1; j < col-1; j++)
{
xian[i][j] = '*';
ying[i][j] = '0';
}
}
menu();
while (choice() == 1)
{
for (i = 0; i < row - 2; i++) //埋<=0颗<11-1雷
{
mai_lei(ying, row, col);
}
da_yin(ying, row, col);
da_yin(xian, row, col);
int i = 0;
while (1)
{
printf("请输入你要扫的坐标:\n");
scanf("%d %d", &x, &y);
if ((i == 0)&&(ying[x][y]== '1'))
{
ying[x][y] = '0';
ying[rand() % (row - 1)][rand() % (row - 1)] = '1';
da_yin(ying, row, col);
}
i++;
if (ying[x][y] == '1')
{
printf("恭喜你!你被炸死了\n");
system("pause");
return 0;
}
else
{
zhan_kai(xian, ying, row, col, x, y);
}
if ((suan_lei(xian, row, col)) == row - 2)
{
printf("恭喜你,你已获胜\n");
system("pause");
return 0;
}
da_yin(xian, row, col);
}
}
system("pause");
return 0;
}
四、效果演示
一、为了方便调试,我们将隐藏的埋好的雷阵打印出来
1、验证优化内容一,第一下不炸死。
故意踩一颗雷,并打印挪走第一下踩的雷之后的埋雷棋盘(方便调试,正式玩时将去掉)
2、验证展开
3、验证判断输赢
(1)、输
(2)、赢