前言
- 🚄 输入是学习的本质,输出是学习的手段。
- 🔖 分享每一次的学习,期待你我都有收获。
- 🎇 欢迎🔎关注👍点赞⭐️收藏✉评论,共同进步!
- 🌊 “要足够优秀,才能接住上天给的惊喜和机会”
- 💬 博主水平有限,如若有误,请多指正,万分感谢!
主要讲一下思想和一些注意点。
扫雷是什么,相信大家没玩过也听过,我就不过多赘述了。
这是一个扫雷界面。
要实现扫雷,需要分为以下步骤:
- 创造一个这样的棋盘
- 布置雷
- 扫雷
☁️1. 创造棋盘,假设我们需要的是9*9的棋盘。
当我们选中一个位置时,如果这个位置不是雷,那么游戏就应该给我们一个反馈,告诉我们周围一圈8个位置有几个雷。
就像这样:
这就表明周围这一圈里有3个雷。如果恰好我们选的位置就是雷,那么我们就被炸死了,游戏就直接结束。
这里就面临一个问题了,如果我选的是中间的位置,那周围有8个位置可以反馈,如果我选的是边缘呢?
显然这已经超出数组范围,越界了。因此,我们不妨再创造这样一个棋盘时,就将范围扩大些。
比如我们要的是一个9*9的棋盘,那么我们就将范围扩大到11 * 11(上下、左右各增加一行一列。绿色为扩大后的范围),只要不在扩大的那部分中埋雷,那么显示出来的数字也只会是9 * 9 这个区间中雷的个数,如图所示:
(假设 * 表示雷)
- 创建数组——大小确定
//用define更方便调整游戏棋盘大小
//实际游戏范围
#define ROW 9
#define COL 9
//扩大的范围
#define ROWS 11
#define COLS 11
☁️2.如何设置雷,如何设置反馈?
有些老铁觉得可以在一个棋盘里
用1和0分别表示雷和非雷
就像这样:(1表示雷 ,0表示非雷)
但是这样做会和反馈周围雷数的1产生二义性,因此不合适。
又有些老铁认为可以用*表示雷,#表示非雷,但是这样不够方便,没有用0和1表示来得方便。为什么不够方便一会儿就知道了,当然如果非要这样实现也是可以的。只是判断的时候会比较复杂一点。
既然我们既要用1和0表示雷和非雷,又不能与反馈的数字产生二义性,那么我们不妨再开一个等大的棋盘,这样,一个棋盘用来布置雷,但是不展示给玩家,另一个棋盘则作为反馈信息的棋盘,展示给玩家。
一开始我们先把布置雷的棋盘初始化,全置为0(非雷),以后再随机埋雷。
将反馈展示给玩家的棋盘全置为 * ,就是一个没有动过的棋盘
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
char mine[ROWS][COLS]; //雷盘
char show[ROWS][COLS];//反馈盘
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0'); //0非雷
InitBoard(show, ROWS, COLS, '*'); //全置 *
效果如下(对比):
然后是布置雷:
#define Count 10 //布置10个雷
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = Count;
while (count)
{
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (mine[x][y] == '0') //如果这个位置不是雷
{
mine[x][y] = '1';
count--;
}
//如果这个位置已经是雷,就不会再重复布置。
}
}
布置完雷后,我们可以看一下效果:
- 布置雷盘和反馈盘
☁️3. 扫雷,何时结束,如何反馈。
int Show(char mine[ROW][COL], int x, int y)
{
return mine[x - 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1];
}
//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
int x, y;
int endcount = ROW * COL - Count; //非雷的个数,用于标记
int count = Count;//雷的个数
//程序结束有两种情况:
//1.被炸死
//2.非雷点全走过了,只剩下雷点,排雷成功
do
{
if (endcount == 0) //非雷点为0
{
printf("已无雷区\n");
break;
}
printf("请输入坐标:\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
{
if (mine[x][y] == '1')
{
printf("你被炸死了\n");
//展开整个图
PrintBoard(mine, ROW, COL);
break;
}
//否则提示该坐标周围有多少雷
else
{
int showcount = Show(mine, x, y);
show[x][y] = showcount + '0';//数字转字符
PrintBoard(show, ROW, COL);
}
}
}
else
{
printf("坐标错误,请重新输入");
continue;
}
} while (1);
}
解释一下Show函数:
在ASCII表中,我们可以观察到,
‘0’-‘0’=0 ( 字符0-字符0等于 数字0 )
而其他任意数字字符减去字符0可以得到该数,比如
‘5’-‘0’= 5
因此 Show函数将周围八个字符(不是0就是1)
全加起来,再减去8个‘0’。就能得到一个数字,
该数字就表明周围有几个1。
这也就解释了为什么说用1和0表示雷和非雷方便。
最后再解释一下这行代码:
show[x][y] = showcount + '0';//数字转字符
因为show数组保存的是字符,无法完全接收这个返回来的整型数字。
字符数 - ‘0’ = 数字 ——> 字符数 = 数字 + ‘0’
完整代码:
game.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//实际游戏范围
#define ROW 9
#define COL 9
//扩大的范围
#define ROWS 11
#define COLS 11
#define Count 10; //雷的个数
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char set);
//打印棋盘
void PrintBoard(char board[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]);
game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
for (int i = 0; i <= row; i++)
{
printf("%d ", i);
}
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");
}
}
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = Count;
while (count)
{
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
int Show(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 + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1] - 8 * '0';
}
//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
int x, y;
int endcount = ROW * COL - Count; //非雷的个数,用于标记
int count = Count;//雷的个数
//程序结束有两种情况:
//1.被炸死
//2.非雷点全走过了,只剩下雷点,排雷成功
do
{
if (endcount == 0) //非雷点为0
{
printf("已无雷区\n");
break;
}
printf("请输入坐标:\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
{
if (mine[x][y] == '1')
{
printf("你被炸死了\n");
//展开整个图
PrintBoard(mine, ROW, COL);
break;
}
//否则提示该坐标周围有多少雷
else
{
endcount--; //没找到一个非雷点,非雷数减一,当非雷数为0 ,说明排雷成功,游戏结束
int showcount = Show(mine, x, y);
show[x][y] = showcount + '0';
PrintBoard(show, ROW, COL);
}
}
}
else
{
printf("坐标错误,请重新输入");
continue;
}
} while (1);
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
#include<time.h>
void game()
{
srand((unsigned int)time(NULL));
char mine[ROWS][COLS]; //雷盘
char show[ROWS][COLS];//反馈盘
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0'); //0非雷
InitBoard(show, ROWS, COLS, '*');
//布置雷
SetMine(mine, ROW, COL);
PrintBoard(show, ROW, COL);
//扫雷
int x, y;
FindMine(mine, show);
}
int main()
{
game();
}