目录
声明:博主使用的VS2022集成开发环境
1.思路分析
1.1 扫雷游戏的介绍
扫雷游戏相信大家都玩过,就是左图这样的形式,通过排查雷的坐标来获得胜利,左侧的扫雷就比较高级些,目前博主技术有限,就先介绍下通过控制台实现的简易版扫雷。
1.2 扫雷游戏的思路分析
扫雷游戏就相当于一个9*9的二维数组,而我们可以通过创建数组来实现扫雷游戏的棋盘,那么,如何表示是雷还是不是雷,这里博主用 0 表示非雷,用1 表示雷,如果,选择一个位置,就要在该位置上显示周围九宫格里有几个雷。
如上面左图所示,如果我们想要求红色区域周围九宫格的雷的数量的话,就会产生越界,那么如何解决呢,我们可以像右图所示一样创建一个11*11的九宫格,周围都不是雷,用0代替,那么9*9的范围内任意一个格子都能求得周围九宫格里有几个雷了。
左图黄色区域九宫格内有一个1,也就是一个雷,那么中心数字应该变成1,但是这样的话就没办法区分这个1 表示的是该位置是雷,还是说周围九宫格内有一个雷,就会分不清,那么,我们就想到了,可以再创建一个数组,第一个数组用来存放雷,第二个数组用来存放选择排雷的位置周围九宫格内有几个雷。这样就解决了这个问题。
我们就可以创建两个数组,第一个数组是存放地雷的地方,第二个数组是存放该位置周围九个位置有几个雷,当然默认情况下我们是看不见那里是雷,哪里不是雷,因此我们可以将第二个数组用字符 ' * ' 初始化,那么第一个表可以将数字1和0也换成字符类型,方便处理,这样两个表的设定就好了,如下图:
左边图形代表埋的雷的9*9数组,右边则根据左侧的数组来统计玩家选择的位置周围有几个雷,显示在右侧棋盘中,左侧棋盘是不会展示给玩家看的,玩家会通过右侧棋盘来进行游戏的实现,直到雷全部排完获胜,或者排到了雷被炸死,重新进行游戏。
2.扫雷游戏代码实现思路
扫雷游戏的代码大概有快200行,如果都写在一个文件里,不太好进行编程,后期找bug也不太容易,后期修改也不容易找到想要修改的位置。因此我们可以创建三个文件来实现扫雷游戏的代码,game.h文件用来对用于游戏的自定义函数的声明,game.c用来实现用于游戏的自定义函数的定义,test.c文件用来放代码的主体main函数并且使用用于游戏的自定义的函数,这样就不会导致代码堆在一个文件里面。
首先创建test.c,game.c,game.h三个文件(文件名可以根据自己的习惯来命名)。
2.1 编写主函数并创建菜单功能
一般游戏的开始都会有一个菜单,我们就创建一个menu()函数作为菜单,1.play或者2.exit两种功能,当然我们不可能只玩一次就自动退出游戏了,可以选择多次进行游戏,自行来选择退出游戏,这里就用到了do-while循环,当然为防止输入错误(输入的不是整数)导致死循环,这里我还用到了while(getchar() != '\n'; 来清空缓存区,重新输入。
如果不理解博主为什么要写while(getchar() != '\n'; ,可以参看博主的另一篇博客:
//在 test.c 文件中
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu()
{
printf("\n");
printf("1.play\n");
printf("2.exit\n");
printf("\n");
}
int main()
{
int input = 1;
do
{
menu();
printf("请选择->\n");
//防止输入的不是数字,导致死循环,用getchar()清空缓冲区,重新输入数字。
if (scanf("%d", &input) != 1)
{
printf("输入错误,请输入数字!!!\n");
while (getchar() != '\n');
continue;
}
switch (input)
{
case 1:
{
//game();
break;
}
case 0:
{
printf("退出游戏!!!\n");
break;
}
default:
{
printf("输入错误,请重新输入!!!\n");
break;
}
}
} while (input);
return 0;
}
2.2 扫雷游戏的游戏部分
接下来就该实现扫雷游戏的主体部分,这里我自定义的game();函数来实现扫雷游戏的内容。
2.2.1 创建扫雷棋盘
这里我们就可以创建两个11*11的数组作为扫雷的棋盘,实际上是9*9的棋盘,(外面的一圈是防止后面计算九宫格有几个雷时超出范围)。/
//在 test.c 文件中
void game()
{
char mine[11][11] = { 0 };
char show[11][11] = { 0 };
}
但如果你后面想要修改棋盘大小的话,就需要一个一个的修改数组的大小,太麻烦了,这里介绍一种方法,在game.h文件里,定义行和列的常量,后面就用定义的常量来代替数字。ROW和COL表示的是9*9,ROWS和COLS表示的是11*11。这四个值都是定义的常量。
//在 game.h 文件中
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
上面的代码是在game.h 头文件中,test.c文件想要使用就需要包含头文件 ,而自定义的头文件包含时应该这样使用:#include "game.h" 用双引号包含头文件。那我们就可以把test.c和game.c需要的头文件都放到game.h头文件中,然后test.c和game.c只需要包含game.h头文件即可。(包含头文件本质就是将头文件里的内容复制一份过来)。
//在 test.c 文件中
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
}
2.2.2 初始化扫雷棋盘
我们需要初始化两张棋盘,一张是11*11的全为‘0’ 的无雷棋盘,另一张是11*11的全为‘*’的隐藏棋盘。这里我传递了四个参数,数组,行,列,初始化字符。
//在 test.c 文件中
void play()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
}
下面是在game.h 文件中声明初始化棋盘函数的。
//在 game.h 文件中
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define AMOUNT 20
//初始化棋盘
void InitBoard(char me[ROWS][COLS], int rows, int cols, char a);
下面是在game.c 文件中定义初始化棋盘函数的。
// 在 game.c 文件中
void InitBoard(char me[ROWS][COLS], int rows, int cols, char a)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
me[i][j] = a;
}
}
}
2.2.3 埋雷
我们需要对第一张棋盘进行随机埋雷,这里要用到srand和 rand函数,下面这篇博客有所介绍 :srand和rand函数介绍
//在 test.c 文件中
void play()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//埋雷
SetMine(mine, ROW, COL);
}
下面是在game.h 文件中声明埋雷函数的。
//在 game.h 文件中
//埋雷
void DisplayBoard(char me[ROWS][COLS], int row, int col);
下面是在game.h 文件中定义埋雷函数的。
//在 game.c 文件中
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int i = 0;
int count = AMOUNT;
while (count > 0)
{
int a = rand() % row + 1;
int b = rand() % col + 1;
if (mine[a][b] == '0')
{
mine[a][b] = '1';
count--;
}
}
}
2.2.4 打印棋盘
这里我们只需要打印第二个棋盘给用户看就行,第一个棋盘显示的是雷的位置,是给我们调试时候显示使用的。
//在 test.c 文件中
void play()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
}
下面是在game.h 文件中声明打印棋盘函数的。
//在 test.h 文件中
//打印棋盘
void SetMine(char mine[ROWS][COLS],int row,int col);
下面是在game.h 文件中定义打印棋盘函数的。
//在 game.c 文件中
void DisplayBoard(char me[ROWS][COLS], int row, int col)
{
int i = 0;
int a = 1;
printf("-------扫雷--------\n");
for (i = 0; i <= 9; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", a);
a++;
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", me[i][j]);
}
printf("\n");
}
}
2.2.5 扫雷
//在 test.c 文件中
void play()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//扫雷
FindBoard(mine, show, ROW, COL);
}
下面是在game.h 文件中声明扫雷函数的。
//在 test.h 文件中
//扫雷
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col);
下面是在game.h 文件中定义扫雷函数的。
下面我又定义了一个need函数,作用是计算用户选择位置的周围九宫格的雷的总数,返回到扫雷函数,打印在第二个棋盘上,显示给用户。
//在 game.c 文件中
int need(char mine[ROWS][COLS],int x,int y)
{
int i = 0;
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] - 48;
}
}
return sum;
}
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int amount = 0;
while (amount < row * col - AMOUNT)
{
while (1)
{
printf("请输入要扫雷的行和列:\n");
if (scanf("%d %d", &x, &y) != 2)
{
printf("输入错误,请输入数字!!!\n");
while (getchar() != '\n');
continue;
}
break;
}
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] != '1')
{
if (show[x][y] == '*')
{
int a = need(mine, x, y);
printf("%d\n", a);
show[x][y] = a + '0';
DisplayBoard(show, ROW, COL);
amount++;
}
else
{
printf("已排查,请重新输入!!!\n");
}
}
else
{
printf("很遗憾,你被炸死了!!!\n");
break;
}
}
else
{
printf("输入错误,请重新输入!!!\n");
}
}
if (amount == row * col - AMOUNT)
{
printf("恭喜你,扫雷成功!!!\n");
}
}
2.2.6 总结
以上就是我对扫雷游戏的整体分析,分为了三个文件来进行编程,test.c ,game.c ,game.h 三个文件,test.c 存放的main函数,以及游戏所需函数的使用,game.h 存放的是游戏所需函数的声明,game.c 存放的是游戏所需函数的定义。
3 扫雷游戏代码
3.1 game.h 文件
#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define AMOUNT 20
//初始化棋盘
void InitBoard(char me[ROWS][COLS], int rows, int cols, char a);
//埋雷
void DisplayBoard(char me[ROWS][COLS], int row, int col);
//打印棋盘
void SetMine(char mine[ROWS][COLS],int row,int col);
//扫雷
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col);
3.2 game.c 文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void InitBoard(char me[ROWS][COLS], int rows, int cols, char a)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
me[i][j] = a;
}
}
}
void DisplayBoard(char me[ROWS][COLS], int row, int col)
{
int i = 0;
int a = 1;
printf("-------扫雷--------\n");
for (i = 0; i <= 9; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", a);
a++;
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", me[i][j]);
}
printf("\n");
}
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int i = 0;
int count = AMOUNT;
while (count > 0)
{
int a = rand() % row + 1;
int b = rand() % col + 1;
if (mine[a][b] == '0')
{
mine[a][b] = '1';
count--;
}
}
}
int need(char mine[ROWS][COLS],int x,int y)
{
int i = 0;
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] - 48;
}
}
return sum;
}
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int amount = 0;
while (amount < row * col - AMOUNT)
{
while (1)
{
printf("请输入要扫雷的行和列:\n");
if (scanf("%d %d", &x, &y) != 2)
{
printf("输入错误,请输入数字!!!\n");
while (getchar() != '\n');
continue;
}
break;
}
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] != '1')
{
if (show[x][y] == '*')
{
int a = need(mine, x, y);
printf("%d\n", a);
show[x][y] = a + '0';
DisplayBoard(show, ROW, COL);
amount++;
}
else
{
printf("已排查,请重新输入!!!\n");
}
}
else
{
printf("很遗憾,你被炸死了!!!\n");
break;
}
}
else
{
printf("输入错误,请重新输入!!!\n");
}
}
if (amount == row * col - AMOUNT)
{
printf("恭喜你,扫雷成功!!!\n");
}
}
3.3 test.c 文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{
printf("\n");
printf("1.play\n");
printf("0.exit\n");
printf("\n");
}
void play()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//埋雷
SetMine(mine, ROW, COL);
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//扫雷
FindBoard(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 1;
do
{
menu();
printf("请选择->\n");
if (scanf("%d", &input) != 1)
{
printf("输入错误,应该输入数字!!!\n");
while (getchar() != '\n');
continue;
}
switch (input)
{
case 1:
{
play();
break;
}
case 2:
{
printf("退出游戏!\n");
break;
}
default:
{
printf("输入错误,请重新输入!!!\n");
break;
}
}
} while (input);
return 0;
}