扫雷---C语言

news2024/11/24 12:52:44

目录

前言:

1.认识扫雷

1.1游戏构思

1.2碎碎念

2.扫雷接口实现

2.1菜单打印

2.2创建标识符常量和初始化数组

2.3打印棋盘

2.4随机埋雷

2.5排查雷

3.源码

3.1头文件和函数原型声明game.h

3.2游戏函数实现game.c 

3.3测试代码文件test.c 


❤博主CSDN:啊苏要学习

  ▶专栏分类:C语言◀

  C语言的学习,是为我们今后学习其它语言打好基础,C生万物!

  开始我们的C语言之旅吧!✈


前言:

  继三子棋小游戏,我们继续来实现一个扫雷游戏。即使学校里有许多作业没完成,也依旧阻挡不了博主总结写博客的态势!

1.认识扫雷

  扫雷游戏简单来说就是在一个棋盘上,玩家选择未知的小格子,这个格子如果是雷,扫雷失败游戏结束,如果不是雷,就在这个格子里显示基于这个小格子一周未显示的格子存在的雷的个数

  随着我们一直排雷,最后把棋盘上所有不是雷的格子选出来,游戏就胜利了

1.1游戏构思

  这个游戏需要用什么去实现呢?我们先在网上看一下扫雷游戏的是长啥样的,好确定实现需要的元素

  (存储和初始化)要有这个9*9的棋盘,我们可以使用一个9行9列的二维数组,然后为这个数组做一个初始化的接口函数。(埋雷)初始化完后,我们自己决定用什么符号表示雷(比如可以使用数字1来表示是雷),然后往棋盘里随机布置雷的存储位置,也就是把数组里的对应元素改成表示雷的符号

  (显示棋盘)还有一个重要的点是:显示棋盘,在选择玩游戏之后,第一事是先把棋盘打印出来给玩家看由于我们现在没有使用界面化的工具(没有像上图那样有那些蓝色方格子),为了让棋盘没有被排查过的格子有些神秘感,最开始我们将棋盘初始化为字符'*'(星)


  这时候读者有没有发现如果仅使用一个二维数组会有些冲突的地方呢我们请看到埋雷那一部分,我们说假设用数字1来表示埋的雷,现在在显示棋盘的时候又说要用*来表示未排查过的格子,那么未被排查过的又是雷格子该怎么表示?使用一些其它的符号吗?那玩家玩上一局发现规律后就完全通关了。那既然这样,我们就使用两个一模一样的二维数组吧,一个用来显示,一个用来表示雷的信息。因为我们使用*字符来初始化显示棋盘,那干脆用字符数组吧,埋雷的棋盘用字符'1'表示雷。

  总结创建两个9行9列的字符数组,一个是用来显示的,一个使用来放雷的信息的显示的我们将它初始化为'*',在经过排查后,'*'变成表示该格子周围一圈8个格子潜藏的雷的个数(字符3和数字3虽然不是同一个东西,但我们能用来表示是3个的意思)埋藏雷的信息的棋盘我们不需要打印出来(相当于是一个后台,玩家通过对界面显示的棋盘进行操作,前台的操作对应后台,形成对接这种感觉~),我们就把埋雷的棋盘叫雷盘吧,雷盘最初我们就假设初始化字符'0',埋雷的时候,把该是雷的位置里的'0'改成'1'就可以了

  (排雷)那么还有一个问题就是,如何联系雷盘与显示棋盘,也就是说,在排雷的时候,我们根据显示的棋盘输入一个未排查的坐标后,应该找到对应的雷盘上的坐标,计算出周围雷的个数后,返回来覆盖掉显示棋盘位置上的'*'

  我们看下面这种情况:

  但是我们的数组只有9行9列,黄色部分超出了数组范围,如果对其进行访问就造成了非法访问内存,程序会挂掉,那该怎么做我们不妨将数组扩大一圈,变成成11行11列(因为横向最顶部,最底部多了一行,相当于加了两行,加了最左列和最右列,相当于加了两列)。雷盘和显示盘在初始化的时候,11行11列都初始化,虽然说显示棋盘只用的部分是9*9,但我们也选择初始化11行11列(因为这样就不会在初始化两个棋盘的时候,传参的行列不一致)在显示显示棋盘的时候依旧是9*9的部分

1.2碎碎念

  首先,如果读者能读到这里,那么先为你叫好,毕竟前面那么多的构思文字都看得下来,说明你是一个很有耐心的读者。哈哈,那么接下来是实现扫雷的接口函数部分啦:

2.扫雷接口实现

2.1菜单打印

  和三子棋一样,博主就直接写出来了:

void menu()
{
	printf("*****************\n");
	printf("*** 1. play  ****\n");
	printf("*** 0. exit  ****\n");
	printf("*****************\n");
}

void game()
{
	printf("扫雷游戏\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("这是一个扫雷游戏,请选择>:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//接着就是实现game函数
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

2.2创建标识符常量和初始化数组

game.h

  ROW和COL是给棋盘真正有用的部分(9*9)设的, 当我们在排查雷或打印棋盘,不需要用(11*11) 

test.c

game.c

2.3打印棋盘

  打印的是显示棋盘9*9的部分:

void Display(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-----扫雷游戏-----\n");
    //打印一行列数
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
    
	for (i = 1; i <= row; i++)
	{
        //在打印一行显示棋子之前打印该行的行数
		printf("%d ", i);
        //打印棋子
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//第1行到第9行是棋盘的有效部分
		}							   //第0行和第10行是外面一圈
		printf("\n");
	}
}

  这里需要补充一点的是:我们打印出来的辅助玩家输入的坐标行和数组的元素是不是对应的关系定义两个都是11行11列的数组的,行的下标和列的下标都是从0-10。我们看下面的图:

  打印完棋盘后,现在就是在雷盘上埋雷啦~,得先有雷才能排雷嘛。

2.4随机埋雷

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;//埋雷是一个循环,用一个未埋的雷数来控制循环
	while (count)
	{
		//随机生成1-9的x,y坐标
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//判断是否是可以埋雷的位置
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;//成功埋一颗雷,剩余雷数减一
		}
	}
}

  既然雷已经埋完了,现在我们再来把排雷的接口函数实现一下吧。

2.5排查雷

  因为显示用的棋盘和存储雷的信息的棋盘它们是一模一样的,所以它们的坐标是相对应的。

//获取雷的个数
int Get_MineCount(char mine[ROWS][COLS], int x, int y)
{
	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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row*col-EASY_COUNT)//win是排掉不是雷的个数等于棋盘减去雷的个数时游戏胜利,不用再继续输入坐标排雷了
	{
		printf("请输入排查坐标:>");
		scanf("%d%d", &x, &y);
        //判断坐标合法性
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,排雷失败,你被炸死了\n");
				Display(mine, row, col);
				break;
			}
			else
			{
				int count = Get_MineCount(mine, x, y);//获取附近雷的个数
				show[x][y] = count + '0';
				Display(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入坐标不合法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		Display(mine, row, col);
	}
}

  将排查的位置周围一圈都加起来,减去8个字符'0'的ASCII码值,等于字符1的个数字符1的ASCII码值是49,字符0的ASCII码值是48,比如现在排查的位置周围有3颗雷,那么一圈加下来就是8个字符0加上3,我们减去8个字符0,让Get_MineCount返回数字3。那么show数组对应的坐标要赋值成3+'\0'是为什么呢?因为这是字符为元素的数组,加上字符0使其成为相应的数字字符,打印出来也可以表示雷的个数。

  好啦,这样就实现我们的简单版的扫雷游戏啦,还有一些功能我们没有实现的就是,当我们排查一个坐标的时候,展开一大片,这是需要使用递归实现的还有标记的功能,图形化界面等等,我们还是一样,博主学有余力的时候再出一篇博客

  由于没有递归展开一片的功能,博主玩胜利一把需要输入71个坐标,哈哈~

3.源码

3.1头文件和函数原型声明game.h

game.h
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>//包含常见的头文件
#include <stdlib.h>
#include <time.h>

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

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void Display(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

3.2游戏函数实现game.c 

game.c

#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

void Display(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-----扫雷游戏-----\n");
	//从零开始可以使其对齐
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//第1行到第9行是棋盘的有效部分
		}							   //第0行和第10行是外面一圈
		printf("\n");
	}
}

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;//埋雷是一个循环,用一个未埋雷数来控制循环
	while (count)
	{
		//随机生成1-9的x,y坐标
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//判断是不是可以埋雷的位置,不能重复埋雷
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;//成功埋一颗雷,剩余雷数减一
		}
	}
}

int Get_MineCount(char mine[ROWS][COLS], int x, int y)
{
	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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row*col-EASY_COUNT)
	{
		printf("请输入排查坐标:>");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,排雷失败,你被炸死了\n");
				Display(mine, row, col);
				break;
			}
			else
			{
				int count = Get_MineCount(mine, x, y);//获取附近雷的个数
				show[x][y] = count + '0';
				Display(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入坐标不合法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		Display(mine, row, col);
	}
}

3.3测试代码文件test.c 

test.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void menu()
{
	printf("*****************\n");
	printf("*** 1. play  ****\n");
	printf("*** 0. exit  ****\n");
	printf("*****************\n");
}

void game()
{
	char mine[ROWS][COLS];//雷盘
	char show[ROWS][COLS];//显示盘
	InitBoard(mine, ROWS, COLS, '0');//将11行11列的雷盘都初始化成字符0
	InitBoard(show, ROWS, COLS, '*');
	Display(show, ROW, COL);
	SetMine(mine, ROW, COL);
	FindMine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	//使用rand()生成随机数必须要调用一次srand
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("这是一个扫雷游戏,请选择>:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//接着就是实现game函数
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

  讲到这里,结束~ 


结语:希望读者读完有所收获!在学C的路上,祝福我们能越来越C!✔

  读者对本文不理解的地方,或是发现文章在内容上有误等,请在下方评论区留言告诉博主哟~,也可以对博主提出一些文章改进的建议,感激不尽!最后的最后!

  ❤求点赞,求关注,你的点赞是我更新的动力,一起努力进步吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HEVC编码标准介绍

视频编码标准的发展历程 目前已经有H266、AVS3、AV1等新编码标准。 H264的编码劣势 宏块个数的爆发式增长&#xff0c;会导致用于编码宏块的预测模式、运动矢量、参考帧索引、量化等宏块级参数信息所占用的码字过多&#xff0c;用于编码残差部分的码字明显减少&#xff1b;…

C++实现并查集

1.并查集原理 在一些应用问题中&#xff0c;需要将n个不同的元素划分成一些不相交的集合。开始时&#xff0c;每个元素自成一个 单元素集合&#xff0c;然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一 个元素归属于那个集合的运算。适合于描述这类…

编译原理----词法分析设计

程序设计实验1 词法分析 一、实验目的&#xff1a; 通过设计编制调试一个具体的词法分析程序&#xff0c;加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 二、实验内容 编制一个单词获取程序&#xff0c;从文件…

门店销售干货 | 4种不同类型的顾客VS销售技巧,直接套用!

“我就路过随便看看” “我在别人家看到的更便宜” “我自己看&#xff0c;你不要跟着我” “我下次再买” …… 在日常的门店经营过程中&#xff0c;你是否经常遇到不同类型的顾客&#xff0c;用各种不同的话拒绝你&#xff0c;最后成交的寥寥无几。 面对不同这样不同类型…

java异常总结

java异常总结 什么是异常&#xff1f; 在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常。 常见异常举例 1.算数异常ArithmeticException public class Test{public static void main(String[] args) {System.out.println(10/0);} }报错 ArithmeticExcept…

webhub123整理 中文语音识别数据集​

我们收集和整理了常用的中文语音识别数据集&#xff0c;合计超过12000小时的数据集。已经按照不同来源整理收录到 webhub123整理 中文语音识别数据集​https://www.webhub123.com/#/home/detail?projectHashid64335220&ownerUserid22053727 整理后的效果如下 ​ 每个卡片…

mysql Lock wait timeout exceeded; try restarting transaction

文章目录 一、mysql死锁及超时的原因二、mysql死锁排查思路1、show full processlist 查询当前数据库全部线程2、information_schema 一、mysql死锁及超时的原因 当在业务逻辑中看到这个错误&#xff0c;或者mysql中使用update语句更新数据报错&#xff1a; Lock wait timeout…

数影周报:丰田215万名日本客户信息被无意泄露,菜鸟将于明年初在港IPO

本周看点&#xff1a;丰田215万名日本客户信息被无意泄露&#xff1b;美光宣布吴明霞出任美光中国区总经理&#xff1b;谷歌将向Gmail用户提供暗网数据泄露报告&#xff1b;淘宝天猫集团架构调整完成&#xff1b;菜鸟计划于2024年初在香港IPO...... 数据安全那些事 丰田215万名…

体验了基于ChatGPT的谷歌翻译插件后,我把其他翻译插件移除了

最近&#xff0c;一个基于 ChatGPT 的谷歌浏览器翻译插件挺火的&#xff0c;我体验了下&#xff0c;总结下来就一个字“666”。 github 上已经有 14.9k 的 star 了 传送门&#xff1a;GitHub - yetone/openai-translator: 基于 ChatGPT API 的划词翻译浏览器插件和跨平台桌面端…

AI学术界无人后继?高校毕业生纷纷进厂,全是香饽饽

来源 | 新智元 微信号&#xff1a;AI-era 【导读】近日&#xff0c;有外媒对一批美国名校的大学生和教授进行了采访。结果显示&#xff0c;高校毕业生入职科技公司已成主流。 AI火&#xff0c;搞AI的人就火。 这不&#xff0c;据Insider最近的一次采访报道&#xff0c;科技类…

Vue3-黑马(五)

目录&#xff1a; &#xff08;1&#xff09;vue3-基础-axios-拦截器 &#xff08;2&#xff09;vue3-基础-条件与列表 &#xff08;3&#xff09;vue3- 基础-监听器 &#xff08;1&#xff09;vue3-基础-axios-拦截器 我们自己创建axios对象有一个好处&#xff0c;就是可以…

[译] Flutter 3.10 的新功能

[译] Flutter 3.10 的新功能 原文 https://medium.com/flutter/whats-new-in-flutter-3-10-b21db2c38c73 无缝的Web和移动端集成&#xff0c;Impeller稳定版的突破性图形性能&#xff0c;以及更多 欢迎使用Flutter 3.10&#xff01;我们非常期待展示我们令人惊叹的Flutter社区所…

java可视化开发工具好用不好用?

java可视化开发工具到底好用不好用&#xff1f;这是不少粉丝朋友经常询问到的一个问题。在数字化发展趋势越发明显的当下&#xff0c;java可视化开发工具可以帮助各中大型企业实现转型升级&#xff0c;它的灵活、简洁、易操作、可视化等功能优势&#xff0c;让很多客户朋友欣慰…

OLYMP‘ARTS 2023奥艺大会中国推介会在北京盛大举行

北京时间2023年5月11日16时&#xff0c;以“艺术连接世界”为主题的OLYMPARTS 2023 国际奥艺大会中国推介会在北京盛大举行。此次活动由国际奥艺委员会&#xff08;WOAC&#xff09;指导支持&#xff0c;共邀请了国外驻华机构、文旅部、央国企、国内外协会机构、知名艺术家代表…

【项目源码】智慧班牌源码 家校互联小程序源码 智慧校园云平台

智慧校园平台源码 智慧班牌源码 人脸识别技术 电子班牌源码 家校互联小程序源码 源码开发环境&#xff1a;Javaspringbootvueelement-uimysql 智慧校园系统定位于中小学教育学校&#xff0c;侧重实际应用&#xff0c;讲究实际&#xff0c;突出加强校园安全监管&#xff0c;德…

【数值模型系列】CMAQ全局属性修改

一、问题产生 在做一个月的浓度预测时&#xff0c;由于GFS只能预报16天左右&#xff0c;因此需要使用CFS气象数据来驱动WRF模型&#xff0c;但CFS在WRF4.x版本有问题&#xff0c;因此重新装了WRF3.9.1版本的WRF。 而我这里的CMAQ ICON会去取前一天的CCTM CONC制作新的ICON文件…

ImageBind 横跨六种数据模式,用向量统一 AI 语言

出品人&#xff1a;Towhee 技术团队 作者&#xff1a;顾梦佳 人工智能&#xff08;AI&#xff09;最近毋庸置疑又迎来了一个高速发展的浪潮。 目前&#xff0c;人工智能的应用已经渗透到各个领域&#xff0c;包括自然语言处理、计算机视觉、语音识别、机器人技术等&#xff0c;…

如何自己搭建Scrapy爬虫框架

前言 当你学了一段时间爬虫后&#xff0c;就会知道各种功能太多而且麻烦。还不如自己整理个框架方便的多。因此&#xff0c;从开始写爬虫程序开始&#xff0c;就会慢慢的接触到一些有关爬虫的框架、效率提升而且扩展也很方便。接下来我将会以Scrapy爬虫框架将我的学习过程记录下…

查找符合条件,且不重复的数据 +++ EXCEL的数组公式(未完成)

查找符合条件&#xff0c;且不重复的数据 1 目标问题 查找第1列&#xff0c;月份为5月&#xff0c;并且第2列不重复的数据个数有2个条件 有的版本有唯一计数&#xff0c;有的没有 2 比较简单的方法 2.1 加辅助列&#xff0c;简单公式&#xff0c;其实逻辑是更清晰的 这里主要…

【数据结构】和栈一样简单的结构——队列

【数据结构】和栈一样简单的结构——队列 一、前言1、什么是队列&#xff1f;2、使用什么结构实现&#xff1f; 二、目标三、实现1、初始化工作2、入队2.1、图解思路2.2、代码实现 3、出队3.1、图解思路3.2、代码实现 4、打印队列(用于测试)5、返回队头元素6、返回队尾元素7、返…