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

相关文章

Python38个游戏开发库整理汇总

《Python38个游戏开发库整理汇总》文章介绍了多种Python游戏开发库,涵盖2D/3D游戏开发、多人游戏框架及视觉小说引擎,适合不同需求的开发者入门,强调跨平台支持与易用性,并鼓励读者交流反馈以... 目录PyGameCocos2dPySoyPyOgrepygletPanda3DBlenderFife

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

C语言中%zu的用法解读

《C语言中%zu的用法解读》size_t是无符号整数类型,用于表示对象大小或内存操作结果,%zu是C99标准中专为size_t设计的printf占位符,避免因类型不匹配导致错误,使用%u或%d可能引发... 目录size_t 类型与 %zu 占位符%zu 的用途替代占位符的风险兼容性说明其他相关占位符验证示

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买