C语言实现扫雷游戏(进阶篇)

2023-11-12

       @C语言实现扫雷游戏(基础篇)的链接在下面@

https://blog.csdn.net/m0_73676323/article/details/129357113https://blog.csdn.net/m0_73676323/article/details/129357113

目录

一)前言

二)爆发式展开  explodespread

三)疑似地雷坐标的标注  signmine

四)代码实现效果

五)完整代码(扫雷)


一)前言

在之前我们讲解了C语言实现扫雷游戏(基础篇)的代码实现,发现程序太过于繁琐,所以今天我们来进行程序的优化。

二)爆发式展开  (explodespread)

 我们知道要完成爆发式展开,我们应该使用函数递归的方式。

而要完成爆发式展开有两个难点:1.当满足什么条件时可以爆发式展开。2.展开是否越界以及重复。下面我们来一一解决。

1)当满足什么条件时可以爆发式展开

 由上图可知只有当我们选择的坐标周围没有地雷的时候我们才可以爆发式展开

2)展开是否越界以及重复访问

 

 

 越界问题:上图是9*9棋盘的一个角落,在基础篇我们知道我们创建了一个11*11的数组,而当我们选择边角的坐标进行展开,由于在初始化数组中我们把mine数组中的元素都初始化为‘0’,所以在展开时会导致访问到了11*11的范围,从而造成越界(如蓝色区域)。

重复问题: 当我们使用递归时我们把周围3*3的无地雷区域展开,应该是把【x】【y】以及周围的8个坐标进行了访问,但是却会导致如:【x-1】【y】坐标会把【x】【y】坐标再一次访问的问题,而导致死循环(如绿色区域)。

解决方案: 1.要限制非法坐标的展开,只需要对坐标进行判断即可

                   2.限制对点位置的重复访问,使得每一个位置只能向四周展开一次

代码展示

void explodespread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	//限制非法坐标的展开
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		int count = getin(mine, x, y);
		if (count == 0)
		{
			show[x][y] = '0';			//无地雷则变为 “0”
            int i = 0;
			for (i = x - 1; i <= x + 1; i++)			
			                                  //向四周共8个位置递归调用
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					//限制对点位置的重复访问,使得每一个位置只能向四周展开一次
					if (show[i][j] == '*')
					{
						explodespread(mine, show, row, col, i, j);
					}
				}
			}
		}
		//标注地雷的个数
		else
		{
			show[x][y] = count + '0';
		}
	}
}

三)疑似地雷坐标的标注  (signmine)

 其实疑似地雷坐标的标注的代码实现非常简单,只需要把玩家在show棋盘中认为的疑似地雷的坐标‘*’改成另一个字符就行了,在这里我们选择‘  !’来作为标记。

代码如下

void signmine(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int n = 1;
	while (n)
	{
		printf("请输入要标记的坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				system("cls");
				printf("已成功标记\n");
				DisPlayBoard(show, row, col);
				printf("请选择是否继续标记(输入“1”继续,输入“0”退出)\n");
				scanf("%d", &n);
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入:\n");
		}
	}

}

四)代码实现效果

扫雷 标记

五)完整代码(扫雷)

  game.h

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define minecount 10

//菜单
void menu();
//初始化雷区
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
//打印雷区
void DisPlayBoard(char board[ROWS][COLS], int row, int col );
//布置地雷
void Setmine(char mine[ROWS][COLS],int row,int col);
//排查雷 
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

  game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

void menu()
{
	printf("<<<<<<<********>>>>>>>\n");
	printf("<<<<<<<*1.play*>>>>>>>\n");
	printf("<<<<<<<*0.exit*>>>>>>>\n");
	printf("<<<<<<<********>>>>>>>\n");
}
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for(i=0;i<rows;i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

void DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i =1 ; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <=col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

void Setmine(char mine[ROWS][COLS],int row, int col)
{
	int x = 0;
	int y = 0;
	int count = minecount;
	while (count)
	{
		x = rand() % row+ 1;//1~9
		y = rand() % col+ 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

static int getin(char mine[ROWS][COLS],int x,int y)//static有三个作用 1.修饰局部变量 2.修饰全局变量 3.修饰函数
{
	return mine[x][y] + mine[x][y - 1] + mine[x][y + 1] +
		   mine[x - 1][y] + mine[x - 1][y + 1] + mine[x - 1][y - 1] +
		   mine[x + 1][y] + mine[x + 1][y + 1] + mine[x + 1][y - 1]- 9 * '0';//可用循环
}

void explodespread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	//限制非法坐标的展开
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		//计算该位置附近四周地雷的个数
		int count = getin(mine, x, y);
		//若四周没有一个地雷,则需要向该位置的四周展开,直到展开到某个位置附近存在地雷为止
		if (count == 0)
		{
			//把附近没有地雷的位置变成字符 “0”
			show[x][y] = '0';
			int i = 0;
			//向四周共8个位置递归调用
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					//限制对点位置的重复展开调用,使得每一个位置只能向四周展开一次
					if (show[i][j] == '*')
					{
						explodespread(mine, show, row, col, i, j);
					}
				}
			}
		}
		//若四周存在地雷则应该在这个位置上标注上地雷的个数
		else
		{
			show[x][y] = count + '0';
		}
	}
}

void signmine(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int n = 1;
	while (n)
	{
		printf("请输入要标记的坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				system("cls");
				printf("已成功标记\n");
				DisPlayBoard(show, row, col);
				printf("请选择是否继续标记(输入“1”继续,输入“0”退出)\n");
				scanf("%d", &n);
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入:\n");
		}
	}

}

void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int n;
	while (win < row * col - minecount)
	{
		printf("请输入要排查的位置下标:\n");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你失败了!");
				break;
			}
			else
			{
				int count = 0;
				//count = getin(mine, x, y);
				//show[x][y] = count + '0';
				explodespread(mine, show, ROW, COL, x, y);
				win++;
				printf("\n");
				DisPlayBoard(show, row, col);
				if (win == row * col - minecount)
				{
					printf("恭喜你,排雷成功\n");
					DisPlayBoard(mine, ROW, COL);
				}
				else 
				{
					printf("请选择是否标记地雷:(输入“1”进行确认,输入“0”退出)\n");
					scanf("%d", &n);
					switch (n)
					{
					case 1:
						signmine(show, row, col);
						break;
					default:
						break;
					}
				}
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	
}

  text.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "game.h"

void game()
{
	//定义数组————字符型 
	char mine[ROWS][COLS] = { 0 };//空格
	char show[ROWS][COLS] = { 0 };
	//初始化雷区
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印雷区
	//DisPlayBoard(mine, ROW, COL);//测试用,游戏时不用
	//printf("\n");
	DisPlayBoard(show, ROW, COL);
	//布置地雷
	printf("\n");
	Setmine(mine, ROW, COL);
	//DisPlayBoard(mine, ROW, COL);
	//排查雷 
	Findmine(mine, show, ROW, COL);

}

int main()
{
	int n = 0;
	srand((unsigned int)time(NULL));
	printf("请选择:\n");
	do
	{
		menu();//菜单打印
		scanf_s("%d", &n);
		switch (n)
		{
		case 1:
			system("cls");
			printf("游戏开始》》》》\n");
			game();//游戏函数
			break;
		case 0:
			printf("退出游戏《《《《\n");
			break;
		default:
			printf("erorr\n");
			break;
		}
		printf("请选择是否继续游玩:\n");
	} while (n);
	return 0;
}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言实现扫雷游戏(进阶篇) 的相关文章

随机推荐

  • LeetCode-135.分发糖果、贪心算法

    老师想给孩子们分发糖果 有 N 个孩子站成了一条直线 老师会根据每个孩子的表现 预先给他们评分 你需要按照以下要求 帮助老师给这些孩子分发糖果 每个孩子至少分配到 1 个糖果 相邻的孩子中 评分高的孩子必须获得更多的糖果 那么这样下来 老师
  • 【共享内存】

    1 共享内存示意图 共享内存区是最快的 IPC 形式 一旦这样的内存映射到共享它的进程的地址空间 这些进程间数据传递不再涉及到 内核 换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据 2 共享内存数据结构 struct shmi
  • uniapp 框架引用turf.js步骤

    Turf js中文网 一 安装指定模块 示例 实现运动轨迹缓冲区及迹缓冲面积计算 接口获取的轨迹点连成线 Turf js 轨迹点只有一个则无法连成线 a npm 安装 npm install turf helpers b 页面引入 impo
  • 深入LDO学习

    目录 工程问题 1 LDO 输出电源电平低于设置值 2 电源芯片欠压保护电路导 致上电时序不满足设计的要求 原理 layout图 优点 缺点 原理 选型 热计算 计算 LDO 工作时的结温 工程问题 1 LDO 输出电源电平低于设置值 某款
  • jqueryweui,两个输入框解决方法

    document ready function
  • CURL使用SSL证书访问HTTPS

    在支付的交互过程中 安全绝对是需要考虑的重要因素之一 体现在对服务器交互数据的签名等环节 但有的时候为了能达到更高的安全级别 还需要用ssl证书 即web服务器有证书 浏览器客户端 请求端也需要安装证书来达到双向验证 比如请求下面的财付通支
  • Linux:169.254.0.0/24路由的来龙去脉

    在Linux中 发现每次系统启动时 都会将 169 254 0 0 16 路由启动并将其添加到路由表中 但是并不知道这条路由具有什么功能和它到底来自于哪里 要想搞清楚路由 169 254 0 0 16 究竟来自哪里并且它的作用是什么 首先需
  • Contest3032 - 计科2101~2104算法设计与分析上机作业03

    目录 问题 A 质数 问题 B 分治法求解全排列问题 问题 C 数的计数 问题 D 最大公共子序列问题 问题 E 分解式的个数 问题 F 矩阵最优连乘问题 问题 A 质数 题目描述 判断给定的一组正整数是否为质数 输入 第一行为测试数据的个
  • linux创建新用户

    只需要四步就能创建一个新的linux用户 1 打开终端 快捷键Ctrl Alt T 2 创建用户和密码 sudo useradd m aaa 创建用户名为aaa的用户 m 自动建立用户的登入目录 sudo是允许系统管理员让普通用户执行roo
  • 在地址栏里输入一个地址回车会发生哪些事情

    解析URL 首先会对 URL 进行解析 分析所需要使用的传输协议和请求的资源的路径 如果输入的 URL 中的协议或者主机名不合法 将会把地址栏中输入的内容传递给搜索引擎 如果没有问题 浏览器会检查 URL 中是否出现了非法字符 如果存在非法
  • libev学习系列之四:ev_loop事件循环

    libev学习系列之四 ev loop事件循环 版本说明 版本 作者 日期 备注 0 1 ZY 2019 5 31 初稿 目录 文章目录 libev学习系列之四 ev loop事件循环 版本说明 目录 一 前言 二 描述 三 例子 一 前言
  • tensorflow(十七)关于tensorboard网络运行时参数的查看

    关于tensorboard网络运行时查看参数主要包括网络的权值和偏差
  • [学习笔记-opencv篇]ubuntu系统下运行opencv自带双目标定+立体匹配程序

    ubuntu系统下运行opencv自带双目标定 立体匹配 双目标定 立体匹配 找了很多opencv自带双目标定的资料 发现大多数都是使用vs opencv或matlab运行的 也可能是找的还不够 参考了一些资料 写了个cmake 然后一通操
  • 机械革命深海幽灵z2学习电脑系统史

    目录 前言 第一次蓝屏 第一次接触重装系统 装Linux系统 Linux16 04版本与显卡1060不兼容问题 windows与linux双系统问题 Linux学习 售后 自己重装系统 换主板 没有我修不好的电脑 键盘故障 再次蓝屏 重装系
  • [精华]uniapp微信授权登录,

    转载一 微信授权登录 转载二 uniapp页面速成提效工具 uniapp uview ui 可视化 完全自由拖拽 一键生成flex代码网站 http aicode shagua wiki uni index html 十大特性 1 可视化
  • Unity屏幕坐标转UI局部坐标

    RectTransformUtility ScreenPointToLocalPointInRectangle https docs unity cn cn 2019 2 ScriptReference RectTransformUtili
  • 在C语言多维数组a[3][2]中的a[2]代表的是什么意思

    一开始我以为a 2 是等价与a 2 0 也就是第三行的第一个元素 后来发现错误 a 2 是等价与 a 2 其值是第二行的首地址 a 2 0 等价于 a 2 个人认为可以把二位数组的数组名理解成一个二维指针 解运算一次 放的是地址 行地址 解
  • 08:js逆向---压缩技术

    可以看到了变的参数只有一个 toke 接下来解决toke 直接搜索 通过window Rohr Opt reload函数加密 reqUrlAndParams是在上面生成的 进去函数里面看 iP reload function jv jv最初
  • Win10家庭版远程桌面工具RDP Wrapper

    由于win10家庭版官方不支持使用远程控制mstsc工具 所以使用RDP Wrapper可以解决该问题 软件环境如下 不止当前版本 经过验证在21H2版本上依旧可行 如果嫌弃阅读文章浪费时间可以直接下载打包好的压缩文件 直接替换就可以使用
  • C语言实现扫雷游戏(进阶篇)

    C语言实现扫雷游戏 基础篇 的链接在下面 https blog csdn net m0 73676323 article details 129357113https blog csdn net m0 73676323 article de