扫雷小游戏【C语言】

目录

前言

一、基本实现逻辑

二、实现步骤

1. 我们希望在进入游戏时有一个菜单让我们选择

2. 我们希望可以重复的玩(一把玩完了还可以接着玩)

3. 采用多文件形式编程

 4.要扫雷先得有棋盘(创建棋盘R*N)

5.初始化棋盘 

6.打印棋盘

7.设置雷

8.排查雷

三、全部源码:


前言

上期我们介绍了三子棋小游戏,玩法也比较简单。这一期我们讨论的是扫雷小游戏。什么是扫雷小游戏?简而言之,就是在一个的棋盘上布置n个雷,把棋盘不显示让你来找雷的过程!OK,来一张图片看一看:

就是这样的一个游戏!下面我们就来用C语言简单实现一扫雷小游戏吧!

一、基本实现逻辑

还是和上期一样,在开始前我们先理清楚整个工程的思路然后在开始敲,不然一开始就狂敲到最后又是一堆的Bug那就不太好了! OK,我们下来梳理一下完成这个小游戏的基本逻辑:

(1)进入游戏时有一个菜单让我们选择。

(2)可以重复的玩(一把玩完了还可以接着玩)。

(3)采用多文件形式编程

(4)要扫雷先得有棋盘(创建棋盘9*9)

(5)初始化棋盘    

(6)打印棋盘

(7)设置雷

(8)排雷

二、实现步骤

1. 我们希望在进入游戏时有一个菜单让我们选择

#include"game.h"

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

int main()
{
	menu();
	return 0;
}

2. 我们希望可以重复的玩(一把玩完了还可以接着玩)

这个我们已经在上一期中介绍过用do...while循环实现!

#include"game.h"

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

int main()
{	
	int input = 0;
	do
	{
		menu();
		printf("请选择操作数:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("扫雷!\n");
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default :
			printf("操作数非法!请重新输入!\n");
			break;
		}
	} while (input);

	return 0;
}

3. 采用多文件形式编程

首先为什么要采用多文件编程?

让文件功能相对简洁,不同文件进行执行不同的功能!也可减少代码的冗余!例如:#include<stdio.h>这个头文件和一些函数声明可能在多个不同的文件中使用,那么就得反复声明!为了减少这种情况,我们采用多文件形式编程,把相应的功能都封装好,谁用谁调即可!这大大的提高了简洁性和代码的复用性!

采用多文件编程的好处:

让对应的代码块的功能独立,后期调试的时候也相对容易!例如:game.h就专门放一些头文件和申明。game.c游戏实现的各种代码块。test.c用于测试!

 4.要扫雷先得有棋盘(创建棋盘R*N)

我们分析,我们得要有两个棋盘(二维数组)----》一个用于存储雷!一个用于给用户展示用于排雷!另外,我们还要考虑到不能把数组的行和列给写死,不然后面改的话就会有很多的地方要改会极其麻烦!(例如,目前可能需要的是9*9,后期要是变需求要5*5,那改的地方可就很多了)因此,我们不能把写死,我们的建议是,用#define 定义的字符常量,后期即使需求改了,我们只需要改一个地方即可!还有就是扫雷的时候,实际是遍历数组,但如果遍历到最边上的话就没有8个位置空间让其遍历了,只时候就会越界(前面有一期“函数栈的帧创建与销毁”中可知道C语言中数组存在栈区,而数组开辟的空间实际在main函数的栈帧上,我们知道在没有初始化的栈帧是0hcccccc,所以一旦越界,它的值是不可控的),为了不让越界,我们把设置类的数组Mine设置为比展示数组show多一行一列就可以完美的解决这个问题了!

void game()
{
	//创建数组
	char mine[RS][CS] = { 0 };
	char show[RS][CS] = { 0 };
}
#define R 9
#define C 9

#define RS R+2
#define CS C+2

5.初始化棋盘 

我们想让 mine数组初始化为字符 0 , 让show 数组初始化为 *(给用户神秘感),为什么给mine数组是字符0而不是#等符号呢?后面在排雷的时候会解释清楚字符0的好处!另外,我们进行初始化的时候都采用RS行和CS列,打印的时候(show数组)让他打印R行C列就OK了。我么初始化两个棋盘要初始化两次,我们直接写一个函数专门用来初始化!

void InitBoard(char board[RS][CS], int rs, int cs, char c)
{
	for (int i = 0; i < rs; i++)
	{
		for (int j = 0; j < cs; j++)
		{
			board[i][j] = c;
		}
	}
}

6.打印棋盘

和上面的初始化一样后期可能会有很多次的打印,我们如果每次都去实现就会造成代码的冗余!因此我们还是封装成函数,用的时候直接调即可!

void DisplayBoard(char board[RS][CS], int r, int c)
{
	for (int i = 1; i <= r; i++)
	{
		for (int j = 1; j <= c; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

OK来看一下效果:

两个数组已经都创建好了!但似乎有点不太好,让人顶不住某个元素在几行几列,这就会让我们后期排雷产生影响!因此我么对此进行优化(版本2):

void DisplayBoard(char board[RS][CS], int r, int c)
{
	for (int i = 0; i <= r; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	printf("  -----------------\n");
	for (int i = 1; i <= r; i++)
	{
		printf("%d|", i);
		for (int j = 1; j <= c; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

这样的确比第一次好多了,但还是没有棋盘的好,我们直接打印成棋盘(版本3):

void DisplayBoard(char board[RS][CS], int r, int c)
{
	printf("----------------扫雷游戏---------------\n");
	//列号
	for (int i = 0; i <= r; i++)
	{	
		if (i == 0)
			printf(" %d ", i);
		else 
			printf(" %d  ", i);
	}
	printf("\n");
	printf("\n");
	for (int i = 1; i <= r; i++)
	{
		printf(" %d ", i);//行号
		for (int j = 1; j <= c; j++)
		{
			printf(" %c ", board[i][j]);
			if (j <= c - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		if (i <= r - 1)
		{
			printf("   ");
			for (int j = 1; j <= c; j++)
			{
				printf("---");
				if (j <= c - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
		
	}
	printf("\n");
}

看效果:

这样就看起来好一点了!

7.设置雷

随机生成符合的坐标,然后在随机坐标处设置雷(字符1)

void SetMine(char mine[RS][CS], int r, int c)
{
	int x = 0;
	int y = 0;
	
	x = rand() % r + 1;
	y = rand() % c + 1;
	int count = NUM;
	while (count)
	{
		x = rand() % r + 1;
		y = rand() % c + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

8.排查雷

我们等输入合理的坐标后进行排查,如果该位置是‘1’则显示被炸死!否则,对周围八个位置排查,我们一开始初始化的时候对mine是进行的初始化为'0',而我们知道一个数字 + 一个字符0就是对应的字符,反之一个字符 - '0', 就是对应的数字!因此我们可以直接对x,y周围的八个坐标进行相加然后 - 8*'0';便是其周围雷的个数!把这个数要放到show数组里面显示给用户(玩家)就得再上‘0’即可!如何判断排雷成功?我们采用计数器与总数之间的差值进行比对!设置一个变量计数器win 当成功排雷一次++,如果r*c-NUM(雷的数) 不大于win 后就说明把所有的空位全部排完了剩下的全部都是雷!此时说明排雷成功!

int GetCount(char mine[RS][CS], int x, int y)
{
	return  (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
		mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +
		mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');
}

void FindMine(char mine[RS][CS], char show[RS][CS], int r, int c)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (r * c- NUM > win )
	{
		printf("请输入要排查的坐标:> ");
		scanf("%d %d", &x, &y);

		if (x >= 1 && x <= r && y >= 1 && y <= c)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了!\n");
				DisplayBoard(mine, R, C);
				break;
			}
			else
			{
				int n = GetCount(mine, x, y);
				show[x][y] = n + '0';
				DisplayBoard(show, R, C);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}

	if (r * c - NUM == win)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, R, C);
	}
}

三、全部源码:

game.h

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

#define R 9
#define C 9

#define RS R+2
#define CS C+2
#define NUM 10

//初始化
void InitBoard(char mine[RS][CS], int rs, int cs, char c);
//打印
void DisplayBoard(char board[RS][CS], int r, int c);
//设置雷
void SetMine(char mine[RS][CS], int r, int c);
//排查雷
void FindMine(char mine[RS][CS], char show[RS][CS] ,int r, int c);

game.c

#include"game.h"

void InitBoard(char board[RS][CS], int rs, int cs, char c)
{
	for (int i = 0; i < rs; i++)
	{
		for (int j = 0; j < cs; j++)
		{
			board[i][j] = c;
		}
	}
}


//void DisplayBoard(char board[RS][CS], int r, int c)
//{
//	for (int i = 0; i <= r; i++)
//	{
//		printf("%d ", i);
//	}
//	printf("\n");
//	printf("  -----------------\n");
//	for (int i = 1; i <= r; i++)
//	{
//		printf("%d|", i);
//		for (int j = 1; j <= c; j++)
//		{
//			printf("%c ", board[i][j]);
//		}
//		printf("\n");
//	}
//}




void DisplayBoard(char board[RS][CS], int r, int c)
{
	printf("----------------扫雷游戏---------------\n");
	//列号
	for (int i = 0; i <= r; i++)
	{	
		if (i == 0)
			printf(" %d ", i);
		else 
			printf(" %d  ", i);
	}
	printf("\n");
	printf("\n");
	for (int i = 1; i <= r; i++)
	{
		printf(" %d ", i);//行号
		for (int j = 1; j <= c; j++)
		{
			printf(" %c ", board[i][j]);
			if (j <= c - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		if (i <= r - 1)
		{
			printf("   ");
			for (int j = 1; j <= c; j++)
			{
				printf("---");
				if (j <= c - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
		
	}
	printf("\n");
}


void SetMine(char mine[RS][CS], int r, int c)
{
	int x = 0;
	int y = 0;
	
	x = rand() % r + 1;
	y = rand() % c + 1;
	int count = NUM;
	while (count)
	{
		x = rand() % r + 1;
		y = rand() % c + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}


int GetCount(char mine[RS][CS], int x, int y)
{
	return  (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
		mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +
		mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');
}

void FindMine(char mine[RS][CS], char show[RS][CS], int r, int c)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (r * c- NUM > win )
	{
		printf("请输入要排查的坐标:> ");
		scanf("%d %d", &x, &y);

		if (x >= 1 && x <= r && y >= 1 && y <= c)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了!\n");
				DisplayBoard(mine, R, C);
				break;
			}
			else
			{
				int n = GetCount(mine, x, y);
				show[x][y] = n + '0';
				DisplayBoard(show, R, C);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}

	if (r * c - NUM == win)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, R, C);
	}
}

test.c

#include"game.h"

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

void game()
{
	//创建数组
	char mine[RS][CS] = { 0 };
	char show[RS][CS] = { 0 };

	//初始化数组
	InitBoard(mine, RS, CS, '0');
	InitBoard(show, RS, CS, '*');

	//打印数组
	//DisplayBoard(mine, R, C);
	DisplayBoard(show, R, C);

	//设置雷
	SetMine(mine, R, C);
	//DisplayBoard(mine, R, C);

	//排查雷
	FindMine(mine,show, R, C);
}

int main()
{	
	system("color 3");
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择操作数:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			//printf("扫雷!\n");
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default :
			printf("操作数非法!请重新输入!\n");
			break;
		}
	} while (input);

	return 0;
}

OK !好兄弟我们下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络安全】深入解析 PHP 代码审计技术与实战

前言 登录某个网站并浏览其页面时&#xff0c;注意到了一些看起来不太对劲的地方。这些迹象可能是该网站存在漏洞或被黑客入侵的标志。为了确保这个网站的安全性&#xff0c;需要进行代码审计&#xff0c;这是一项专门针对软件代码进行检查和分析的技术。在本文中&#xff0c;…

一、Docker介绍

学习参考&#xff1a;尚硅谷Docker实战教程、Docker官网、其他优秀博客(参考过的在文章最后列出) 目录 前言一、Docker是什么&#xff1f;二、Docker能干撒&#xff1f;三、容器虚拟化技术 和 虚拟机有啥区别&#xff1f;1.虚拟机2.容器虚拟化技术3.对比4.Docker为啥比VM虚拟机…

献给蓝初小白系列(二)——Liunx应急响应

1、Linux被入侵的症状​​ ​​https://blog.csdn.net/weixin_52351575/article/details/131221720​​ 2、Linux应急措施 顺序是&#xff1a;隔离主机--->阻断通信--->清除病毒--->可疑用户--->启动项和服务--->文件与后门--->杀毒、重装系统、恢复数据 …

AAC ADTS格式分析

标题 1.AAC简介2. AAC ADTS格式分析2.1 adts_fixed_header详细介绍2.2 adts_variable_header详细介绍 1.AAC简介 AAC音频格式:Advanced Audio Coding(⾼级⾳频解码)&#xff0c;是⼀种由MPEG-4标准定义的有损⾳频压缩格式&#xff0c;由Fraunhofer发展&#xff0c;Dolby, Sony…

vue3 + TS + elementplus + pinia实现后台管理系统左侧菜单联动实现 tab根据路由切换联动内容

效果图&#xff1a; home.vue页面代码 <template><el-container><el-aside width"collapse ? 200px : 70px"><el-button color"#626aef" click"collapseToggle()"><el-icon><Expand v-if"collapse"…

SQL Server 数据加密功能解析

数据加密是数据库被破解、物理介质被盗、备份被窃取的最后一道防线&#xff0c;数据加密&#xff0c;一方面解决数据被窃取安全问题&#xff0c;另一方面有关法律要求强制加密数据。SQL Server的数据加密相较于其他数据库&#xff0c;功能相对完善&#xff0c;加密方法较多。通…

Unity Class深拷贝问题分析

Unity Class深拷贝问题分析 前言常用解决方案1.手动复制字段2.使用序列化工具3.使用Instantiate方法(只能用于MonoBehaviour)4.重写运算符赋值5.使用Visual Scripting中提供的拷贝函数&#xff08;推荐&#xff09; 前言 在Unity项目中&#xff0c;我们面临一个读取数据表并深…

web前端-TypeScript学习

web前端-TypeScript学习 TypeScript 介绍TypeScript 初体验安装编译TS的工具包编译并运行TS代码 TypeScript 常用类型类型注解常用基础类型原始类型数组类型类型别名函数类型对象类型接口元祖类型推论类型断言字面量类型枚举any类型typedof TypeScript 高级类型class类class的基…

笔记:WebRTC 网络技术理论与实战(二)

WebRTC技术笔记 笔记&#xff1a;WebRTC 网络技术理论与实战&#xff08;一&#xff09; 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.n…

C语言之文件的读写(1)

前面三部分已经给大家介绍过了&#xff0c;网址发给大家方便大家复习 打开方式如下&#xff1a; 文件使用方式 含义 如果指定文件不存在 “r”&#xff08;只读&#xff09; 为了输入数据&#xff0c;打开一个已经存在的文本文件 出错 “w”&#xff08;只写&#xff09; 为了输…

OC(iOS)中常见的面试题汇整(大全)

你如何理解OC这门语言的?谈一下你对OC的理解? ​​​​​​​ OC语言是C语言的一个超集,只是在C语言的基础上加上了面向对象的语言特征,如:继承,封装,多态. 封装:把属性和方法封装成一个类,方便我们使用 多态:不同对象对于同一消息的不同响应,子类可以重…

同时安装vue-cli2和vue-cli3

同时安装vue-cli2和vue-cli3 发布时间环境安装后的效果安装vue-cli2安装vue-cli3vue-cli3和vue-cli2的区别vue-cli2目录结构vue-cli3目录结构 发布时间 vue版本发布时间Seed.js2013年vue最早版本最初命名为Seedvue-js 0.62013年12月更名为vuevue-js 0.82014年1月对外发布vue-j…

微软ChatGPT技术的底层支撑——GPU

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来看一看微软ChatGPT技术的底层支撑——GPU。 想要了解GPU&#xff0c;你必须要清楚CPU、GPU、TPU三者的关系。 微软的chatgpt是基于复杂的人工神经网络和强化学习的技术&#xff0c;这是如何运算的…

io.netty学习(八)零拷贝原理

目录 零拷贝 传统I/O操作存在的性能问题 零拷贝技术原理 虚拟内存 mmap/write 方式 sendfile 方式 带有 scatter/gather 的 sendfile方式 splice 方式 总结 io.netty学习使用汇总 零拷贝 零拷贝&#xff08;Zero-Copy&#xff09;是一种 I/O 操作优化技术&#xff0c…

web漏洞-反序列化之PHPJAVA全解(上)(37)

这个很重要 为什么会产生这个东西&#xff1a;序列化之后便于我们对象的传输和保存&#xff0c;这个作用就是为了数据的传递和格式的转换&#xff0c;我们称之为序列化。 在这给过程中&#xff0c;会涉及到一种叫做有类和无类的情况&#xff0c;开发里面经常看到的一个东西&a…

AbstractQueuedSynchronizer源码

介绍 基于队列的抽象同步器&#xff0c;它是jdk中所有显示的线程同步工具的基础&#xff0c;像ReentrantLock/DelayQueue/CountdownLatch等等&#xff0c;都是借助AQS实现的。 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplemen…

使用omp并行技术加速最短路径算法-迪杰斯特拉(Dijkstra)算法(记录最短路径和距离)

原理&#xff1a; Dijkstra算法是解决**单源最短路径**问题的**贪心算法** 它先求出长度最短的一条路径&#xff0c;再参照该最短路径求出长度次短的一条路径 直到求出从源点到其他各个顶点的最短路径。 首先假定源点为u&#xff0c;顶点集合V被划分为两部分&#xff1a;集合…

【玩转Linux操作】Linux服务管理

&#x1f38a;专栏【玩转Linux操作】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;服务(service)管理⭐service管理指令 &…

chatgpt赋能python:Python如何快速提取指定行和列的数据?

Python如何快速提取指定行和列的数据&#xff1f; 在进行数据分析和处理时&#xff0c;常常需要从海量数据中筛选出所需的数据。这时&#xff0c;Python是一款非常强大的工具&#xff0c;可以方便地进行大规模数据清洗和筛选。本文将介绍如何使用Python快速提取指定行和列的数…

chatgpt赋能python:Python提取指定位置字符

Python 提取指定位置字符 Python 是一种高级程序语言&#xff0c;其易读性、简单易学性和易维护性使其成为最受欢迎的编程语言之一。它可以用于各种数据分析和科学计算&#xff0c;包括搜索引擎优化&#xff08;SEO&#xff09;。 在SEO中&#xff0c;提取和处理数据是一个重…