一、扫雷游戏的简单认识与解释
相信大家都玩过扫雷游戏吧。但是你真的会玩扫雷游戏吗?那就让我来给你具体讲一下扫雷游戏的玩法。规则如下:
首先是已经布置好雷区,第一次排雷全靠运气;
当未踩中雷,会显示出以你排的位置为中心,9x9的范围内有多少颗雷;
当未踩中雷,且9x9的范围内没有雷时,会直接拓展区域,直到周围有雷停止拓展;
当你踩中雷时,游戏直接结束;
直到你排完雷,才算游戏取得胜利。
既然我们熟悉了规则,那我们来看一下具体的代码及思路的实现吧。
二、扫雷游戏的代码及思路实现
一、扫雷游戏的思路
我们先来大概想一下整体的思路。简单的可分为以下步骤:
- 菜单打印
- 创建扫雷区域
- 初始化扫雷区域
- 打印雷区
- 布置雷区
- 排雷
有了上面的整体的扫雷实现思路,我们就来一一展开实现。当然在不同板块实现中还有很多的小细节,具体的细节我们再实现中一一引出来分析。
1、菜单打印
菜单的打印需要简单明了即可。且实现比较简单。注意要单独放在一个自定义函数中,让主函数中的代码尽量减少,方便观察。
void meau() { printf("************************\n"); printf("***** 1、play *****\n"); printf("***** 0、exit *****\n"); printf("************************\n"); }
通过上面的菜单,我们可以很容易的看出选择 ‘ 1 ’ 开始游戏,选择 ‘ 0 ’ 退出游戏。
2、创建扫雷区
创建雷区需要注意的是,我们后期可能要改变雷区的大小。为了方便后期更改雷区大小,所以我们这里选择define定义常量。
我们在这里创建雷区时选择创建两个二位数组。一个数组放雷,另一个数组输出提示。这样会更加方便实现。假如我们这里只创建一个二维数组的话,在扫雷的同时还需要输出提示会很麻烦。
当我们选择9x9的雷区时,我们定义的雷区需要在上下左右各加一行,以便后面我们排雷时不会越界访问数组。代码如下:
#define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 char mine[ROWS][COLS] = { 0 }; //放雷数组 char show[ROWS][COLS] = { 0 }; //输出数组
3、初始化雷区
我们先把两个数组初始化。在mine[ROWS][COLS]中,我们将整个数组初始化成 ’ 0 ’;将show[ROWS][COLS] 全部初始化成 ‘*’。把mine数组初始化成’ 0 ’,是因为我们要把雷设置成 ‘ 1 ’,以便我们后期统计雷的数量。把show数全部初始化成 ‘ * ’,是因为输出的时候可看性比较高。接下来我们看一下代码的实现:
void init_board(char board[ROWS][COLS], int rows, int cols,char set) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = set; } } } init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*');
4、打印雷区
打印雷区时,我们可以自己适当添加一些格式,以便后期玩家更加方便的玩游戏。这里我们添加了行和列标示,还有扫雷区的提示。代码的实现如下:
void print_board(char board[ROWS][COLS], int row, int col) { int j = 0; int i = 0; printf("-------G扫雷-------\n"); printf("\n"); for (j = 0; j <= col; j++) { printf("%d ", j); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } printf("\n"); printf("-------G扫雷-------\n"); } print_board(mine, ROW, COL); print_board(show, ROW, COL);
5、布置雷区
布置雷区当然是要随机布置的。 提到随机,我们就因该联想到rand()函数和srand()函数,在这里我就不详细介绍这两个函数的使用方法了,在之前的猜数字小游戏中有详细的解释,可以去了解一下。需要注意的是,我们要把布置的雷区放在9x9的范围内,且已经布置过的地方不能重新布置。我们看一下代码的实现:
void set_mine(char mine[ROWS][COLS], int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } } set_mine(mine, ROW, COL);
6、排雷
排雷的时候需要我们注意以下几种情况:
输入所要排雷的坐标需要合法,不合法时要给出相应的提示;
排查过的坐标不需要重复排查;
排查的坐标3x3的周围没有雷时要进行相应的展开;
踩中雷时,要给出相应的提示,并且同时打印书雷区数组。
上面的雷区展开,我们进行展开时需要用到递归。我们结合着代码综合理解一下,代码如下
int sum_mine(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][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y+1] - 8 * '0'); } void spread_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { int n = sum_mine(mine, x, y); if (x >= 1 && x <= ROW && y >= 1 && y <= COL) { if (show[x][y] == '*') { if (n == 0) { show[x][y] = '0'; spread_mine(mine, show, x - 1, y); spread_mine(mine, show, x - 1, y - 1); spread_mine(mine, show, x, y - 1); spread_mine(mine, show, x + 1, y - 1); spread_mine(mine, show, x + 1, y); spread_mine(mine, show, x + 1, y + 1); spread_mine(mine, show, x, y + 1); spread_mine(mine, show, x - 1, y + 1); } else { show[x][y] = n + '0'; } } } } void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win<(row*col- EASY_COUNT)) { printf("请输入你要排查的坐标:"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] != '*') { printf("该坐标已经被排查过了哦。\n"); continue; } if (mine[x][y] == '0') { spread_mine(mine, show, x, y); int n = sum_mine(mine, x, y); show[x][y] = n + '0'; print_board(show, ROW, COL); win++; } else { printf("不好意思,你踩中雷了。雷区如下:\n"); print_board(mine, ROW, COL); break; } } else { printf("你输入的坐标非法哦,请重新输入合法坐标。\n"); } } if (win == (row * col - EASY_COUNT)) { printf("恭喜你,排雷成功了ovo!\n"); } } find_mine(mine,show, ROW, COL);
三、扫雷游戏代码的整合
由于代码量相对来说有一点多,所以我们就将函数的声明的定义分开,这样有利于提高代码的可读性,同时会保持一个良好的思路,且方便编写代码。
我们将函数的声明放在单独的一个game.h的头文件,函数的实现放在一个单独的game.c源文件,函数的主方法及调用放在另一个单独的test.c源文件。
game.h
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 80 //雷的个数 //初始化扫雷界面 void init_board(char board[ROWS][COLS], int rows, int cols, char set); //打印扫雷界面 void print_board(char board[ROWS][COLS], int row, int col); //布置雷区 void set_mine(char mine[ROWS][COLS], int row, int col); //排雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
#include "game.h" void init_board(char board[ROWS][COLS], int rows, int cols,char set) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = set; } } } void print_board(char board[ROWS][COLS], int row, int col) { int j = 0; int i = 0; printf("-------G扫雷-------\n"); printf("\n"); for (j = 0; j <= col; j++) { printf("%d ", j); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } printf("\n"); printf("-------G扫雷-------\n"); } void set_mine(char mine[ROWS][COLS], int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } } int sum_mine(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][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y+1] - 8 * '0'); } void spread_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { int n = sum_mine(mine, x, y); if (x >= 1 && x <= ROW && y >= 1 && y <= COL) { if (show[x][y] == '*') { if (n == 0) { show[x][y] = '0'; spread_mine(mine, show, x - 1, y); spread_mine(mine, show, x - 1, y - 1); spread_mine(mine, show, x, y - 1); spread_mine(mine, show, x + 1, y - 1); spread_mine(mine, show, x + 1, y); spread_mine(mine, show, x + 1, y + 1); spread_mine(mine, show, x, y + 1); spread_mine(mine, show, x - 1, y + 1); } else { show[x][y] = n + '0'; } } } } //void spread_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) //{ // 判断坐标是否越界 // if (x == 0 || y == 0 || x == ROWS - 1 || y == COLS - 1) // return; // 判断是否已经被排除 // if (show[x][y] != '*') // { // return; // } // int count = sum_mine(mine, x, y); // if (count > 0) // { // show[x][y] = count + '0'; // return; // } // 递归拓展地图 // else if (count == 0) // { // show[x][y] = '0'; // spread_mine(mine, show, x - 1, y); // spread_mine(mine, show, x - 1, y - 1); // spread_mine(mine, show, x, y - 1); // spread_mine(mine, show, x + 1, y - 1); // spread_mine(mine, show, x + 1, y); // spread_mine(mine, show, x + 1, y + 1); // spread_mine(mine, show, x, y + 1); // spread_mine(mine, show, x - 1, y + 1); // } //} void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win<(row*col- EASY_COUNT)) { printf("请输入你要排查的坐标:"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] != '*') { printf("该坐标已经被排查过了哦。\n"); continue; } if (mine[x][y] == '0') { spread_mine(mine, show, x, y); int n = sum_mine(mine, x, y); show[x][y] = n + '0'; print_board(show, ROW, COL); win++; } else { printf("不好意思,你踩中雷了。雷区如下:\n"); print_board(mine, ROW, COL); break; } } else { printf("你输入的坐标非法哦,请重新输入合法坐标。\n"); } } if (win == (row * col - EASY_COUNT)) { printf("恭喜你,排雷成功了ovo!\n"); } }
test.c
#include "game.h" void game() { srand((unsigned int)time(NULL)); char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化扫雷界面 init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*'); //打印扫雷界面 //print_board(mine, ROW, COL); print_board(show, ROW, COL); //布置雷区 set_mine(mine, ROW, COL); print_board(mine, ROW, COL); //排雷 find_mine(mine,show, ROW, COL); } void meau() { printf("************************\n"); printf("***** 1、play *****\n"); printf("***** 0、exit *****\n"); printf("************************\n"); } void test() { int input = 0; do { meau(); printf("请选择是否要开始游戏:"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏。\n"); break; default: printf("选择错误,请重新选择哦\n"); break; } } while (input); } int main() { test(); return 0; }
这里相对较难理解的是排雷时的展开,也是需要重点理解的地方。
希望这篇文章能给你带来一个很好的理解,对你有所帮助,感谢阅读。
后续会一直更新的哦ovo!