目录
“
扫雷也是对C语言学习前期学习的一个很好的总结,其中包含了二维数组,循环,函数以及递归等等C语言知识点,完成这个小游戏,锻炼你的代码思维吧🤔
”
一、预览
1.测试文件(text.c)
#include"game.h"
void menu()
{
printf("***************************\n");
printf("******** 1.play *******\n");
printf("******** 0.exit *******\n");
printf("***************************\n");
}
void game()
{
char mineBoard[ROWS][COLS] = { '\0' }; //一个用于内置雷
char showBoard[ROWS][COLS] = { '\0' }; //一个用于玩家排雷,并打印
InitBoard(mineBoard, ROWS, COLS,'0'); //初始化两个棋盘
InitBoard(showBoard, ROWS, COLS,'*');
SetBoard(mineBoard, ROW, COL); //布置雷
DisplayBoard(showBoard, ROW, COL); //打印棋盘
FindMine(mineBoard, showBoard, ROW, COL); //排雷
}
void text()
{
srand((unsigned int)time(NULL)); //设置时间戳
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
printf("共有%d个雷\n",MODE_EAZY);
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,重新输入\n");
break;
}
} while (input);
}
int main()
{
system("color 0A"); //改变字体颜色
text();
return 0;
}
2.函数声明(game.h)
#include<stdio.h>
#include<stdlib.h> //改变字体颜色
#include<time.h> //生成随机数中time函数所用
#define ROW 9
#define COL 9
#define ROWS ROW+2 //防止越界
#define COLS COL+2
#define MODE_EAZY 10
void InitBoard(char Board[ROWS][COLS], int rows, int cols, int set); //初始化
void DisplayBoard(char Board[ROWS][COLS], int row, int col); //打印棋盘
void SetBoard(char Board[ROWS][COLS], int row, int col); //布置雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //排查雷
3.函数实现(game,c)
#include"game.h"
void InitBoard(char Board[ROWS][COLS], int rows, int cols, int set) //初始化棋盘
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board[i][j] = set;
}
}
}
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
printf("* * * * * * *排雷* * * * * * *\n\n");
for (int j = 0; j <= col; j++) //打印列数
{
printf("%d ", j);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i); //打印行数
for (int j = 1; j <= col; j++)
{
printf("%c ", Board[i][j]); //打印棋盘
}
printf("\n");
}
printf("\n* * * * * * *排雷* * * * * * *\n");
}
void SetBoard(char Board[ROWS][COLS], int row, int col)
{
int count = MODE_EAZY; //布置10颗雷
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (Board[x][y] != '1') //该位置已有雷,循环再随机生成一个坐标
{
Board[x][y] = '1';
count--;
}
}
}
int Count(char mine[ROWS][COLS], int x, int y) //计算(x,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_plus(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int*win)
{
if (mine[x][y] == '0') //若该坐标不是雷,则对计数win-1
*win-=1;
char sum = Count(mine, x, y) + '0'; //+'0' 将整型数字转换成字符型的数字
show[x][y] = sum;
if (show[x][y] == '0')
{
show[x][y] = ' '; //如果周围没有雷,则该坐标变为空格,并对其周围的坐标递归
}
if (show[x][y] == ' ') //递归条件:没有越界且递归过去的坐标没有被排过雷
{
if (x - 1 >= 1 && x - 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 && show[x - 1][y - 1] == '*')
FindMine_plus(mine, show, x - 1, y - 1, win);
if (x - 1 >= 1 && x - 1 <= 9 && y >= 1 && y <= 9 && show[x - 1][y] == '*')
FindMine_plus(mine, show, x - 1, y, win);
if (x - 1 >= 1 && x - 1 <= 9 && y + 1 >= 1 && y + 1 <= 9 && show[x - 1][y + 1] == '*' )
FindMine_plus(mine, show, x - 1, y + 1, win);
if (x >= 1 && x <= 9 && y - 1 >= 1 && y - 1 <= 9 && show[x][y - 1] == '*' )
FindMine_plus(mine, show, x, y - 1, win);
if (x >= 1 && x <= 9 && y + 1 >= 1 && y + 1 <= 9 && show[x][y + 1] == '*')
FindMine_plus(mine, show, x, y + 1, win);
if (x + 1 >= 1 && x + 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 && show[x + 1][y - 1] == '*')
FindMine_plus(mine, show, x + 1, y - 1, win);
if (x + 1 >= 1 && x + 1 <= 9 && y >= 1 && y <= 9 && show[x + 1][y] == '*')
FindMine_plus(mine, show, x + 1, y, win);
if (x + 1 >= 1 && x + 1 <= 9 && y + 1 >= 1 && y + 1 <= 9 && show[x + 1][y + 1] == '*')
FindMine_plus(mine, show, x + 1, y + 1, win);
}
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = row * col - MODE_EAZY; //win为非雷的数量
while (win)
{
printf("请依次输入行、列坐标排查雷:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //如果坐标合法,分三种情况
{
if (mine[x][y] == '1')
{
printf("你被炸死啦!\n"); //第一种:踩雷
DisplayBoard(mine, row, col);
break;
}
if (show[x][y] != '*') //第二种,该坐标以排过
{
printf("该坐标已被排查\n");
continue;
}
FindMine_plus(mine,show,x,y,&win); //第三种则是该坐标不是雷,并对其周围展开
DisplayBoard(show, row, col);
}
else
{
printf("坐标非法,重新输入\n");
}
}
if (win == 0) //win等于0表示剩下的坐标全为雷
{
printf("排雷成功!\n");
DisplayBoard(mine, row, col);
}
}
二、思路分析
这儿我把我自己分析的草稿分享给大家,希望对大家理解有帮助⬇️
1.打印菜单以及初始化棋盘
① 首先打印菜单,定义变量input,输入1表示进入游戏,输入0表示退出,输入其他则重新输入
② 用do-while循环,第一次不用判断直接进入循环,再用switch分支语句,根据input的值判断是开始游戏还是跳出循环,结束游戏
③ 我们需要初始化两个棋盘:
一个用于布置雷 —— 用 ‘ 1 ’ 表示雷,用 ‘ 0 ’ 表示非雷,便于之后坐标周围雷的计数统计(不用打印出来)
一个用于排查雷 —— 用 ‘ * ’ 表示暂未排查的坐标,用字符型的数字表示排查之后周围雷的个数;
2.布置雷
生成随机数 模(%)9 及得到 0 - 8 的数,再 +1 则课得到 1-9 的数作为坐标;
定义变量,值为雷的个数,每设置一个雷对其减一,注意坐标已有雷得情况
* 3.排查雷(递归展开)
要想获得文章开头排雷得效果,就得写出递归得代码
注意点:
① 当一个坐标周围没有雷时,选择递归周围的坐标,每次递归过去也相当于排查该点的雷,所以要对之前定义的变量win减一 ※需要传地址过去!!!
② 递归过去之后同样对周围的坐标统计雷的数量,如果还是没有雷继续递归周围坐标,这时需要特别注意的是:递归死循环导致栈溢出⬇️
为了避免上图情况,需要对递归加上判断条件 --> show[x - 1][y - 1] == ' * ' ,意思为递归的这个坐标之前没有排查过✔️
③ 递归还需要特别注意的一点:不能递归 9 × 9 棋盘之外的坐标(因为之前为了边缘坐标统计周围雷数,将两个二维数组的长宽均加了2)
所以加上判断条件 x - 1 >= 1 && x - 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 (以一条递归为例)
三、导出为 .exe
1. 将Debug转换成Release
2. 重新生成解决方案
生成 -> 重新生成解决方案
3. 找到.exe
将他复制粘贴给你的室友和老师吧!⬇️
Summery💐
扫雷可以写成没有递归的形式,但是没有展开的那种效果,扫雷的效率也就自然低了,这个扫雷游戏的难点就在于这个递归的实现,需要我们对各种情况的全面考虑!