扫雷游戏(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

相关文章

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QGroupBox控件的实现

《Qt中QGroupBox控件的实现》QGroupBox是Qt框架中一个非常有用的控件,它主要用于组织和管理一组相关的控件,本文主要介绍了Qt中QGroupBox控件的实现,具有一定的参考价值,感兴趣... 目录引言一、基本属性二、常用方法2.1 构造函数 2.2 设置标题2.3 设置复选框模式2.4 是否

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的