该文章将用C语言实现扫雷游戏的基本功能,其中用到的知识有:数组,函数,条件选择语句,循环语句等。通过实现一个简单的小游戏,来对这些知识进行巩固。
玩家要在一定时间内找到所有安全的方块,避免踩到地雷。
键盘输入要探测的坐标位置后按回车键,如果探测的位置是地雷,则游戏结束;若探测的位置不是地雷,则该方格会显示一个数字,该数字表示周围的8个方格中含有的地雷数。当所有安全方块都被翻开后,游戏胜利;否则,游戏失败。
在进行扫雷的过程中,需要定义一个数据结构存储雷的信息,我们可以想到用一个二维数组mine来存储雷的信息,1表示所在的位置有雷,0表示所在的位置无雷。如下图所示:
但是在这个过程中存在一个问题,在对边界位置进行探测时,如果没有雷,那么显示的是周围5个方格中含有的地雷数,这时如果对数组进行相应的操作,可能会造成数组越界的问题;例如,我们要对9*9区域中的(9,7)所在位置进行探测,可能会造成数组越界的问题。如下图所示:
为了解决此问题,我们可以在整要探测区域扩大一圈,即增加两行和两列,将所增加的区域全部设置为0。如下图所示:
这样就可以避免数组越界的问题,同时这样还有一个好处:由于二维数组的行和列下标都是从0开始的,而实际的扫雷区域下标是从1开始的,如果不对扫雷区域进行扩大一圈,那么实际要探测的坐标位置与数组对应的坐标位置不一样,还要对两个坐标位置进行转换。而扩大一圈后,实际要探测的坐标位置与数组对应的坐标位置一样,不用进行坐标转换。例如,要对扫雷区域(2,3)位置进行探测,如果不进行扩大一圈,那么对应数组的位置为(1,2),如果进行扩大一圈,对应数组的位置为(2,3)。
当我们在区域中布置了雷,假设我们排查了一个位置后,这个坐标不是雷,我们需要将周围雷的个数进行打印,作为排雷的重要参考信息。如果将这个信息保存在mine数组中,雷的信息和周围雷个数的信息就会产生混淆,因此我们要再定义一个二维数组show,用来存放拍出雷的信息,show数组的大小与mine数组一样。
当我们将扫雷数组与显示数组设计完成后,要考虑初始化的问题。对于扫雷数组来说,我们应该先将数组中的元素都初始化为0,表示目前整个扫雷区域没有雷,然后根据要生成雷的个数,在棋盘上随机生成雷。对于显示数组来说,由于一开始所有区域未探索,用字符“”表示当前位置为被探索,并将显示数组中所有元素初始化为“”。
主函数主要功能就是显示菜单,并循环接收用户的输入,当用户选择开始游戏时,开始扫雷游戏;若用户选择退出时,程序结束。
进入游戏后,先打印显示数组,并让用户输入要探测的位置,若用户探测到雷,提示“很遗憾,游戏失败”后返回菜单,若用户没有探测到雷,则更新显示数组,并将其打印,继续让用户输入探测的位置,依次类推,若用户将所有安全位置都找到后,提示“恭喜你,排出了所有的雷”后返回菜单。
VS2022
main.c // 主程序文件
game.h // 文件中写需要声明的变量和函数等
game.c // 文件中写需要实现的函数等
主函数主要是实现菜单显示,以及接收用户输入
主函数的实现如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Menu() {
printf("************************\n");
printf("*******1、开始游戏******\n");
printf("*******0、退出 ******\n");
printf("************************\n");
}
void game() {
char mine[ROWS][COLS];//存放布置好的雷,'1'表示对应位置有雷,'0'表示对应位置无雷
char show[ROWS][COLS];//显示怕查出雷的信息,'*'表示对应位置没有被探索,如果被探索,显示周围雷的个数
//初始化数组
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//DisplayBoard(mine, ROW, COL);
//DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
int main() {
srand((unsigned int)time(NULL));
int input;
do
{
Menu();
printf("请输入选项>");
scanf("%d", &input);
switch (input) {
case 1:
system("cls");
game();
system("pause");
system("cls");
break;
case 0:
break;
default:
printf("输入选项非法,请重新输入\n");
system("pause");
system("cls");
break;
}
} while (input);
}
game.h文件中写需要声明的变量和函数等
文件代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9 //扫雷游戏数组的行数
#define COL 9 //扫雷游戏数组的列数
#define ROWS ROW + 2 //扫雷游戏数组扩大一圈后的行数
#define COLS COL + 2 //扫雷游戏数组扩大一圈后的列数
#define MINE_COUNT 80 //需要布置的雷的个数
#include
#include
#include
void InitBoard(char board[][COLS], int rows, int cols, char set);
void DisplayBoard(char board[][COLS], int row, int col);
void SetMine(char board[][COLS], int row, int col);
void FindMine(char mine[][COLS], char show[][COLS], int row, int col);
game.c文件主要对game.h文件中声明的函数进行实现,文件代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化数组
void InitBoard(char board[][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 DisplayBoard(char board[][COLS], int row, int col)
{
printf("----- 扫雷游戏 -----\n");
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 board[][COLS], int row, int col)
{
int count = MINE_COUNT;
while (count) {
int mine_row = rand() % row + 1;
int mine_col = rand() % col + 1;
if (board[mine_row][mine_col] == '0') {
board[mine_row][mine_col] = '1';
count--;
}
}
}
//返回探索区域周围雷的个数
int GetCount(char mine[][COLS],int x,int y) {
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1]
+ mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
//排查类
void FindMine(char mine[][COLS], char show[][COLS], int row, int col)
{
int find_count = 0;
while (find_count < row * col - MINE_COUNT) {
int x;
int y;
DisplayBoard(show, row, col);
DisplayBoard(mine, row, col);
printf("请输入要探测的位置坐标>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*') {
if (mine[x][y] == '1') {
printf("很遗憾,踩到地雷,游戏结束\n");
break;
}
else
{
show[x][y] = GetCount(mine, x, y) + '0';
//printf("%d\n", GetCount(mine, x, y));
find_count++;
system("cls");
}
}
else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '*') {
printf("该位置已被探索,请重新输入坐标\n");
system("pause");
system("cls");
}
else {
printf("输入的坐标位置非法,请重新输入坐标位置\n");
system("pause");
system("cls");
}
if (find_count == row * col - MINE_COUNT) {
printf("恭喜你,排除了所有的雷\n");
DisplayBoard(mine, ROW, COL);
}
}
}
菜单界面测试:
游戏功能测试
由于成功排雷花费时间较长,我在测试前将地雷的数量设置为80,并打印mine数组,这样在测试前就可以知道雷的位置。
退出游戏
至此,用C语言实现简单的扫雷游戏基本结束,完整代码可见gitee链接: link
本文章只实现了扫雷游戏基本的功能,还有很功能没有实现,日后若有时间将继续完善。当然,设计过程中可能存在不足之处,还请各位大佬指正。