原文地址:https://beryl-licorice-3a8.notion.site/5421ccb8a34c4ebbb185b6b70e0ba9b9?pvs=4
玩家点击一个方格后,方格会显示以下几种情况:一个数字,表示周围八个方格中隐藏的地雷数量;一个空白,表示周围八个方格内没有地雷;或者一个地雷,表示玩家触发了地雷,游戏失败。
玩家需要通过解读数字的线索,推理出哪些方格安全(非雷区域),哪些方格可能藏有地雷。安全的方格点击后会揭示更多信息,有助于玩家找出其他的安全方格。
如果玩家成功找出所有的安全方格,而没有触发任何地雷,那么游戏胜利
扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要一定的数据结构来存储这些信息:我们用1存放雷,用0存放正常块。
1.1数据结构的分析:
首先我们用已知的知识进行分析,布置的雷和排查的雷都需要存储,所以我们需要一定的数据结构来存储这些信息。迅速反应过来-数组!
如何实现排雷的算法呢?根据规则,当我们点开一个块后,它现实的数字告诉我们它周围有多少个雷,因此我们需要获取以它为中心的信息:
这又会导致一个新的问题:如何防止数组越界访问?根据已有的知识,我觉得再套一层都为0的外壳就能完美解决问题1
继续分析,我们在棋盘上布置了雷,棋盘上雷的信息(1)和⾮雷的信息(0),假设我们排查了某⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪⾥呢?答案是数组
解决方案:我们采取两个数组存储信息,一个存取雷,一个存取排查出的信息
1.2文件结构设计
这么多行代码放在同一个文件中肯定是很臃肿的,我们使用三个文件来实现程序。多文件组织形式随代码实战讲述。
1.3注意事项:
存放数据的类型太多,容易产生歧义
在统计一个坐标周围的雷的个数的时候,可能会越界
用两个数组,一个存放雷的信息,一个存放排查雷的信息
char show[11][11];存放’0’
char mine[11][11];存放’*’
2.1建立文件:
快速补充多文件知识
⼀般情况下,函数的声明、类型的声明放在头⽂件(.h)中,函数的实现是放在源⽂件(.c)⽂件中。如下:
add.c
int Add(int x,int y)
{ return x+y:
}//定义函数
add.h 将这个文件放在头文件中,其余都是源文件
int Add(int x,int y);//函数的声明
test.c
#include
#include"add.h"//自己定义的头文件特殊写法
int main(){
int a=10; int b=20;
int c = Add(a,b)//函数调用
运行结果:
2.2封装界面
void menu() {
printf("********************");
printf("*********1go********");
printf("*********2out*******");
printf("********************");
}
void game() {
}
int main()
{
int input = 0;
do {//使用do-while循环的原因是要先进入再作循环,避免输入0无操作情况
menu();
printf("请输入");
scanf("%d", &input);
switch (input) {
case 1:
printf("扫雷");
game();//封装函数
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("非法输入,请重新选择");
break;
}
} while (input);
return 0;
}
初始化棋盘:使用宏定义的好处之一就是便于简化和理解代码
由上文,我们需要:
#define ROW 9 #define COL 9
#define ROWS ROW+2 #define COLS COL+2
#include
#include"game.h"
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set) {
int i = 0;//行
for(i=0;i<rows;i++){
int j = 0;
for (j = 0; j < cols; j++) {
arr[i][j] = set;
}
}
}
void DisplayBoard(char arr[ROWS][COLS],int row,int col) {
int i = 1;
for (i = 1; i <= row; i++) {
int j = 0;
for (j = 1; j < col; j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("\n");
}
我们解决了数组访问越界的问题,同时我们要注意为了节省代码,我们尽量要让一个函数实现更大的用处。
头文件:
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set);
void DisplayBoard(char arr[ROWS][COLS],int row,int col);
函数构造:
void game() {
char mine[ROWS][COLS];
char show[ROWS][COLS];
//初始化棋盘
InitBoard(mine, ROWS, COLS,'0');//'0'
InitBoard(show, ROWS, COLS,'*');//'*'
//打印棋盘
DisplayBoard(show,ROW,COL);
}
效果图:
2.3建立坐标系
我们如何才能获取用户输入的合法数据呢?我们可以建立坐标系,让用户输入列坐标和行坐标来获取信息。
void DisplayBoard(char arr[ROWS][COLS],int row,int col) {
int i = 1;
//先打印行坐标:
for (i = 0; i <= row; i++) {// i=0开始,使得棋盘符合实际情况
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
int j = 0;
for (j = 1; j <= col; j++) {
if (1 == j) printf("%d ", i);//打印列坐标
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("\n");
}
为什么要先打印行坐标?因为按照打印顺序,如果先打印列坐标,行坐标就会在下面了。
我把打印坐标的代码单列出来,为了代码对齐和美观,我们注意:
for (i = 0; i <= row; i++) {// i=0开始,使得棋盘符合实际情况
printf("%d ", i);
}
for (j = 1; j <= col; j++) {
if (1 == j) printf("%d ", i);//打印列坐标
2.4实现游戏操作
2.4.1布置雷
setmine(mine, ROW, COL);//在test.c中的void game中进行函数的使用
void setmine(char arr[ROWS][COLS], int row, int col);函数声明
void setmine(char arr[ROWS][COLS], int row, int col) {
//随机布置10个雷
int count = 10;
while (count) {
int x = rand() % COL + 1;
int y = rand() % ROW + 1;
if (arr[x][y] == '0') {
arr[x][y] = '1';
}
else count++;
count--;
}
}//函数的实现注意
我们将1当作雷存放在0的表格中。
这里我要提醒几点:
现在test.c中设立函数并放置game()函数中
再在game.h中完成函数的声明
最后在game.c文件中完成函数的实现
2.4.2排查雷
在show数组中验证信息,在mine数组中完成排雷。
finedmind(mine, show, ROW, COL);
由此建立函数。
void finemine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
建立函数声明
建立函数实现:
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 - 10) {//因为排查71次!
printf("请输入要排查的坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (mine[x][y] == '1')
{
printf("you lose!");
}
else
{
int n = getminecount(mine, x, y);
show[x][y] = n + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else printf("非法坐标,请重新输入\n");
}
}
//嵌套子函数形式:
int getminecount(char arr[ROWS][COLS], int x, int 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';}
至此我们的程序完成。
game.c
game.h
test.c