问题解决:
利用二维数组设计了一个扫雷游戏,通过这个游戏去进一步理解二维数组与函数之间的相互嵌套,有空也可以和舍友一起玩玩
游戏规则:先任意点开少量的方格,没有爆炸时,会出现一个数字,这个数字代表的意思是以它为中心的9个格子内所有雷的个数,等打开所有非雷区,你就赢啦!
目录
🎇 开始布置雷啦--(有关rand()及时间戳的概念)关于时间戳的知识:(如何让电脑输出一个1~100的随机数)http://t.csdn.cn/kdMLx
游戏设计思路解析 :
(1)需要有一个打印游戏界面的函数;
(2)需要有一个初始化雷阵的函数,以便玩家结束一场游戏后开始下一场游戏;
(3)当初始化雷阵之后,需要将预先设置的雷数随机的分布在雷阵中;
(4)由于扫雷面板给用户排雷时需要把雷隐藏起来,又需要记录雷的信息进行排雷判断,所以一个二维数组难以一起做到这些功能,所以创建了两个二维数组进行使用。
(5)由于创建了两个不同数组需要不同的初始化方式,所以初始化函数多加了一个参数,对不同的数组进行初始化。
(6)对扫雷面板行,列,雷的数量用#define的标识符常量进行定义,会方便整个程序的修改,测试。
(7)埋雷借助rand以及srand函数配合使用产生随机的坐标进行埋雷,确保坐标具有随机性。
需要写一个函数统计玩家所指定的坐标周围的雷的个数;
(8)通过一个递归函数实现将雷阵展开,即不是被数字或者雷环绕的坐标都自动展开。在这里,提醒读者一个地方,如果读者是和我的一样通过原来的坐标的基础上从八个方向依次判断递归,需要特别注意:通过传递坐标实现递归调用自身的过程中,需要将原来的坐标排除在外,否则会造成栈溢出,原因是两个坐标间的相互调用递归函数;对排雷时附近没有雷的坐标进行递归展开,分析得出递归需要满足这三个条件:
(1)、该坐标是不是雷;
(2)、该坐标附近8个坐标是不是都没有雷;
(3)、该坐标是否被排查过,这点需要特别注意,如果未进行判断标记,会使递归进入死递归,从而导致栈溢出,程序挂掉。
(9)需要统计一下剩余未展开的坐标数,以此来与所布的雷数对比;
()要有一个判断输赢的条件,如果剩余的坐标数与雷数相等,说明玩家排雷成功,否则继续排雷。
函数设计思路解析 :
1.先上传头文件-“标头.h”
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define row 15 //棋盘行大小
#define col 15 //棋盘列大小
#define rows row+2
#define cols col+2
#define dilei 25 //放置地雷个数
void InitBoard(char board[rows][cols],int Rows,int Cols,char set);
void Display(char board[rows][cols], int Rows, int Cols);
void SetMine(char board[rows][cols], int Rows, int Cols);
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 Row, int Col);
int Residue(char mine[rows][cols], char show[rows][cols]);
2.再上传两个源文件(名称可以任意)
菜单部分及函数功能介绍
#define _CRT_SECURE_NO_WARNINGS
#include"标头.h"
void menu()
{
printf("\n-----欢迎来到*扫雷游戏*---------\n");
printf("\n------------------------------\n");
printf("------- 1.进入游戏 ---------\n");
printf("------- 0.退出游戏 ---------\n");
printf("------------------------------\n");
}
void game()
{ //雷的信息存储
char mine[rows][cols] = { 0 }; // 1.布置好雷的信息
char show[rows][cols] = { 0 }; // 2.排查出雷的信息
InitBoard(mine, rows, cols, '0'); //初始化扫雷表盘
InitBoard(show, rows, cols, '*'); //初始化扫雷表盘
//Display(mine,row,col); //打印表盘
Display(show, row, col); //打印表盘
SetMine(mine, row, col); //布置雷
//Display(mine, row, col); //扫雷
FindMine(mine, show, row, col); //寻找坐标周围雷的个数
Expand(mine, show, row, col); //使用函数递归,使其可以展开
Residue(mine, show); //计算剩余多少个'*'
}
void test()
{
int input;
do
{
menu();
printf("请选择是否玩游戏:>");
scanf("%d", &input);
switch (input)
{
case 1:game();
break;
case 0:
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
}
int main()
{
test();
return 0;
}
函数具体实现游戏部分
#define _CRT_SECURE_NO_WARNINGS
#include"标头.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 Display(char board[rows][cols], int Rows, int Cols)
{
int i, j;
printf(" ");
for (j = 1; j <= col; j++)//打印列号
{
printf(" %2d", j);
}
printf("\n");
printf(" ");
for (i = 1; i < col + 1; ++i)
{
printf("===");
}
printf("==\n");
for (i = 1; i <= row; i++)
{
if(i<10)
printf(" %2d |", i);
else
printf(" %2d |", i);
for (j = 1; j <= col; j++)
{
printf(" %c ", board[i][j]);
if (j == col)
printf(" | ");
}
printf("\n");
}
}
void SetMine(char board[rows][cols], int Rows, int Cols)//设置雷
{
srand((unsigned int)time(NULL));
int count = dilei;
while(count)
{
int x = rand() % row + 1;//控制雷在表盘中
int y = rand() % col + 1;//x,y范围为1-9
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int GetMine(char mine[rows][cols],int x,int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y +1]
+ mine[x][y - 1] + mine[x][y +1] + mine[x + 1][y - 1]
+ mine[x + 1][y] + mine[x+ 1][y+ 1]-8*'0';
}
void FindMine(char mine[rows][cols], char show[rows][cols], int Row, int Col)
{
int x,y,count;
while (1)
{
printf("请输入排查雷的坐标:>");
scanf("%d%d", &x, &y);
if (show[x][y] == '*')
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '0')//不是雷
{//坐标合法
GetMine(mine, x, y);
Expand(mine,show, x, y);
show[x][y] = GetMine(mine, x, y) + '0';
printf("未展开坐标数:%d\n", Residue(mine,show));
if (Residue(mine, show) == dilei)
{
printf("恭喜您,取得了胜利!!!\n");
Display(mine, row, col);
break;
}
Display(show, row, col);
}
else
{
printf("很遗憾,你被炸死了!\n");
Display(mine, row, col);
break;
}
}
else
{
printf("输入坐标非法,请重新输入!\n");
}
}
else
printf("抱歉,这个坐标你已经输入过了\n");
}
}
void Expand(char mine[rows][cols], char show[rows][cols], int x, int y)
{
int count;
count = GetMine(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
if (show[x-1][y] == '*'&&x - 1 >= 1 && x - 1 <= row && y >= 1 && y <= col)
Expand(mine, show, x - 1, y);
if (show[x +1][y] == '*' && x +1 >= 1 && x + 1 <= row && y >= 1 && y <= col)
Expand(mine, show, x + 1, y);
if (show[x ][y-1] == '*' && x >= 1 && x <= row && y-1>= 1 && y-1 <= col)
Expand(mine, show, x, y-1);
if (show[x ][y+1] == '*' && x >= 1 && x <= row && y +1>= 1 && y +1<= col)
Expand(mine, show, x, y+1);
if (show[x + 1][y+1] == '*' && x + 1 >= 1 && x + 1 <= row && y +1>= 1 && y+1 <= col)
Expand(mine, show, x+1, y +1);
if (show[x +1][y-1] == '*' && x + 1 >= 1 && x + 1 <= row && y-1 >= 1 && y-1 <= col)
Expand(mine, show, x+1, y - 1);
if (show[x - 1][y+1] == '*' && x - 1 >= 1 && x - 1 <= row && y+1 >= 1 && y+1<= col)
Expand(mine, show, x-1, y + 1);
if (show[x - 1][y-1] == '*' && x - 1 >= 1 && x - 1 <= row && y-1 >= 1 && y-1 <= col)
Expand(mine, show, x-1, y - 1);
}
else
{
show[x][y] = count + '0';
}
}
int Residue(char mine[rows][cols], char show[rows][cols])
{
int count = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row; ++i)
{
for (j = 1; j <=col; ++j)
{
if (show[i][j] == '*')
count++;
}
}
return count;
}
函数设计思路解析 :
菜单循环函数---(打印游戏菜单)
void menu()
{
printf("\n-----欢迎来到*扫雷游戏*---------\n");
printf("\n------------------------------\n");
printf("------- 1.进入游戏 ---------\n");
printf("------- 0.退出游戏 ---------\n");
printf("------------------------------\n");
}
void test()
{
int input;
do
{
menu();
printf("请选择是否玩游戏:>");
scanf("%d", &input);
switch (input)
{
case 1:game();
break;
case 0:
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
}
初始化扫雷表盘函数--InitBoard()
InitBoard(mine, rows, cols, '0'); //初始化扫雷表盘
InitBoard(show, rows, cols, '*'); //初始化扫雷表盘
//其中'0','*'(实参)通过函数InitBoard()传入set(形参)实现一盘多用
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; //通过set实现一盘多用
}
}
}
显示棋盘函数(一个放置随机雷,一个隐藏雷)
Display(mine,row,col)
这个是放置随机雷的,一般不显示在屏幕上(直到你赢了或者被炸死才让你看雷的真实分布)
//Display(mine,row,col);
//这个是放置随机雷的,一般不显示在屏幕上
//(直到你赢了或者被炸死才让你看雷的真实分布)
Display(show, row, col); //打印表盘,显示在屏幕上
#define row 15 //棋盘行大小
#define col 15 //棋盘列大小
#define rows row+2
#define cols col+2
#define dilei 25 //放置地雷个数
void InitBoard(char board[rows][cols],int Rows,int Cols,char set);
void Display(char board[rows][cols], int Rows, int Cols);
void SetMine(char board[rows][cols], int Rows, int Cols);
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 Row, int Col);
注意:打印棋盘是(row+2)X(col+2)的棋盘,因为需要判断计算该点(x,y)周围8个点所含雷的个数,但是实际显示的棋盘大小为(row)X(col)的规格。只需(row+2)X(col+2)棋盘最外围全为‘0’即可(但是最外围一行列不打印)
void Display(char board[rows][cols], int Rows, int Cols)
{
int i, j;
printf(" ");
for (j = 1; j <= col; j++)//打印列号
{
printf(" %2d", j);
}
printf("\n");
printf(" ");
for (i = 1; i < col + 1; ++i)
{
printf("===");
}
printf("==\n");
for (i = 1; i <= row; i++)
{
if(i<10)
printf(" %2d |", i);
else
printf(" %2d |", i);
for (j = 1; j <= col; j++)
{
printf(" %c ", board[i][j]);
if (j == col)
printf(" | ");
}
printf("\n");
}
}
开始布置雷啦--(有关rand()及时间戳的概念)关于时间戳的知识:(如何让电脑输出一个1~100的随机数)http://t.csdn.cn/kdMLx
void SetMine(char board[rows][cols], int Rows, int Cols)//设置雷
{
srand((unsigned int)time(NULL));
int count = dilei;
while(count)
{
int x = rand() % row + 1;//控制雷在表盘中
int y = rand() % col + 1;//x,y范围为1-9
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
寻找坐标周围雷的个数:
int GetMine(char mine[rows][cols],int x,int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y +1]
+ mine[x][y - 1] + mine[x][y +1] + mine[x + 1][y - 1]
+ mine[x + 1][y] + mine[x+ 1][y+ 1]-8*'0';
}
void FindMine(char mine[rows][cols], char show[rows][cols], int Row, int Col)
{
int x,y,count;
count = row * col - dilei;
while (1)
{
printf("请输入排查雷的坐标:>");
scanf("%d%d", &x, &y);
if (show[x][y] == '*')
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '0')//不是雷
{//坐标合法
GetMine(mine, x, y);
Expand(mine,show, x, y);
show[x][y] = GetMine(mine, x, y) + '0';
--count;//计算剩余多少个安全点
printf("还有%d个安全点,你将取得胜利!\n", count);
Display(show, row, col);
}
else
{
printf("很遗憾,你被炸死了!\n");
Display(mine, row, col);
break;
}
}
else
{
printf("输入坐标非法,请重新输入!\n");
}
}
else
printf("抱歉,这个坐标你已经输入过了\n");
}
}
使用函数递归,使其可以展开
void Expand(char mine[rows][cols], char show[rows][cols], int x, int y)
{
int count;
count = GetMine(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
if (show[x-1][y] == '*'&&x - 1 >= 1 && x - 1 <= row && y >= 1 && y <= col)
Expand(mine, show, x - 1, y);
if (show[x +1][y] == '*' && x +1 >= 1 && x + 1 <= row && y >= 1 && y <= col)
Expand(mine, show, x + 1, y);
if (show[x ][y-1] == '*' && x >= 1 && x <= row && y-1>= 1 && y-1 <= col)
Expand(mine, show, x, y-1);
if (show[x ][y+1] == '*' && x >= 1 && x <= row && y +1>= 1 && y +1<= col)
Expand(mine, show, x, y+1);
if (show[x + 1][y+1] == '*' && x + 1 >= 1 && x + 1 <= row && y +1>= 1 && y+1 <= col)
Expand(mine, show, x+1, y +1);
if (show[x +1][y-1] == '*' && x + 1 >= 1 && x + 1 <= row && y-1 >= 1 && y-1 <= col)
Expand(mine, show, x+1, y - 1);
if (show[x - 1][y+1] == '*' && x - 1 >= 1 && x - 1 <= row && y+1 >= 1 && y+1<= col)
Expand(mine, show, x-1, y + 1);
if (show[x - 1][y-1] == '*' && x - 1 >= 1 && x - 1 <= row && y-1 >= 1 && y-1 <= col)
Expand(mine, show, x-1, y - 1);
}
else
{
show[x][y] = count + '0';
}
}