【C语言】扫雷游戏(可展开)——超细教学

news2024/9/23 16:03:29

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页:June-Frost
🚀专栏:C语言

🔥该篇将运用数组来实现 扫雷游戏。

目录:

  • 🌟思路框架
    • 测试
    • 游戏
  • 🌟测试部分函数实现
  • 🌟 游戏部分函数实现
  • 🌟完整的代码
  • ❤️ 结语

🌟思路框架

测试

通过迭代保证每次玩完游戏后可以再来一局或者退出。


游戏

说明:

1 .  实现扫雷,首先需要布置雷,通过一个二维数组就可以很容易的将雷的信息存储下来。只需要将二维数组的元素赋予两个不同的值,例如,’1’ ——雷,’0‘ ——无雷,或者 ‘#’ ——雷,’ * ’ ——无雷,只不过这两种在后续排查雷中操作不同(通过一个循环前者可以加起来计算出周围有几个雷,后者可以通过比较来判定有多少雷)。


 如上图,对于 排查雷(周围有多少雷),如果只有一个数组,那么在周围如果只有一个雷的情况下(’1‘——表示雷,’0‘——表示无雷),该位置的数组元素就会被赋值为1,在最后展示棋盘的时候,这个1就会造成歧义,就会不知道这个’1‘究竟是雷,还是周围雷的个数。
所以,通过建立另一个数组,来记录周围雷的数量。
如图:

3.

但是我们可以将其逻辑统一:

  1.  为了简便操作,我们将两个数组规定为同类型同大小(两个都扩充一圈)。
  • 优点1:可以方便数组操作,下标一样,不需要重新计算对应数组元素的下标。
  • 优点2:同类型意味着可以使用同一个函数,不需要一个功能写两次。

每个部分都是单独的函数体,下面将会单独对每个函数体进行实现。


🌟测试部分函数实现

  • 选择是否玩游戏

 与三子棋一样,该板块的循环部分将会使用do while 循环来实现(保证一开始可以选择,运行完游戏部分后还可以继续选择)。

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

int main()
{
	int input = 0;
	do
	{
		menu();// 菜单
		printf("请选择:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入非法,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

🌟 游戏部分函数实现

 首先我们根据游戏部分的思路框架,将整个游戏的逻辑写在game()函数中。
 前提:

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

定义两种,可以在后续分别用于不同的函数。

  • 游戏逻辑
void game()
{
	char mine[ROWS][COLS] = { 0 };//存放雷
	char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
	//初始化
	InitBoard(mine, ROWS, COLS, '0');//都先初始化为无雷
	InitBoard(show, ROWS, COLS, '*');
	//布置雷
	SetMine(mine, ROW, COL);
	//打印第二个棋盘
	DisplayBoard(show, ROW, COL);
	//排查雷
	FineMine(mine, show, ROW, COL);
}
  • 初始化

 由于想使用一个函数就可以将两个棋盘初始化,所以在参数上需要增加一个变量用于接收初始化的值。

void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}
  • 布置雷

 前提:#define Set_Mine 10;//需要布置雷的数量
 布置雷是随机的,所以我们采用伪随机数,将下标在合法范围内随机,如果该坐标下是无雷的,就赋值‘1’,定为有雷。

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = Set_Mine;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x][y] == '0')
			{
				board[x][y] = '1';
				count--;//布置成功,雷数减少1
			}
		}
	}
}
  • 打印棋盘

 为了方便玩家定位坐标,在打印棋盘的时候,可以将行和列打印出来。并且展示出来的,是中间的区域(不展示扩容区域)

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("--------扫雷游戏-------\n");
	int i = 0;
	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]);
		}
		printf("\n");
	}
}
  • 排查雷

计数:

int GetMineCount(char board[ROWS][COLS], int x, int y)
{
	int i = x - 1;
	int j = y - 1;
	int ret = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			ret += board[i][j];
		}
	}
	return ret - 9 * '0';
}

排雷:

void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
	int win = 0;//如果要赢 需要的有检查的次数,例如:9*9棋盘,有10个雷,检查出71个地区不是雷,就意味着获胜。
	while (win<ROW*COL-Set_Mine)
	{
		int x = 0;
		int y = 0;
		printf("请输入坐标,中间用空格:> ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法
		{
			//踩到雷
			if (mine[x][y] == '1')
			{
				printf("很遗憾,已踩到雷\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			//没猜到雷
			else
			{
				Unfold(mine,show, ROW, COL, x, y,&win);//递归展开
				DisplayBoard(show, ROW, COL);//展示棋盘
			}
		}
		else
		{
			printf("输入非法,需要重新输入\n");
		}

	}
	if (win == ROW * COL - Set_Mine)
	{
		printf("游戏获胜\n");
		DisplayBoard(mine, ROW, COL);
	}
}

展开:

如何实现上面的展开效果呢?
当我们点击一个坐标后,看是否满足3个条件:
1.这个坐标不是雷(如果按照博主的排雷逻辑,这个条件不需要判断,因为坐标下不是雷的时候才会调用展开函数)
2.周围没有雷
3.没有被递归过(防止死递归)
 这时被点击的地方就可以被赋值为空,并对周围的8个区块看是否符合这样的条件,8个区块也按照这个逻辑,如果满足三个条件,被赋空并且遍历周围,如果不满足就显示周围有多少雷,或者不做任何反应,按照这样的逻辑一直延续下去。这种情况我们可以使用递归去解决。

void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y, int* count_w)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)//判断合法
	{
		if (!GetMineCount(mine, x, y) && show[x][y] != ' ')//周围没有雷并且该位置没有被递归过
		{
			show[x][y] = ' ';
			int i = 0;
			int j =0;
			int ret = 0;
			//直接遍历9个,至于中间的那个坐标,最后一个else if 会处理
			for (i = x - 1; i <= x + 1; i++)
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					Unfold(mine, show, row,col,i, j, count_w);
					(* count_w)++;
				}
			}
		}
		else if (GetMineCount(mine, x, y))//周围有雷
		{
			int count = GetMineCount(mine, x, y);
			show[x][y] = count + '0';
			(* count_w)++;
		}
		else if (show[x][y] == ' ')//被递归过
		{
			;//不需要操作
		}
	}
	//不合法的函数不需要任何处理
}

这里需要判断合法性,因为扩容出去的那一圈的元素不可以递归,而且也不可以计算 (不然按照GetMineCount的逻辑会越界访问,而且就会可以计算出,也不会打印这个区域,所以可以直接不用管,递归这些地方的时候,函数会什么都不处理)。


🌟完整的代码

game .h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define Set_Mine 10//需要布置雷的数量
//游戏逻辑
void game();
//初始化
void InitBoard(char board[ROWS][COLS], int row, int col, char set);
//埋雷
void SetMine(char board[ROWS][COLS], int row, int col);
//展示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col); 
//排雷
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game .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 InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = Set_Mine;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x][y] == '0')
			{
				board[x][y] = '1';
				count--;//布置成功,雷数减少1
			}
		}
	}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("--------扫雷游戏-------\n");
	int i = 0;
	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]);
		}
		printf("\n");
	}
}
//计数
int GetMineCount(char board[ROWS][COLS], int x, int y)
{
	int i = x - 1;
	int j = y - 1;
	int ret = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			ret += board[i][j];
		}
	}
	return ret - 9 * '0';
}

//展开
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y, int* count_w)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (!GetMineCount(mine, x, y) && show[x][y] != ' ')//周围没有雷并且该位置没有被递归过
		{
			show[x][y] = ' ';
			int i = 0;
			int j =0;
			int ret = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					Unfold(mine, show, row,col,i, j, count_w);
					(* count_w)++;
				}
			}
		}
		else if (GetMineCount(mine, x, y))//周围有雷
		{
			int count = GetMineCount(mine, x, y);
			show[x][y] = count + '0';
			(* count_w)++;
		}
		else if (show[x][y] == ' ')//被递归过
		{
			;
		}
	}
}
//排雷
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
	int win = 0;
	while (win<ROW*COL-Set_Mine)
	{
		int x = 0;
		int y = 0;
		printf("请输入坐标,中间用空格:> ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//踩到雷
			if (mine[x][y] == '1')
			{
				printf("很遗憾,已踩到雷\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			//没猜到雷
			else
			{
				Unfold(mine,show, ROW, COL, x, y,&win);//递归展开
				DisplayBoard(show, ROW, COL);//展示棋盘
			}
		}
		else
		{
			printf("输入非法,需要重新输入\n");
		}

	}
	if (win == ROW * COL - Set_Mine)
	{
		printf("游戏获胜\n");
		DisplayBoard(mine, ROW, COL);
	}
}

//游戏逻辑
void game()
{
	char mine[ROWS][COLS] = { 0 };//存放雷
	char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
	//初始化
	InitBoard(mine, ROWS, COLS, '0');//都先初始化为无雷
	InitBoard(show, ROWS, COLS, '*');
	//布置雷
	SetMine(mine, ROW, COL);
	//打印第二个棋盘
	DisplayBoard(show, ROW, COL);
	DisplayBoard(mine, ROW, COL);
	//排查雷
	FineMine(mine, show, ROW, COL);
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

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

❤️ 结语

文章到这里就结束了,如果对你有帮助,你的点赞将会是我的最大动力,如果大家有什么问题或者不同的见解,欢迎大家的留言~

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

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

相关文章

SimSolid技术原理解析 衡祖仿真

面向超大规模结构的无网格分析软件Altair SimSolid&#xff0c;自从面世以来&#xff0c;受到广大工程师的关注。SimSolid 是面向设计师、工程师和分析师的颠覆性仿真技术&#xff0c;可在几分钟内对结构复杂的 CAD 装配体进行结构分析。它消除了传统结构仿真中非常耗时、非常专…

农业水价综合改革系统主要组成

一、系统概述 农业水价改革灌区信息化系统主要由感知采集层、网络传输层、系统应用层等部分组成。通过无线技术、感知层技术与新型应用的有效结合&#xff0c;可以用于各种业务的传送&#xff0c;充分满足灌区监测站间的物与物互联&#xff0c;农业生产的自动化和信息化相结合。…

计算机系统简介

计算机系统简介 计算机系统 硬件 计算机实体&#xff0c;如主机、外设软件 具有各类特殊功能的信息&#xff08;程序&#xff09;组成 软件 系统软件 用来管理整个计算机系统 &#xff08;语言处理程序、操作系统、服务性程序、数据库管理系统、网络软件&#xff09;应用…

Apple用户如何利用Twinmotion路径追踪器渲染

Twinmotion 是一款功能强大的实时可视化工具&#xff0c;允许设计人员为建筑设计、产品设计等创建高质量的渲染和交互体验。其当前最强大的渲染功能之一是路径追踪器&#xff0c;它通过精确的照明计算生成逼真的图像。 然而&#xff0c;许多 Apple 用户想知道如何利用 Twinmot…

生物医学翻译,选择专业翻译公司有何优势

我们知道&#xff0c;生物医学翻译是基于生物医学等相关行业的翻译服务&#xff0c;具有较强的专业性和复杂性&#xff0c;为了确保生物医学翻译的质量&#xff0c;务必选择专业的翻译公司。那么&#xff0c;专业翻译公司有何优势&#xff0c;北京生物医学翻译哪家好&#xff1…

【python】Leetcode(primer-set)

文章目录 78. 子集&#xff08;集合的所有子集&#xff09;90. 子集 II&#xff08;集合的所有子集&#xff09; 更多 leetcode 题解可参考&#xff1a;【Programming】 78. 子集&#xff08;集合的所有子集&#xff09; 给定一组不含重复元素的整数数组 nums&#xff0c;返回…

【python】Leetcode(primer-binary)

文章目录 504. 七进制数&#xff08;进制转换&#xff09;461. 汉明距离&#xff08;进制转换 / 异或&#xff09;136. 只出现一次的数字&#xff08;位运算&#xff09; 更多 leetcode 题解可参考&#xff1a;【Programming】 504. 七进制数&#xff08;进制转换&#xff09; …

Python学习 -- 类的继承

类继承是面向对象编程中的重要概念&#xff0c;它允许我们创建一个新的类&#xff0c;通过继承已有的类的属性和方法&#xff0c;从而实现代码的重用和扩展。Python作为一门面向对象的编程语言&#xff0c;提供了强大而灵活的类继承机制。本篇博客将深入探讨Python中类继承的概…

全国规模最大!算力并网行动开启

8月19日&#xff0c;在2023中国算力大会主论坛上&#xff0c;中国移动携手多个超算中心、智算中心&#xff0c;以及云服务商&#xff0c;共同发起全国规模最大的“百川”算力并网行动&#xff0c;打造算力类型最全、规模最大、覆盖最广的“百川”算力并网平台&#xff0c;加快推…

ngix详解

ngix是什么 Nginx是一款轻量级、高性能的Web服务器、反向代理服务器&#xff0c;由于它的内存占用少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;在互联网项目中广泛应用。 正向代理&#xff1a; 客户端访问服务器时&#xff0c;统一通过一个代理服务器把请求…

Flutter实现动画列表AnimateListView

由于业务需要&#xff0c;在打开列表时&#xff0c;列表项需要一个从右边飞入的动画效果&#xff0c;故封装一个专门可以执行动画的列表组件&#xff0c;可以自定义自己的动画&#xff0c;内置有水平滑动&#xff0c;缩放等简单动画。花里胡哨的动画效果由你自己来定制吧。 功…

在本地搭建Jellyfin影音服务器,支持公网远程访问影音库的方法分享

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

爆火「视频版ControlNet」开源了!靠提示词精准换画风,全华人团队出品

“视频版ControlNet”来了&#xff01; 让蓝衣战神秒变迪士尼公举&#xff1a; 视频处理前后&#xff0c;除了画风以外&#xff0c;其他都不更改。 女孩说话的口型都保持一致。 正在插剑的姜文&#xff0c;也能“下一秒”变猩球崛起了。 这就是由全华人团队打造的最新视频处理…

四川玖璨电子商务有限公司:怎么拉升抖店体验分

抖音是中国最受欢迎的短视频分享平台之一&#xff0c;而在抖音上开设自己的抖店成为许多电商从业者的选择。为了提高抖店的曝光度和用户体验&#xff0c;抖店体验分成为了一个重要的指标。在本文中&#xff0c;小编将从两个方面来讨论怎么拉升抖店体验分&#xff0c;包括影响因…

你真的理解 shell 中的 $?

$? 是一个特殊变量&#xff0c;用于获取上一个命令或函数的退出状态码&#xff0c;这里要注意的是状态码和返回值是不同的概念。在函数中我们可以使用返回标志 return&#xff0c;return 的返回值会作为退出状态码供 $? 截取&#xff0c;当函数没有 return 返回值时&#xff…

【Jellyfin影音服务器】 本地部署公网远程影音库

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

程序员至少要掌握几种编程语言?来看看这些编程语言的优势和用途

你可能听说过&#xff0c;作为一个程序员&#xff0c;你要学习很多种编程语言&#xff0c;才能应对各种不同的项目和需求。那么&#xff0c;程序员最少要掌握几种编程语言呢&#xff1f;其实&#xff0c;这个问题没有一个确定的答案&#xff0c;因为不同的编程语言有不同的优势…

若依vue打印的简单方法

像我们后端程序员做前端的话,有时候真不需要知道什么原理,直接塞就好了 我们选用基于hiprint 的vue-plugin-hiprint来打印 目的是为了实现点击某些行的数据,然后点击某个按钮直接弹出下面的打印 此链接 大佬是原创,我拿来总结梳理一下 插件进阶功能请移步: 链接 插件模板制作页…

直播购物系统开发定制:融合技术与个性化的未来购物体验

在数字化时代&#xff0c;直播购物已经成为了商业领域的一大趋势。而通过直播购物系统开发定制&#xff0c;商家能够更好地满足消费者的个性化需求&#xff0c;为购物体验带来前所未有的变革。在本文中&#xff0c;我们将探讨如何通过技术手段来实现直播购物系统的开发定制&…

Layer Normalization(层规范化)

详细内容在这篇论文&#xff1a;Layer Normalization 训练深度神经网络需要大量的计算&#xff0c;减少计算时间的一个有效方法是规范化神经元的活动&#xff0c;例如批量规范化BN&#xff08;batch normalization&#xff09;技术&#xff0c;然而&#xff0c;批量规范化对小批…