扫雷游戏(C语言实现)初级版和优化版(增加了自动展开、标记地雷功能,同时排除了第一次排到地雷的情况)

本文主要是介绍扫雷游戏(C语言实现)初级版和优化版(增加了自动展开、标记地雷功能,同时排除了第一次排到地雷的情况),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、初级版扫雷
    • 1.1 游戏功能
    • 1.2 设计思路
  • 二、优化版扫雷
    • 2.1 优化功能
    • 2.2 设计思路
  • 三、完整代码
    • GitHub链接
    • Gitee链接


一、初级版扫雷

1.1 游戏功能

初级版只具备最基础的两个功能:
1、显示当前输入坐标周围雷的数目
2、排雷错误,则游戏结束
3、当排除所有的非雷区域后,取得胜利

1.2 设计思路

我们以9*9的格子(我们称为棋盘)为例。
首先,我们需要两个数组,其中一个用来布置地雷的位置(这个数组不对玩家显示),另一个用来显示排雷的信息。
在这里插入图片描述
假设我们现在要统计坐标(1,1)和(3,4)周围雷的数目,坐标(3,4)需要统计周围8个位置是否有雷,坐标(1,1)只用统计周围三个位置是否有雷,在棋盘中,大多数格子都被8个格子所包围,而边界位置旁边有3或5个格子,如果分别进行判断的话,需要讨论多种情况,因此我们将棋盘扩大为11 *11。有效区域只有中间的9 *9部分,这样对于每一个位置,都是统计周围8个格子是否有雷。
在这里插入图片描述
运行效果:
在这里插入图片描述

在游戏开始前,我们打印一个游戏菜单供玩家进行选择,当玩家选择了开始游戏后,我们进入游戏模块。

void menu()
{printf("**********************************************\n");printf("********请选择->   1:开始游戏  0:结束游戏****\n");printf("**********************************************\n");
}
int main()
{int input = 0;srand((unsigned int)time(NULL));		//初始化种子do{menu();		//打印菜单scanf("%d",&input);switch (input){case 1:printf("扫雷游戏开始:\n");game();break;case 0:printf("退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

当玩家选择开始游戏后,我们要先创建两个数组,并将其初始化。存放地雷的数组,先将其全部初始化为字符’0’;存放排雷信息的数组先全部初始化为字符 ’ * '。

游戏模块:

void game()
{char Myboard[ROWS][COLS] = { 0 };		//用来存放地雷char ShowBoard[ROWS][COLS] = { 0 };		//用来显示排雷的信息InitBoard(Myboard, ROWS, COLS, '0');	//将地雷棋盘全部初始化为0InitBoard(ShowBoard, ROWS, COLS, '*');	//将显示棋盘全部初始化为*SetBoard(Myboard, ROW, COL);		//布雷PrintBoard(ShowBoard, ROWS, COLS);		//打印棋盘FindBoard(Myboard,ShowBoard, ROWS, COLS);			//排雷
}

初始化模块:

void InitBoard(char board[ROWS][COLS], int row, int col, char c)	//初始化棋盘
{int i = 0, j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++)board[i][j] = c;}
}

然后我们使用srand和rand函数随机布置地雷的位置(srand函数要放在主函数的循环体外)因为棋盘的有效区域只有中间的9 *9部分,因此我们要确保rand产生的随机数在1-9这个范围内。

void SetBoard(char board[ROWS][COLS], int row, int col)		//布雷
{for (int cnt = 0; cnt < COUNT;){int x = rand() % row + 1;		//随机产生横纵坐标int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';cnt++;}}
}

当布置好雷之后,我们打印显示棋盘,在打印的同时打印上横纵序号,方便玩家确定坐标

void PrintBoard(char board[ROWS][COLS], int row, int col)			//打印棋盘 
{printf("  ");for (int i = 1; i < row - 1; i++)			//打印列坐标printf("%d ", i);printf("\n");for (int i = 1; i < row - 1; i++){printf("%d ", i);			//打印横坐标for (int j = 1; j < col - 1; j++)printf("%c ", board[i][j]);printf("\n");}}

最后是游戏的核心部分:排雷
当玩家输入的坐标不是雷,统计周围8个位置雷的数目,可以用8个if语句或者循环语句来统计,此处我将每个位置的字符相加,最后减去8个’0’,也是雷的数目。

int Num(char board[ROWS][COLS], int x, int y)			//统计当前位置周围有几个雷
{return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] +board[x][y - 1] + board[x][y + 1] +board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//如果是雷,则对应的坐标位置是字符 '1',否则是字符'0',将周围八个位置的字符全部相加最后减去8个字符'0',就是周围雷的数目//'1' - '0' = 1(数字1) '0' - '0' = 0(数字0)
}
void FindBoard(char board[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col)			//排雷
{int x = 0;int y = 0;int cnt = ROW * COL - COUNT;		//cnt表示非雷的数目,即棋盘大小减去地雷数while (1){printf("请输入坐标\n");scanf("%d%d", &x, &y);if (x < 1 || x >row || y < 1 || y > col){printf("坐标非法,请重新输入\n");}else{if (board[x][y] == '1')			//当前坐标位置是雷,游戏结束{printf("你失败了,游戏结束\n");PrintBoard(board, row, col);break;}else if (board[x][y] == '0')		//当前坐标不是雷,统计周围雷的数目{if (0 == Num(board, x, y))		//如果周围没有雷,则当其置为空{ShowBoard[x][y] = ' ';}else ShowBoard[x][y] = Num(board, x, y) + '0';  //周围有雷,将其置为雷的个数//(因为我们打印的是字符型变量,因此要加上'0'才是所对应的数字字符)cnt--;						//每排完一个雷,非雷的数目减1PrintBoard(ShowBoard, row, col);		//打印当前显示棋盘的信息}}if (cnt == 0){printf("恭喜你,成功排雷\n");PrintBoard(board, row, col);break;}}
}

我们将代码分别封装在main.c和game.c两个文件中,并在game.h头文件中对函数进行声明

main.c

#include"game.h"
void menu()
{printf("**********************************************\n");printf("********请选择->   1:开始游戏  0:结束游戏****\n");printf("**********************************************\n");
}
void game()
{char Myboard[ROWS][COLS] = { 0 };		//用来存放地雷char ShowBoard[ROWS][COLS] = { 0 };		//用来显示排雷的信息InitBoard(Myboard, ROWS, COLS, '0');	//将地雷棋盘全部初始化为0InitBoard(ShowBoard, ROWS, COLS, '*');	//将显示棋盘全部初始化为*SetBoard(Myboard, ROW, COL);		//布雷PrintBoard(ShowBoard, ROWS, COLS);		FindBoard(Myboard,ShowBoard, ROWS, COLS);			//排雷
}
int main()
{int input = 0;srand((unsigned int)time(NULL));		//初始化种子do{menu();		//打印菜单scanf("%d",&input);switch (input){case 1:printf("扫雷游戏开始:\n");game();break;case 0:printf("退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

game.c

void InitBoard(char board[ROWS][COLS], int row, int col, char c)	//初始化棋盘
{int i = 0, j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++)board[i][j] = c;}
}
void PrintBoard(char board[ROWS][COLS], int row, int col)			//打印棋盘 
{printf("  ");for (int i = 1; i < row - 1; i++)			//打印列坐标printf("%d ", i);printf("\n");for (int i = 1; i < row - 1; i++){printf("%d ", i);			//打印横坐标for (int j = 1; j < col - 1; j++)printf("%c ", board[i][j]);printf("\n");}}
void SetBoard(char board[ROWS][COLS], int row, int col)		//布雷
{for (int cnt = 0; cnt < COUNT;){int x = rand() % row + 1;		//随机产生横纵坐标int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';cnt++;}}
}
int Num(char board[ROWS][COLS],int x,int y)			//统计当前位置周围有几个雷
{return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] +board[x][y - 1] + board[x][y + 1] +board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';		
}
void FindBoard(char board[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col)			//排雷
{int x = 0;int y = 0;int cnt = ROW * COL - COUNT;		//cnt表示非雷的数目,即棋盘大小减去地雷数while (1){printf("请输入坐标\n");scanf("%d%d", &x, &y);if (x < 1 || x >row || y < 1 || y > col){printf("坐标非法,请重新输入\n");}else{if (board[x][y] == '1')			//当前坐标位置是雷,游戏结束{printf("你失败了,游戏结束\n");PrintBoard(board, row, col);break;}else if (board[x][y] == '0')		//当前坐标不是雷,统计周围雷的数目{if (0 == Num(board, x, y))		//如果周围没有雷,则当其置为空{ShowBoard[x][y] = ' ';}else ShowBoard[x][y] = Num(board, x, y) + '0';  //周围有雷,将其置为雷的个数//(因为我们打印的是字符型变量,因此要加上'0'才是所对应的数字字符)cnt--;						//每排完一个雷,非雷的数目减1PrintBoard(ShowBoard, row, col);		//打印当前显示棋盘的信息}}if (cnt == 0)		//所有非雷位置均排查完{printf("恭喜你,成功排雷\n");PrintBoard(board, row, col);break;}}
}

二、优化版扫雷

2.1 优化功能

在初级版扫雷中,我们必须一个一个地将所有非雷位置全部排查完才能完成游戏的胜利,提示的信息特别少,并且如果运气不好,第一次就可能排到雷的位置。在优化版扫雷中,增加了自动展开、防止第一次就排到雷的情况同时增加了标记功能。

2.2 设计思路

1、展开功能
当输入一个坐标,如果它的周围没有雷,则将其周围不是雷的区域展开,如果它周围的格子周围也没有雷,则继续展开。
在这里插入图片描述
同时为了防止数组越界,在统计雷的数目函数里增加一个if语句来判断。同时为了记录在展开函数中,展开了多少个格子,我们将展开函数返回值设为int,用来记录展开了多少格子。

int Num(char board[ROWS][COLS],int x,int y)			//统计当前位置周围有几个雷
{if (x >= 1 && x <= ROW && y >= 1 && y <= COL)	//防止数组越界return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] +board[x][y - 1] + board[x][y + 1] +board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';		
}
int Blank(char board[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y)		//判断当前位置周围是不是没有雷
{int count = 0;		//展开的格子数for (int i = x - 1; i <= x + 1 && i >=0 && i <= ROW; i++){for (int j = y - 1; j <= y + 1 && j >=0 && j <=COL; j++){if (i == x && j == y)		//跳过坐标为(x,y)的位置;else if (ShowBoard[i][j] == '*' && board[i][j] != '1')		//如果坐标(i,j)处没有被初始化并且不为雷,判断其周围有没有雷{															//如果周围也没有雷,将其展开	int cnt = Num(board, i, j);                                                                               if (cnt == 0){ShowBoard[i][j] = ' ';count += Blank(board, ShowBoard, i, j);				//继续判断这个坐标周围的位置是否需要展开}else ShowBoard[i][j] = cnt + '0';if (i >= 1 && i <= ROW && j >= 1 && j <= COL)		//当展开的格子在有效棋盘范围内时,count++count++;}}}return count;		//总共展开了多少个格子
}

2、防止第一次就排到雷

如果运气不好,第一次就排到雷,则重置这个雷的位置。

3、在排雷过程中,让玩家选择排雷和标记,当选择标记,将输入的坐标置为’#’

void FindBoard(char board[ROWS][COLS],char ShowBoard[ROWS][COLS],int row, int col)			//排雷
{int cnt = ROW * COL - COUNT;int time = 1;		//time为1时表示是第一步int over = 0;//PrintBoard(board, row, col);while (1)		{printf("请选择-> \n1、排雷   2、标记\n");int choose = 0;scanf("%d", &choose);switch (choose){case 1:while (1)		//排雷循环{printf("请输入排雷的坐标\n");int x = 0;int y = 0;scanf("%d%d", &x, &y);if (x < 1 || x >row || y < 1 || y > col){printf("坐标非法,请重新输入\n");}else{if (board[x][y] == '1'){if (time) //如果第一次就遇见雷,则重置这个雷的位置{board[x][y] = '0';while (1)		//重置第一次雷的位置{int x1 = rand() % row + 1;int y1 = rand() % col + 1;if (board[x1][y1] == '0'){board[x1][y1] = '1';break;		//跳出重置第一次雷的循环}}time = 0;	//time置为0表示不是第一次if (0 == Num(board, x, y)){ShowBoard[x][y] = ' ';int count = Blank(board, ShowBoard, x, y);		//如果重置之后,周围没有雷,展开这个坐标cnt -= count;}else ShowBoard[x][y] = Num(board, x, y) + '0';cnt--;PrintBoard(ShowBoard, row, col);goto end;		//第一次排到雷,判断是否排完}else		//不是第一次{over = 1;break;	//跳出排雷循环}}else if (board[x][y] == '0'){time = 0;if (0 == Num(board, x, y)){ShowBoard[x][y] = ' ';int count = Blank(board, ShowBoard, x, y);		//如果周围没有雷,进行展开cnt -= count;}else ShowBoard[x][y] = Num(board, x, y) + '0';cnt--;PrintBoard(ShowBoard, row, col);break;		//跳出排雷循环}}}break;		//跳出switchcase 2:printf("请输入标记的坐标\n");while (1)	//标记循环{int x1 = 0;int y1 = 0;scanf("%d%d", &x1, &y1);if (x1< 1 || x1>row || y1<1 || y1>col)printf("坐标越界,请重新输入\n");else if (ShowBoard[x1][y1] != '*')printf("此处坐标已排查过,请重新输入\n");else if (ShowBoard[x1][y1] == '*'){ShowBoard[x1][y1] = '#';PrintBoard(ShowBoard, row, col);break;		//跳出标记循环}}break;		//跳出switchdefault:printf("输入错误,请重新输入\n");break;	//跳出switch}end :if (over){printf("你失败了,游戏结束\n");PrintBoard(board, row, col);break;		//游戏结束}if (cnt == 0){printf("恭喜你,成功排雷\n");PrintBoard(board, row, col);break;		//游戏结束}}
}

运行结果:雷数为10
在这里插入图片描述
雷数为1:
在这里插入图片描述

雷数为2:
在这里插入图片描述
雷数为79(这里我们打印出雷的布局,方便我们测试):
在这里插入图片描述
雷数为80(随便输入一个坐标都会取得胜利):
在这里插入图片描述

三、完整代码

GitHub链接

扫雷初级版
扫雷优化版

Gitee链接

扫雷初级版
扫雷优化版

这篇关于扫雷游戏(C语言实现)初级版和优化版(增加了自动展开、标记地雷功能,同时排除了第一次排到地雷的情况)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821242

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount