普通网站建设的缺陷,四川省建设注册中心网站,徐州网站关键词排名,仙居住房和城乡建设部网站目录
前言
一、分模块化
二、准备雷盘
2.1 游戏菜单
2.2 创建雷盘思路
2.3 构建雷盘
2.4 雷盘展示
2.4.1 初始化雷盘
2.4.2 打印雷盘
三、排雷
3.1 布置雷
3.2 排查雷
四、进阶版扫雷
总结 前言 C语言实现扫雷小游戏#xff0c;帮我们更进一步的掌握数组、模块化…目录
前言
一、分模块化
二、准备雷盘
2.1 游戏菜单
2.2 创建雷盘思路
2.3 构建雷盘
2.4 雷盘展示
2.4.1 初始化雷盘
2.4.2 打印雷盘
三、排雷
3.1 布置雷
3.2 排查雷
四、进阶版扫雷
总结 前言 C语言实现扫雷小游戏帮我们更进一步的掌握数组、模块化思想等知识。 一、分模块化 对于扫雷小游戏相信老铁们应该不陌生根据信息进行排雷大家可以参考网页版的扫雷游戏扫雷小游戏 对于扫雷小游戏虽然是一个小项目代码量不多但对于初学者来说重要的是学习如何分模块开发项目。 本文将该游戏分成三个文件 test.c游戏测试文件game.c游戏执行逻辑函数实现game.h游戏声明函数声明头文件 二、准备雷盘 扫雷游戏首先需要一个雷盘本文讲解的雷盘为9×9规格。 2.1 游戏菜单 一个游戏最先呈现给用户看的是游戏菜单对于游戏菜单可以用do...while循环 switch选择语句完成因为无论用户是否开始还是退出游戏程序都会运行一次。 test.c文件
#include game.h
void menu()
{printf(********************\n);printf(***** 1、play ******\n);printf(***** 0、exit ******\n);printf(********************\n);
}
int main()
{int input 0;do{//游戏菜单menu();printf(请选择);scanf(%d, input);switch (input){case 1:game();break;case 0:printf(退出游戏\n);break;default:printf(选择错误\n);break;}} while (input);return 0;
}game.h文件
#pragma once
#include stdio.h 2.2 创建雷盘思路 对于雷盘使用二维数组存储数据是最合适的符合行列。那一个雷盘就可以了吗 我们规定雷用字符1表示非雷用字符0表示。 因此冲突点就出现了如果对某个位置进行排雷统计雷数时正好也为1那究竟是雷还是雷数呢 为此这里用了一个很巧妙的方法我们定义两个大小一致的二维数组也就是两个雷盘一个雷盘用于布置雷一个雷盘用于存放统计后雷的个数。 因此我们有以下规定 布置雷的二维数组叫mine。存放雷数的二维数组叫show。因为要展示给用户看选择的位置周围的雷数因此叫show。mine雷盘中字符0为非雷字符1为雷。show雷盘中字符*表示未排查字符数字表示该位置已被排查。 前面说过我们的雷盘是一个9×9的规格那相对于的两个数组的大小也是9×9吗 其实不是应该为11×11因为当我们对最上、最下、最左、最右中的位置进行排雷时统计周围有几颗雷时会造成数组越界的情况因此我们定义多2行2列的数据这部分位置不存放雷将雷存放在9×9的雷盘中多定义的2行2列只是为了防止数组越界。 2.3 构建雷盘 对于数组的大小我们不能写死应该用#define标识符常量来表示方便以后想扩大雷盘。并且我们需要分别定义9×9和11×11的标识符因为在布置雷、打印雷盘、排雷的功能中只需要用到9×9的区域而在初始化雷盘时需要用到11×11区域。 test.c文件
#include game.h
void menu()
{printf(********************\n);printf(***** 1、play ******\n);printf(***** 0、exit ******\n);printf(********************\n);
}
void game()
{//定义两个大小相同的二维数组//这里的数组大小最好用#define标识符表示后续想改变就该#define就好了//用于布置雷用字符数组的原因是规定字符0为非雷字符1为雷char mine[ROWS][COLS] { 0 };//用于存放排查雷的信息主要给用户看的页面//用字符数组的原因是规定雷数用字符数字表示其它位置用字符*表示char show[ROWS][COLS] { 0 };}
int main()
{int input 0;do{//游戏菜单menu();printf(请选择);scanf(%d, input);switch (input){case 1:game();break;case 0:printf(退出游戏\n);break;default:printf(选择错误\n);break;}} while (input);return 0;
}game.h文件
#include stdio.h
/*解释一下这里定义的标识符该游戏的排雷范围为9×9。点击一处位置那周围的8处位置就要判断有几个雷。当位置位于上下一行时或者左右一行时那访问周围8处位置时就会造成越界访问了因此这里可以加多两行两列目的是为了不越界访问这两行两列不放置雷雷的范围只在9×9中
*/
#define ROW 9
#define COL 9
#define ROWS ROW2
#define COLS COL22.4 雷盘展示
2.4.1 初始化雷盘 首先我们要对六个雷盘进行初始化操作mine雷盘一开始全为字符0因为还没布置雷show雷盘一开始全为字符*因为还没开始排雷。 test.c文件
#include game.h
void game()
{//初始化两个棋盘//mine雷盘一开始全是字符0因为还没布置雷InitializeMinefield(mine, ROWS, COLS, 0);//show雷盘一开始全是字符*因为还没统计周围雷数InitializeMinefield(show, ROWS, COLS, *);
}game.h文件
#pragma once
#include stdio.h
#define ROW 9
#define COL 9
#define ROWS ROW2
#define COLS COL2
//初始化雷盘
//这里的参数写什么
//既然是初始化雷盘那要有个数组来接收传来的mine数组和show数组
//初始化9×9还是11×1111×11这样方便后续计算雷的个数
//还有一个参数很重要那就是set既然用一个函数就初始化两个雷盘
//那就要将标识字符传过来进行设置
void InitializeMinefield(char init[ROWS][COLS],int rows,int cols,char set);game.c文件
#include game.h
//初始化雷盘
void InitializeMinefield(char init[ROWS][COLS], int rows, int cols, char set)
{for (int i 0; i rows; i){for (int j 0; j cols; j){init[i][j] set; //set为标识符号mine雷盘传0show雷盘传*}}
} 初始化两个雷盘要为每个雷盘分别写一个函数吗 不需要mine雷盘初始化时将字符0当成参数show雷盘初始化时将字符*当成参数即可。 2.4.2 打印雷盘 打印雷盘的注意点在于打印多大的雷盘是9×9还是11×11 前面说过其实我们真正的雷盘大小为9×9的11×11是为了让数组不越界。 test.c文件
#include game.h
void game()
{//打印雷盘给用户展示的是show雷盘//打印多大9×9因为这是真正排雷区域PrintMindefield(show, ROW, COL);
}game.h文件
#pragma once
#include stdio.h
#define ROW 9
#define COL 9
#define ROWS ROW2
#define COLS COL2//打印雷盘
//这里需要注意接收数组的大小需要写11×11因为定义数组的时候就是11×11
//打印时只打印9×9但在语法上来说数组的大小为11×11。
void PrintMindefield(char print[ROWS][COLS],int row,int col);game.c文件
#include game.h
//打印雷盘
void PrintMindefield(char print[ROWS][COLS], int row, int col)
{printf(-------扫雷--------\n);//给雷盘编号这样用户更快找到坐标进行排雷//上边编号for (int i 0; i col; i){printf(%d , i);}printf(\n);//初始值从下标1开始最后下标row/col//因为数组的大小为11×11只打印9×9时就不能从0开始了结尾也同理for (int i 1; i row; i){printf(%d , i); //左边编号for (int j 1; j col; j){printf(%c , print[i][j]);}//每打印一行就\nprintf(\n);}
} 虽然我们只打印9×9的雷盘但是定义数组时的大小为11×11在函数形参接收时数组中的[][]应为11×11但传过去的行和列为9、9。 雷盘准备完毕我们看看效果 三、排雷
3.1 布置雷 布置雷的思路很简单我们在mine数组上生成随机坐标需要注意的如下 该坐标位置没布置过雷坐标内容为字符0。生成随机数的函数srand随机数生成器和rand函数搭配使用。#define标识符定义雷的个数。 test.c文件
#include game.h
void game()
{//布置雷//在mine数组中布置//#define标识符定义雷数//同样只需要在row × col中布置Place_mine(mine, ROW, COL);
}game.h文件
#pragma once
#include stdio.h
#define ROW 9
#define COL 9
#define ROWS ROW2
#define COLS COL2
//雷的总个数
#define Mine_Sum 10
//布置雷
void Place_mine(char mine[ROWS][COLS], int row, int col);game.c文件
#include game.h
//布置雷
//随机布置随机生成坐标使用rand函数前要设置随机生成器srand
/*条件坐标不是雷即不是1
*/
void Place_mine(char mine[ROWS][COLS], int row, int col)
{int x 0;int y 0;int count Mine_Sum;//总雷数当count为0时表示已布置好雷while (count){x rand() % row 1; y rand() % col 1; if (mine[x][y] 0){mine[x][y] 1;count--;}}} 3.2 排查雷 对于用户操作方面 用户通过坐标进行排雷。输入的坐标有边界条件x1 xrow y1 ycol输入的坐标不能重复 什么时候结束呢两种情况: 被炸死排雷成功排雷次数row*col - 雷数 如何统计周围雷数 周围坐标之和 - 8*字符0。因为是mine数组中只有字符0或字符1.字符数字转为数字字符数字-字符0 数字。因为字符进行加减操作时是以ascll码进行。数字转为字符数字数字字符0 数字。因此周围坐标之和再给每个坐标依次减字符0那最终的结果就为周围雷的个数数字。统计完成后将个数转为字符个数存储到对应位置的show雷盘中。 test.c文件
#include game.h
void game()
{//排雷用户输入坐标排雷/*排雷功能需要两个雷盘mine雷盘用来检查周围雷的个数show雷盘用于存放统计好周围雷的个数并展示*/Examine(mine, show, ROW, COL);
}game.h文件
#pragma once
#include stdio.h
#include stdlib.h
#include time.h
#include stdlib.h
#include time.h
#include Windows.h
#define ROW 9
#define COL 9
#define ROWS ROW2
#define COLS COL2
//雷的总个数
#define Mine_Sum 10
//排雷
void Examine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);game.c文件
#include game.h
//排雷
/*两个核心1、通过用户坐标排雷2、检查周围坐标有几个雷后放置到show雷盘中显示
*/
//统计该坐标周围的雷数
//此时的查找的雷盘范围为11×11防止越界
int StatisNumberMines(char mine[ROWS][COLS], int x, int y)
{/*mine雷盘中放的都是字符0 或 字符1前备知识字符数字转为数字: 字符数字 - 字符0 数字数字转字符数字数字 字符0 字符数字原因因为在进行字符间的加减操作时是以ascll码进行的。现在mine雷盘中放的都是字符数字那将8个位置的字符数字相加后: (8个位置之和) - 8*字符0再依次减字符0那就能统计处周围有几个雷*/return (mine[x 1][y] mine[x 1][y - 1] mine[x][y - 1] mine[x - 1][y - 1] mine[x - 1][y] mine[x - 1][y 1] mine[x][y 1] mine[x 1][y 1]) - 8 * 0;
}
void Examine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x 0;int y 0;/*坐标限制1、不能重复2、不能超出有效范围x1 xrow y1 ycol*//*什么时候结束呢两种情况:1、被炸死。2、排雷成功排雷次数row*col - 雷数*/int num 0;while (num row * col-Mine_Sum){printf(请玩家输入排雷坐标);scanf(%d %d, x, y);if (x 1 x row y 1 y col){//输入坐标不能重复/*检查show雷盘未排雷过的坐标为字符*排查过的坐标字符0或字符数字*/if (show[x][y] *){//判断是否是雷if (mine[x][y] 0){/*输入的坐标不是雷统计周围8个坐标有几颗雷*///在mine雷盘中统计int count StatisNumberMines(mine, x, y);//统计好后放到show雷盘中//当然要将数字转为字符数字因为数组时char类型show[x][y] count 0;PrintMindefield(show, ROW, COL);num;}//是雷就结束else{printf(您被炸死了\n);PrintMindefield(mine, ROW, COL);break;}}else{printf(该坐标已被排查\n);}}else{printf(输入的坐标超出范围\n);}}if (num row*col - Mine_Sum){printf(恭喜你排雷成功\n);}
} 四、进阶版扫雷 简易版的扫雷有什么缺陷呢 其实有很多功能都没有实现比如即时、标记、剩余雷数等等但最重要的是展开一片的功能没有实现因此进阶版扫雷将修改排雷函数让用户输入坐标后将不是周围一大片不是雷的地方展开并提供更多雷数信息。 此时的扫雷只能一个一个坐标的排查并不会展开一片。 展开一大片的关键点 该坐标不是雷该坐标周围坐标没有雷排查坐标没有被排查过。为什么因为当有一个坐标满足三个条件后然后该坐标周围又有坐标满足条件进行展开那原先满足条件的坐标也算是它周围8个坐标那还要判断它吗它已经不是雷了因此这个条件很重要容易造成死递归 //统计该坐标周围的雷数
//此时的查找的雷盘范围为11×11防止越界
int StatisNumberMines(char mine[ROWS][COLS], int x, int y)
{/*mine雷盘中放的都是字符0 或 字符1前备知识字符数字转为数字: 字符数字 - 字符0 数字数字转字符数字数字 字符0 字符数字原因因为在进行字符间的加减操作时是以ascll码进行的。现在mine雷盘中放的都是字符数字那将8个位置的字符数字相加后: (8个位置之和) - 8*字符0再依次减字符0那就能统计处周围有几个雷*/return (mine[x 1][y] mine[x 1][y - 1] mine[x][y - 1] mine[x - 1][y - 1] mine[x - 1][y] mine[x - 1][y 1] mine[x][y 1] mine[x 1][y 1]) - 8 * 0;
}
//排雷展开一片
/*基本思路1、递归终止条件为周围坐标之和不为0说明周围有雷2、如果为0那将该坐标设为空字符然后依次查看周围坐标的周围坐标是否为0如果是那就设置为空但前提是查看的坐标没有被排查过就是坐标里的值是*并且该坐标没超出范围
*/
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{int count StatisNumberMines(mine, x, y);if (count 0){show[x][y] ;int i 0;int j 0;for (i x - 1; i x 1; i){for (j y - 1; j y 1; j){if (show[i][j] * i 0 i 10 j0 j 10){Unfold(mine, show, i, j);}}}}else{show[x][y] count 0;}
}
//查看show雷盘中还剩多少个位置没排查当只剩Mine_Sum个时排雷成功
int Win(char show[ROWS][COLS],int row,int col)
{int count 0;for (int i 0; i row; i){for (int j 0; j col; j){if (show[i][j] *){count;}}}return count;}
void Examine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x 0;int y 0;/*坐标限制1、不能重复2、不能超出有效范围x1 xrow y1 ycol*//*什么时候结束呢两种情况:1、被炸死。2、排雷成功排雷次数row*col - 雷数*/int num 0;while (num row * col-Mine_Sum){printf(请玩家输入排雷坐标);scanf(%d %d, x, y);if (x 1 x row y 1 y col){//输入坐标不能重复/*检查show雷盘未排雷过的坐标为字符*排查过的坐标字符0或字符数字*/if (show[x][y] *){//判断是否是雷if (mine[x][y] 0){//进阶版----展开一片Unfold(mine, show, x, y);PrintMindefield(show, ROW, COL);}//是雷就结束else{printf(您被炸死了\n);PrintMindefield(mine, ROW, COL);break;}}else{printf(该坐标已被排查\n);}}else{printf(输入的坐标超出范围\n);}if (Win(show, row, col) Mine_Sum){printf(恭喜你排雷成功\n);PrintMindefield(mine, ROW, COL);break;}}
} 使用递归实现当周围坐标没有雷时将该坐标设置为空字符然后再依次对周围坐标判断是否被排查过并且不超过有效范围如果满足则递归看该坐标的周围坐标是否符合没有雷条件。直到周围坐标有雷递归就结束开始返回。 总结 这就是扫雷小游戏希望对您有所帮助后续会出更多干货多多支持关注我❤❤❤源代码自取。