目录
前言
扫雷游戏,相信大家都玩过,但扫雷游戏是什么原理呢?其实我们可以将它的网盘区域看为二维数组 ,用C语言二维数组实现扫雷。其实扫雷和三子棋大同小异,你们可以结合我的上篇博客三子棋一起理解。
一、介绍
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
扫雷从Windows 3.1时期开始整个游戏界面由一个个方块组成,任意点击其中一个方块,如果该方块下面藏着地雷,则Game Over。但却可以让人不知不觉间搭上好几个小时的投入时间。作为日常调剂的游戏作品,扫雷的确是款老少男女弯直咸宜的休闲游戏,而且用来杀时间会非常有效。这种游戏对于微软这样的游戏大厂而言,或许也算是体现功力的点所在。
灰色的方格,红色的LED数字,各种颜色的数字以及红色小旗子,这估计是大部分人对《扫雷》这款经典游戏的记忆。但随着Windows XP离我们远去,扫雷也已经不是我们熟悉的那个游戏了。
编辑
二、游戏详解
初始化游戏
我们可以设置两个二维数组面板,一个二维数组为雷板(mine),一个为可见面板(show),将mine数组全部初始化为‘0’,将show数组全部初始化为‘*’。
InitBoard(mine, '0'); InitBoard(show, '*');
void InitBoard(char arr[ROWS][COLS], char ch) { for (int i = 0; i < ROWS; ++i) { for (int j = 0; j < COLS; ++j) arr[i][j] = ch; } }
埋雷
初始化面板设置好后,我们就可以设置雷区了。
我们用rand()函数来随机生成雷面板坐标的x和y,将此坐标改为‘1’。
SetMine(mine, ROW, COL);
void SetMine(char mine[ROWS][COLS],int row,int col) { int count = GAME_LEVEL; srand(time(0)); while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } }
显示游戏界面
为了人们输入某个位置方便和后面电脑计算雷的数量方便,在显示游戏界面时,只显示[ROWS-2][COLS-2]的二维数组,并显示x与y坐标。
编辑
void DisplayBorad(char show[ROWS][COLS], int row, int col) { for (int i = 0; i <= col; ++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 ", show[i][j]); } printf("\n"); } }
面板显示后,我们即可开始游戏进行排雷。
扫雷
玩家开始游戏,此处应该用个死循环判断是否找出所有空区。
检查坐标有效性
当玩家输入要想扫雷位置的坐标,应该先判断输入的位置是否有效,如果无效,则跳出此次循环重新输入。
if ((x<1 || x>row) || (y<1 || y>col)) { printf("输入坐标有误!\n"); continue; }
如果输入位置有效,则应该判断此位置是否有雷。
检查是否排雷成功
如果有雷,则直接结束游戏。
if (mine[x][y] == '1') { printf("你被雷炸死了!\n"); DisplayBorad(mine, ROW, COL); break; }
否则,计算周围9x9大小位置有几颗雷,并在此位置上显示。
int _GetCountOfMine(char mine[ROWS][COLS], int row, int col) { return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1] + mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - (8 * '0')); }
这里,也体现了前面为什么设置了[ROWS][COLS]这么大面板却只显示[ROWS-2][COL-2]的面板,原因是如果显示[ROWS][COLS]这么大的面板,则在计算第一行、第一列、最后一行、最后一列的周围雷数时会很麻烦,这样写少了我们的代码量。
递归展开周围所有安全区域
传统的扫雷游戏中,当你点击一个坐标,若该坐标没有雷,则会展开该坐标周围所有的安全区域,直到周围有雷的坐标,上述过程可由递归实现。
1.若该坐标没有雷,则赋值为空格。之后,判断周围八个坐标的周围是否有雷,周围没有雷的坐标同样赋值为空格,周围没有雷的坐标则继续向外展开,直到遇到周围有雷的坐标或达到了扫雷盘面的边缘,则停止递归。
2.若该坐标有雷,则直接赋值为周围雷的个数。
void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int *win) { int count = _GetCountOfMine(mine, x, y); if (count == 0) { show[x][y] = ' ';//没有雷的坐标赋值为空格 (*win)++; //递归周围的八个格子 if (show[x - 1][y - 1] == '*' && x - 1 > 0 && x - 1 < ROWS && y - 1 > 0 && y - 1 < COLS) ExpandBoard(mine, show, x - 1, y - 1, win); if (show[x - 1][y] == '*' && x - 1 > 0 && x - 1 < ROWS && y > 0 && y < COLS) ExpandBoard(mine, show, x - 1, y, win); if (show[x - 1][y + 1] == '*' && x - 1 > 0 && x - 1 < ROWS && y + 1 > 0 && y + 1 < COLS) ExpandBoard(mine, show, x - 1, y + 1, win); if (show[x][y - 1] == '*' && x > 0 && x < ROWS && y - 1 > 0 && y - 1 < COLS) ExpandBoard(mine, show, x, y - 1, win); if (show[x][y + 1] == '*' && x > 0 && x < ROWS && y + 1 > 0 && y + 1 < COLS) ExpandBoard(mine, show, x, y + 1, win); if (show[x + 1][y - 1] == '*' && x + 1 > 0 && x + 1 < ROWS && y - 1 > 0 && y - 1 < COLS) ExpandBoard(mine, show, x + 1, y - 1, win); if (show[x + 1][y] == '*' && x + 1 > 0 && x + 1 < ROWS && y > 0 && y < COLS) ExpandBoard(mine, show, x + 1, y, win); if (show[x + 1][y + 1] == '*' && x + 1 > 0 && x + 1 < ROWS && y + 1 > 0 && y + 1 < COLS) ExpandBoard(mine, show, x + 1, y + 1, win); } else { show[x][y] = count + '0'; } }
以上版面大小、雷的数量,都可根据自己需求来更改。也可以做出挑选游戏难易程度(如:简单、中等、困难),但由于代码量增多,今天太晚了,博主就先写这么多功能,后面有时间补上来。活不多说,直接上代码!
完整代码
utili.h
#ifndef _UTILI_H_ #define _UTILI_H_ #include<stdio.h> #include<stdlib.h> #include<time.h> #endif /* _UTILI_H_ */
Game.h
#ifndef _GAME_H_ #define _GAME_H_ #include"utili.h" #define Play 1 #define Exit 0 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define GAME_LEVEL 15 //开始游戏 void StartGame(); //更新雷数 int _GetCountOfMine(char mine[ROWS][COLS], int row, int col); //扫雷 void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col); //显示游戏界面 void DisplayBorad(char show[ROWS][COLS], int row,int col); //埋雷 void SetMine(char mine[ROWS][COLS], int row, int col); //初始化游戏 void InitBoard(char arr[ROWS][COLS],char ch); #endif /* _GAME_H_ */
Game.cpp
#include"Game.h" void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int *win) { int count = _GetCountOfMine(mine, x, y); if (count == 0) { show[x][y] = ' ';//没有雷的坐标赋值为空格 (*win)++; //递归周围的八个格子 if (show[x - 1][y - 1] == '*' && x - 1 > 0 && x - 1 < ROWS && y - 1 > 0 && y - 1 < COLS) ExpandBoard(mine, show, x - 1, y - 1, win); if (show[x - 1][y] == '*' && x - 1 > 0 && x - 1 < ROWS && y > 0 && y < COLS) ExpandBoard(mine, show, x - 1, y, win); if (show[x - 1][y + 1] == '*' && x - 1 > 0 && x - 1 < ROWS && y + 1 > 0 && y + 1 < COLS) ExpandBoard(mine, show, x - 1, y + 1, win); if (show[x][y - 1] == '*' && x > 0 && x < ROWS && y - 1 > 0 && y - 1 < COLS) ExpandBoard(mine, show, x, y - 1, win); if (show[x][y + 1] == '*' && x > 0 && x < ROWS && y + 1 > 0 && y + 1 < COLS) ExpandBoard(mine, show, x, y + 1, win); if (show[x + 1][y - 1] == '*' && x + 1 > 0 && x + 1 < ROWS && y - 1 > 0 && y - 1 < COLS) ExpandBoard(mine, show, x + 1, y - 1, win); if (show[x + 1][y] == '*' && x + 1 > 0 && x + 1 < ROWS && y > 0 && y < COLS) ExpandBoard(mine, show, x + 1, y, win); if (show[x + 1][y + 1] == '*' && x + 1 > 0 && x + 1 < ROWS && y + 1 > 0 && y + 1 < COLS) ExpandBoard(mine, show, x + 1, y + 1, win); } else { show[x][y] = count + '0'; } } int _GetCountOfMine(char mine[ROWS][COLS], int row, int col) { return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1] + mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - (8 * '0')); } void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x, y; int winner = 0; while (winner < row*col - GAME_LEVEL) { printf("请输入坐标:>"); scanf("%d %d", &x, &y); //检查坐标有效性 if ((x<1 || x>row) || (y<1 || y>col)) { printf("输入坐标有误!\n"); continue; } //检查是否排雷成功 if (mine[x][y] == '1') { printf("你被雷炸死了!\n"); DisplayBorad(mine, ROW, COL); break; } //更新雷数 int mine_count = _GetCountOfMine(mine, x, y); show[x][y] = mine_count + '0'; winner++; //展开周围区域 ExpandBoard(mine, show, x, y, &winner); DisplayBorad(show, ROW, COL); } //宣布结果 if (winner >= row*col - GAME_LEVEL) { printf("排雷成功!\n"); } } void SetMine(char mine[ROWS][COLS],int row,int col) { int count = GAME_LEVEL; srand(time(0)); while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } } void DisplayBorad(char show[ROWS][COLS], int row, int col) { for (int i = 0; i <= col; ++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 ", show[i][j]); } printf("\n"); } } void InitBoard(char arr[ROWS][COLS], char ch) { for (int i = 0; i < ROWS; ++i) { for (int j = 0; j < COLS; ++j) arr[i][j] = ch; } } void StartGame() { char mine[ROWS][COLS]; char show[ROWS][COLS]; //初始化游戏 InitBoard(mine, '0'); InitBoard(show, '*'); //埋雷 SetMine(mine, ROW, COL); //显示游戏界面 DisplayBorad(show, ROW, COL); //扫雷 FindMine(mine, show, ROW, COL); }
GameMain.cpp
#include"Game.h" int main() { //设置界面 system("title 简易扫雷"); system("mode con cols=26 lines=26"); system("color F4"); int select = 1; while (select) { printf("**************************\n"); printf("* 简 易 扫 雷 *\n"); printf("**************************\n"); printf("* [1] Play [0] Exit *\n"); printf("**************************\n"); printf("请选择:>"); scanf("%d", &select); if (select == Exit) break; if (select != Play) { printf("输入有误,请重新选择!\n"); continue; } //开始游戏 StartGame(); } printf("游戏结束!\n"); return 0; }
这就是对扫雷的简易实现,希望大家能关注点赞我的博客,我会持续更新。如果有什么疑问和见解,欢迎评论区留言。
制作不易,白嫖不好,感谢三连!