? 使?控制台实现经典的扫雷游戏
? 游戏可以通过菜单实现继续玩或者退出游戏
? 扫雷的棋盘是9*9的格?
? 默认随机布置10个雷
? 可以排查雷 ?
如果位置不是雷,就显?周围有?个雷 ? 如果位置是雷,就炸死游戏结束 ? 把除10个雷之外的所有?雷都找出来,排雷成功,游戏结束
? ?说到9*9的扫雷,首先浮入脑海的一定是一个9*9规格的棋盘,我们在这个棋盘上每排查一个坐标,如果是雷就结束游戏,不是雷的话就显示此坐标周围有几个雷。如图:
? 但是你有没有想过,计算机是如何得知周围有几个雷的呢?只靠在我们眼前的棋盘真的能做到吗?显然不能!所以要知道这里是有一个幕后棋盘的,这个幕后棋盘存放着计算机事先埋好的雷,如右图:
幕后棋盘不会显示出来,它被用于后台隐藏操作,在这里,存放地雷的位置被标为‘1’,安全区为‘0’。当我们输入某个坐标后,计算机会在幕后棋盘中统计周围雷的个数,然后将个数反映在“展示棋盘”对应的位置。
? 这里有一个问题,如果输入的是9*9棋盘边缘的坐标,那么在幕后棋盘那里执行统计操作时会出现数组访问越界的问题,我们采用的办法是将棋盘数组创建为11*11规格,也就是在9*9的基础上又套了一圈,这样就不会造成越界的问题啦~
为了方便说明,我们将幕后棋盘设为mine,展示棋盘为show,它们都是二维数组,同时为了保持神秘,show数组开始时初始化为字符 '*',为了保持两个数组的类型?致,可以使?同? 套函数处理,mine数组最开始也初始化为字符'0',布置雷改成'1'。
对应的数组应该是:
char mine[11][11];//存储雷的布置信息
char show[11][11];//存储雷的个数信息
由于该程序内容颇多,我们可以采用多文件来实现。这里我们设计三个文件:
1.test.c//文件中写游戏的测试逻辑
2.game.c//文件中写函数的实现(定义)
3.game.h//文件中写游戏需要的数据类型和函数声明等
我们做的是游戏程序,就要保证玩家能根据自己的意愿选择玩还是退出以及是否继续玩,代码如下:
test.c文件:
#include"game.h"
void menu()
{
puts("**********************");
puts("******1.play**********");
puts("******0.exit**********");
puts("**********************");
}
void game()
{
}
int main()
{
int input;
do
{
menu();//游戏菜单
printf("是否开始游戏?【1……play/0……exit】");
scanf("%d", &input);
switch (input)
{
case 1:
game();//游戏环节
break;
case 0:
puts("游戏结束,退出游戏");
break;
default:
puts("选择错误,请重新选择\a");
break;
}
} while (input);
return 0;
}
此框架我已在“猜数游戏”博客中详细说明过,有兴趣的小伙伴可以去看一下哦:【综合运用分支与循环结构、函数调用以及数组来完成猜数游戏(2)】-CSDN博客
有所不同的是由于我们用了多文件实现,所以我将包含stdio.h在内的头文件都放到了game.h头文件中,在test.c中声明一下“game.h”头文件就可以连同里面的各种头文件一起声明,注意我们自己的头文件在声明时应用“”括起来,而不是<>;
(1)创建两个二维棋盘数组,大小用宏定义表示,且宏定义也要在game.h中:
#pragma once
#include<stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
test.c:
//游戏环节
void game()
{
char mine[ROWS][COLS] = { 0 };//幕后棋盘
char show[ROWS][COLS] = { 0 };//展示棋盘
}
? 在埋雷和猜雷之前,mine棋盘都是安全区,show棋盘也全都是“未开发状态”,即mine数组元素都是‘0’,show数组全是‘*’;因此我们需要使用一个函数实现将数组全部初始化为我们想要的字符。
先在test.c中写上:
//游戏环节
void game()
{
?? ?char mine[ROWS][COLS] = { 0 };//幕后棋盘
?? ?char show[ROWS][COLS] = { 0 };//展示棋盘?? ?InitBoard(mine, ROWS, COLS,'0');//初始化棋盘
?? ?InitBoard(show, ROWS,COLS,'*');
}
在头文件game.h中加以声明:
#pragma once
#include<stdio.h>#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2void InitBoard(char board[ROWS][COLS], int rows, int cols,char ch);
然后在game.c中定义函数:
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char ch)
{
?? ?int i, j;
?? ?for (i = 0; i < rows; i++)
?? ?{
?? ??? ?for (j = 0; j < cols; j++)
?? ??? ?{
?? ??? ??? ?board[i][j] = ch;
?? ??? ?}
?? ?}
}
后续函数的写法皆是如此,后面不再赘述,只展示函数是如何实现的
void DisplayBoard(char board[ROWS][COLS], int row, int col)//传入ROW和COL
{
?? ?int i, j;
?? ?printf("------------------扫雷游戏-----------------------\n");
?? ?for (i = 0; i <= col; i++)
?? ?{
?? ??? ?printf(" %-d", i);
?? ?}
?? ?putchar('\n');
?? ?printf("---------------------------------------\n");?? ?for (i = 1; i <= row;i++)
?? ?{
?? ??? ?printf("%d |", i);
?? ??? ?for (j = 1; j <= col; j++)
?? ??? ?{
?? ??? ??? ?printf("%c ", board[i][j]);
?? ??? ?}
?? ??? ?putchar('\n');
?? ?}
}
由于显示的9*9棋盘,所以这里将ROW和COL传递给形参row和col;此时的效果图是这样的:
在game.h中宏定义NUMBER为雷的个数方便我们更改,暂定为10,参考代码:
void SetMine(char board[ROWS][COLS], int row, int col)
{
//布置10个雷
//用随机数布置雷
int count = NUMBER;//雷个数
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')//确保不重复埋雷
{
board[x][y] ='1';
count--;
}
}
}
void FindMine(char mine[][COLS], char show[][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - NUMBER)
{
printf("请输入坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
//猜到地雷
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!");
DisplayBoard(mine, ROW, COL);
break;
}
//若不是雷,则统计这个坐标周围有几个雷
else
{
int count = GetMineBoard(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
puts("该坐标已被排查过了,请重新选择");
}
}
else
{
printf("坐标非法,重新输入\n");
}
}
if (win == row * col - NUMBER)
{
printf("恭喜你,排雷成功!");
DisplayBoard(mine, ROW, COL);
}
}
由于呈现出来的是9*9宫格,所以我们输入坐标时也应限制在1到9之间,此外,还要考虑到重复猜到一个位置的情况,当满足这两个条件时,如果猜的坐标对应在mine上为‘1’,则为雷,游戏结束,并显示雷的分布情况。若不是雷,则应显示此坐标周围的雷的个数,这里用了NumberofMine函数来实现,因此接下来我们再对它来定义。
int NumberofMine(char mine[][COLS], int x, int y)
{
int i;
int sum = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
sum += (mine[i][j] - '0');
}
}
return sum;
}
? 虽然我们创建的两个数组都是11*11格式,但实际应用的只是9*9,所以大部分传参都传了ROW/COL。? 最终代码如下:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define NUMBER 10//雷的个数
void InitBoard(char board[ROWS][COLS], int rows, int cols,char ch);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char ch)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = ch;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)//传入ROW和COL
{
int i, j;
printf("------------------扫雷游戏-----------------------\n");
for (i = 0; i <= col; i++)
{
printf(" %-d", i);
}
putchar('\n');
printf("---------------------------------------\n");
for (i = 1; i <= row;i++)
{
printf("%d |", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
putchar('\n');
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
//布置10个雷
//用随机数布置雷
int count = NUMBER;//雷个数
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')//确保不重复埋雷
{
board[x][y] ='1';
count--;
}
}
}
int NumberofMine(char mine[][COLS], int x, int y)
{
int i;
int sum = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
sum += (mine[i][j] - '0');
}
}
return sum;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x, y;
int win = 0;
while (win < row * col - NUMBER)
{
printf("请输入要排查的坐标:");
scanf("%d%*c%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\a\a\n\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//若不是雷,统计周围的雷个数
int count = NumberofMine(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, row, col);
win++;
}
}
else
{
puts("该坐标已被排查过,请重新选择:");
}
}
else
{
puts("坐标越界,重新输入!");
}
}
if (win == row * col - NUMBER)
{
printf("恭喜你,排雷成功!\n\n");
}
}
#include"game.h"
void menu()
{
puts("**********************");
puts("******1.play**********");
puts("******0.exit**********");
puts("**********************");
}
//游戏环节
void game()
{
char mine[ROWS][COLS] = { 0 };//幕后棋盘
char show[ROWS][COLS] = { 0 };//展示棋盘
InitBoard(mine, ROWS, COLS,'0');//初始化棋盘
InitBoard(show, ROWS,COLS,'*');
//显示棋盘
DisplayBoard(show, ROW, COL);
//在mine中埋雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
srand(time(NULL));
int input;
do
{
menu();//游戏菜单
printf("是否开始游戏?【1……play/0……exit】");
scanf("%d", &input);
switch (input)
{
case 1:
game();//游戏环节
break;
case 0:
puts("游戏结束,退出游戏");
break;
default:
puts("选择错误,请重新选择\a");
break;
}
} while (input);
return 0;
}