使用控制台实现经典的扫雷游戏
游戏可以通过菜单实现继续玩或者退出游戏
扫雷的棋盘是9*9的格子
默认随机布置10个雷
可以排查雷
如果位置不是雷,就显示周围有几个雷
如果位置是雷,就炸死,游戏结束
把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束
扫雷过程中,布置的雷和排查出的雷的信息都需要储存,所以我们需要一定的数据结构来存储这些信息。
text.c//写游戏的测试逻辑
game.c//写游戏中函数的实现等
game.h//写游戏需要的数据结构类型和函数声明等
完成一个9*9的扫雷游戏,首先想到的是创建一个9*9的数组。但这里,还需要创建11*11的数组。理由如下:排查雷时,若排查的位置不是雷,就显示周围一圈(8个位置)有几个雷。而当该位置位于坐标的边界时,则会出现越界的情况。为了防止越界,在设计时,给数组扩大一圈,雷还是布置在9*9的坐标上,周围一圈不去布置雷,这样就解决了越界问题。
在棋盘上布置雷,符号‘1’为雷,符合‘0’为非雷。当我们排查雷时,若某位置不是雷,会返回该位置周围有几个雷,若有一个雷,则返回数字1。此时,若返回的数字1与符号‘1’(雷)在同一个棋盘上,玩家则无法区分它是数字1,还是符号‘1’。因此,需要两个棋盘来存放信息。
因此,需要在game.h文件中,定义行,列和10个雷,来让这3个文件都使用。只有其他两个文件都写上#include"game.h",在game.h文件中声明函数和数据结构类型都通用。
#include其余两个文件就不用加这个头文件了
#define ROW 9//行
#define COL 9//列
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//10个雷
设计一个扫雷游戏需要初始化棋盘,打印棋盘,布置雷,排查雷。
首先,建立text.c文件,通过菜单实现继续玩或者退出游戏,使用do while循环实现先选择后判断
(如果不太了解函数传参,可以看博主的这篇文章https://blog.csdn.net/wait___wait/article/details/135047014?spm=1001.2014.3001.5502ss)
(switch语句有些遗忘的,也可以看看博主这篇文章https://blog.csdn.net/wait___wait/article/details/134805824?spm=1001.2014.3001.5501)
#include"game.h"//自己定义的头文件用“”
void menu()
{
printf("*******************\n");
printf("******1.paly*******\n");
printf("******0.exit*******\n");
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()函数,因而在text.c(游戏的测试逻辑)这个文件中要进行定义。
根据对该游戏的分析和设计,知,game()中要创建两个数组来存放布置好的雷,和排查出雷的信息。
char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出雷的信息
text.c
数组是11*11,所需棋盘是9*9
//初始化棋盘 ,11*11数组
InitBoard(show, ROWS, COLS, '*');//show数组最开始全是‘*’
InitBoard(mine, ROWS, COLS, '0');//mine数组最开始全是‘0’
game.c
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 1; j < cols; j++)//j若初始化为0,后面排查雷时,输入的坐标在棋盘上输出的坐标会不一样
{
board[i][j] = set;
}
}
}
game.h
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘
运行示例图
text.c
show数组是11*11,但传过去的参数ROW,COL定义为9,因此打印的棋盘是9*9
//打印棋盘
PlayBoard(show, ROW, COL);
//PlayBoard(mine, ROW, COL);//检查‘0’是否设置好
game.h
void PlayBoard(char board[ROWS][COLS], int row, int col);//打印棋盘
game.c
//打印棋盘
void PlayBoard(char board[ROWS][COLS], int row, int col)
{
printf("------扫雷游戏-------\n");
for (int i = 0; i <= col; i++)//打印列号0-9
{
printf("%d|", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d|", i);//打印行号1-9
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
运行示例图
text.c
由于布置雷时需要产生随机数,就要用到rand函数。又因为调用rand函数之前要先调用srand函数,所以在该文件的main函数中使用srand函数,
srand((unsigned int)time(NULL));
//布置雷
SetMine(mine, ROW, COL);
//PlayBoard(mine, ROW, COL);//检查是否成功设置雷
game.h
#include//srand,rand的头文件
#include//time的头文件
void SetMine(char board[ROWS][COLS], int row, int col);//布置雷
game.c
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int count = EASY_COUNT;
while (count)
{
x=rand() % row + 1;//生成1-9的随机数
y=rand() % row + 1;//生成1-9的随机数
if (board[x][y] == '0')//判断该坐标是否是雷,防止重设置,导致雷的个数少于10
{
board[x][y] = '1';
count--;//每设置一个雷,count-1。直到10个雷设置完成,count=0,跳出while循环
}
}
}
运行实例,显示雷的位置需要把text.c文件中的PlayBoard取消注销
text.c
FindMine(show, mine, ROW, COL);
game.h
void FindMine(char show[ROWS][COLS], char[ROWS][COLS], int row, int col);//排雷
game.c
//排雷
//如果位置不是雷,就显示周围有几个雷
//如果位置是雷,就炸死,游戏结束
//把十个雷之外的所有雷都找出来,排雷成功,游戏结束
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int win = 0;//排查次数
while (win < row*col-EASY_COUNT)//确保win=71之后,不再继续,因为之后排查的一定是炸弹
{
printf("请输入你要排查的坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
PlayBoard(mine, row, col);
break;
}
else
{
int count = GetMineCount(mine, x, y);//统计雷坐标周围雷的个数
show[x][y] = count + '0';
PlayBoard(show, row, col);
win ++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == col * row - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
PlayBoard(mine, row, col);
}
printf("是否继续游戏?\n");
}
计算周围雷的个数
//计算周围雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
count += (mine[x][y] - '0');//mine[x][y]-'0'得到的是整数
}
}
return count;
}
mine数组里面的元素是字符型,‘0’也是字符,两个字符相减,得到的是整型数字。
text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*******************\n");
printf("******1.paly*******\n");
printf("******0.exit*******\n");
printf("*******************\n");
}
void game()
{
char show[ROWS][COLS] = { 0 };//全部设为‘*’
char mine[ROWS][COLS] = { 0 };//全部设为‘0’
//初始化棋盘
InitBoard(show, ROWS, COLS, '*');
InitBoard(mine, ROWS, COLS, '0');
//打印棋盘
PlayBoard(show, ROW, COL);
//PlayBoard(mine, ROW, COL);
//布置雷‘1'为雷,‘0’非雷
SetMine(mine, ROW, COL);
//PlayBoard(mine, ROW, COL);//检查是否成功设置雷
//排雷
FindMine(show, mine, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
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
#include
#include//srand,rand的头文件
#include//time的头文件
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//10个雷
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘
void PlayBoard(char board[ROWS][COLS], int row, int col);//打印棋盘
void SetMine(char board[ROWS][COLS], int row, int col);//布置雷
void FindMine(char show[ROWS][COLS], char[ROWS][COLS], int row, int col);//排雷
game.c
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘
void PlayBoard(char board[ROWS][COLS], int row, int col)
{
printf("------扫雷游戏-------\n");
for (int i = 0; i <= col; i++)//打印列号0-10
{
printf("%d|", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d|", i);//打印行号1-9
for (int j = 1; j <= col; j++)//j若初始化为0,棋盘就是9*10,而非9*9
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int count = EASY_COUNT;
while (count)
{
x=rand() % row + 1;//生成1-9的随机数
y=rand() % row + 1;//生成1-9的随机数
if (board[x][y] == '0')//判断该坐标是否是雷,防止重设置,导致雷的个数少于10
{
board[x][y] = '1';
count--;//每设置一个雷,count-1。直到10个雷设置完成,count=0,跳出while循环
}
}
}
//排雷
//如果位置不是雷,就显示周围有几个雷
//如果位置是雷,就炸死,游戏结束
//把十个雷之外的所有雷都找出来,排雷成功,游戏结束
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
count += (mine[x][y] - '0');
}
}
return count;
}
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int win = 0;
while (win < row*col-EASY_COUNT)
{
printf("请输入你要排查的坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
PlayBoard(mine, row, col);
break;
}
else
{
int count = GetMineCount(mine, x, y);//统计雷的坐标
show[x][y] = count + '0';
PlayBoard(show, row, col);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == col * row - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
PlayBoard(mine, row, col);
}
printf("是否继续游戏?\n");
}
运行示例
这个是因为打印棋盘时,game.c文件中,第三个for循环里,j初始化为0,导致输入(4,5)坐标,得到(4,6)坐标,所以,要注意自己变量赋值。(并且这个是9*10棋盘,不知道当时怎么没看出来,哈哈)
show,mine数组是11*11,但传过去的参数ROW,COL定义为9,因此打印的棋盘是9*9
建议写完一个模块,运行一个,好纠错。
欢迎斧正!!!