目录
上次实现了三子棋小游戏,那这次就实现一个扫雷小游戏
分析
如图玩家排查了 5 ,4这个位置,显然没有雷,就将周围的八个位置的雷数统计到该位置上,如果踩到雷,挑战失败。
先给出 9*9 大小的棋盘,把字符 0放到每个位置上,之后开始布雷,布雷的地方把字符 1方放进去,将布雷的坐标记录下来,如果玩家没有踩到该布雷的位置,就统计周围八个位置的雷数。
那么就带了以下几个问题
1、如果统计的雷数为 1可能会和布雷的地方产生冲突
那我们就可以创建两个棋盘,一个展示给玩家、一个用来埋雷
2、给的数组大小为 9*9的 如果玩家排查边界的位置要怎么统计雷数,会产生越界
方法1、把边界和中间的坐标分开统计周围的位置的雷数(计算量太大,不推荐)
方法2、把数组上下左右个扩大一圈,埋雷的时候只在中间的位置埋雷,这样统计就比方法1 好一点。
通过分析我们就可以得到,创建棋盘大小比实际展示的棋盘大小大上一圈
用 show 数组向玩家展示棋盘,mine 数组用来埋雷,
为了页面美观先打印菜单,和三子棋的页面差别不太多
为了整洁创建三个文件,将和游戏有关的头文件和定义放到 game.h头文件中,游戏有关的函数放到game.c源文件中,测试游戏的相关代码放到test.c源文件中
菜单打印
#include"game.h"
void menu()
{
printf("***********************\n");
printf("***** 1. game *****\n");
printf("***** 0. exit *****\n");
printf("***********************\n");
}
void game()
{
printf("扫雷游戏\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请输入\n");
scanf("%d", &input);
switch (input)
{
case 1:game();
break;
case 0:printf("退出游戏\n");
break;
default:printf("输入错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
打印结果
实现
一、棋盘初始化
此时game.h的内容
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#include<stdio.h>
//初始化棋盘
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
borad[i][j] = ch;//由于初始化内容不同,要把初始化内容传过来
}
}
}
//显示棋盘
void DisPlayBorad(char borad[ROWS][COLS], int row, int col)
{
int i = 0;
printf("---------------扫雷-----------------\n");
for (i = 0; i <= row; i++)
{
printf("%d ", i);//打印横坐标
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印纵坐标
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", borad[i][j]);
}
printf("\n");
}
printf("---------------扫雷-----------------\n");
}
这里显示棋盘只需要显示 9*9的就行了,可以加上横纵坐标的打印
二、随机埋雷
下面开始埋雷,同样还是采用随机埋雷
使用函数 rand 函数,要在主函数里敲入srand((unsigned int)time(NULL);
需要的头文件stdlib和time
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = (rand() % row) + 1;
int y = (rand() % col) + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
EASY_COUNT为雷的个数,字符 1为雷
三、排查雷
- 玩家输入坐标,进行判断,是否排查过,是否在数组内
- 玩家踩到雷----->游戏结束
- 统计周围雷的个数------>游戏继续
- 排查完了------>游戏结束
展开一片(递归)
首先要先展开一片,那么这个坐标周围雷的个数要为 0,再判断该坐标在 shou 数组内是否为字符 *,这些都符合才可以递归,进去递归就要把 show 数组的该位置变为空格避免死递归,
void boom(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = 0;
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
count = Count_Mine(mine, x, y);
if (count != 0)
{
show[x][y] = count + '0';
}
else if(show[x][y]!=' ')
{
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++)
{
boom(mine, show, i, j);
}
}
}
else
{
return;
}
}
}
递归思想:判断该坐标是否在显示棋盘内,经过Count_Mine函数求出该坐标周围有没有雷,当该坐标周围没有雷的时候,并且该坐标没有被判断过;才可以递归
其中Count_Mine函数有两个方法
int get_mine(char mine[ROWS][COLS], int x, int y)
{
return mine[x][y + 1] + mine[x][y - 1] + mine[x - 1][y] + mine[x + 1][y]
+ mine[x + 1][y + 1] + mine[x - 1][y - 1] + mine[x + 1][y - 1] +
mine[x - 1][y + 1]-8*'0';
}
第二种
int Count_Mine(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] == '1')
count++;
}
}
return count;
}
判断玩家输入的坐标:
如果输入横坐标如果在 0到col之间,纵坐标在 0到row之间的话,在判读该坐标是否排查过,
当以上条件都满足时就可以递归了
找雷函数FineMine
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("请输入坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("您被炸死了\n");
DisPlayBorad(mine, ROW, COL);
break;
}
else
{
boom(mine, show, x, y);
DisPlayBorad(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法\n");
}
}
if (win == (row * col) - EASY_COUNT)
{
printf("你真是个排雷大师呢\n");
}
else
{
printf("下次加油吧\n");
}
}
注:最后要判断找到了的个数是否等于埋雷个数
因为当玩家被炸死时,程序也会来到这里
game.h头文件
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 80
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//初始化棋盘
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch);
//显示棋盘
void DisPlayBorad(char borad[ROWS][COLS], int row, int col);
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
test.c源文件
#include"game.h"
void menu()
{
printf("***********************\n");
printf("***** 1. game *****\n");
printf("***** 0. exit *****\n");
printf("***********************\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBorad(mine, ROWS, COLS, '0');
InitBorad(show, ROWS, COLS, '*');
DisPlayBorad(show, ROW, COL);
SetMine(mine, ROW, COL);
DisPlayBorad(mine, ROW, COL);
FindMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请输入\n");
scanf("%d", &input);
switch (input)
{
case 1:game();
break;
case 0:printf("退出游戏\n");
break;
default:printf("输入错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
game.c源文件
#include"game.h"
//初始化棋盘
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
borad[i][j] = ch;//由于初始化内容不同,要把初始化内容传过来
}
}
}
//显示棋盘
void DisPlayBorad(char borad[ROWS][COLS], int row, int col)
{
int i = 0;
printf("---------------扫雷-----------------\n");
for (i = 0; i <= row; i++)
{
printf("%d ", i);//打印横坐标
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印纵坐标
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", borad[i][j]);
}
printf("\n");
}
printf("---------------扫雷-----------------\n");
}
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = (rand() % row) + 1;
int y = (rand() % col) + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
void boom(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = 0;
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
count = Count_Mine(mine, x, y);
if (count != 0)
{
show[x][y] = count + '0';
}
else if(show[x][y]!=' ')
{
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++)
{
boom(mine, show, i, j);
}
}
}
else
{
return;
}
}
}
//int get_mine(char mine[ROWS][COLS], int x, int y)
//{
// return mine[x][y + 1] + mine[x][y - 1] + mine[x - 1][y] + mine[x + 1][y]
// + mine[x + 1][y + 1] + mine[x - 1][y - 1] + mine[x + 1][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("请输入坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("您被炸死了\n");
DisPlayBorad(mine, ROW, COL);
break;
}
else
{
boom(mine, show, x, y);
DisPlayBorad(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法\n");
}
}
if (win == (row * col) - EASY_COUNT)
{
printf("你真是个排雷大师呢\n");
}
else
{
printf("下次加油吧\n");
}
}
下期:函数栈帧的创建和销毁