【C语言】扫雷小游戏

文章目录

  • 前言
  • 一、游戏玩法
  • 二、创建文件
    • test.c文件
      • menu()——打印菜单
      • game()——调用功能函数,游戏的实现
      • main()主函数
    • game.c文件
      • 初始化棋盘
      • 打印棋盘
      • 随机布置雷的位置
      • 统计周围雷的个数
      • 展开周围一片没有雷的区域
      • 计算已排查位置的个数
      • 排查雷(包括检测输赢):
    • game.h文件
      • 头文件
      • 棋盘的大小以及雷的个数
      • 主要功能函数的声明
  • 三、完整代码

前言

扫雷游戏与前文的三子棋小游戏有些类似,都是在二维数组的基础上进行的,但扫雷需要考虑的东西更多,也更难一点。今天我们就一起来学习如何实现扫雷小游戏,找回童年的回忆。(最后附有完整代码)

一、游戏玩法

没有玩过扫雷的小伙伴可以打开电脑自带的扫雷小游戏玩两把,很上头有没有,哈哈哈。

游戏规则:

1.首先,扫雷是在一个N*N的棋盘上进行的游戏,这些方格中随机暗藏着一定数量的雷。
2.揭开一个方格,如果没有地雷,则会显示周围8个方格的地雷的数量,如果周围8个方格都没有地雷,则会翻开一片区域。
3.如果揭开的是地雷,那么游戏失败。
4.可以选择标记未揭开的方格为雷,也可以取消标记,方便玩家记忆雷的位置

游戏胜利条件: 不触发地雷,找出所有不是雷的位置。

程序试玩:

初始菜单

在这里插入图片描述

上方是玩家棋盘,下方是布置好的雷盘

在这里插入图片描述

选择1排查雷,点开坐标(4,3)的方格,显示3,说明该坐标周围有3颗地雷

在这里插入图片描述
选择2,标记坐标(4,1)为雷,显示 " ! "

在这里插入图片描述

游戏失败!

在这里插入图片描述

二、创建文件

像三子棋一样,我们同样采用分模块的编程思想,创建三个文件来分别存放对应功能的代码。

test.c文件:主程序,功能的调用
game.c文件:扫雷游戏的具体功能实现
game.h文件:工程需要的头文件和函数声明以及宏定义

这样做的好处有:提高代码的可读性,方便后续调试,条理清晰,使主程序看起来简洁

test.c文件

menu()——打印菜单

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

game()——调用功能函数,游戏的实现

1.创建并初始化棋盘
2.随机布置雷的位置
3.打印棋盘信息
4.排查雷(包括判断游戏输赢)

这些是game.h中的宏定义信息,后面都会用到的,这里先声明一下

#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define count 10
void game()
{
	char mine[ROWS][COLS] = { 0 };//布置好的雷盘
	char show[ROWS][COLS] = { 0 };//排查出的雷盘

	init_board(mine, ROWS, COLS, '0');//mine棋盘全部初始化为0
	init_board(show, ROWS, COLS, '*');//show棋盘全部初始化为*

	set_mine(mine, ROW, COL);	//随机布置雷的位置
	print_board(show, ROW, COL);//打印玩家棋盘
	find_mine(mine, show, ROW, COL);//排查雷
}

之所以创建两个棋盘数组,是因为一个棋盘用来存放系统布置的雷的位置,一个棋盘用来存放玩家手中的棋盘信息。相当于一份是有答案的卷子,一份是玩家需要做的白卷。

创建的棋盘多两行两列是为了 方便后面排查周围8个格子时不会产生越界行为,不然要分情况讨论,很麻烦。显然,直接在创建数组时多开辟一圈更省事。

main()主函数

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择->:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出游戏\n");
			break;
		case 1:
			system("cls");
			game();
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

game.c文件

初始化棋盘

初始化棋盘为ch字符,可以自己定义初始化字符
我们将存放答案的那张棋盘全部初始化为字符0,后面布置有雷则置1
将玩家手中的游戏棋盘全部初始化为 * ,表示未揭开状态

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

打印棋盘

我们每写完一个功能模块,最好调用打印函数来验证是否正确

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

随机布置雷的位置

使用rand()产生随机数,但rand()每次只会产生固定的随机数,所以要在主函数中加入srand((unsigned int)time(NULL));将时间作为seed产生不断变化的随机数

rand()和srand()的使用需要包括头文件#include<stdlib.h>
time()函数的使用需要包括头文件#include<time.h>

void set_mine(char board[ROWS][COLS], int row, int col)
{
	int cnt = count;
	while (cnt)
	{
		int x = rand() % row + 1; //得到1~row的随机数
		int y = rand() % col + 1; //得到1~col的随机数
		if (board[x][y] == '0')
		{
			board[x][y] = '1';//表示该位置布置了雷
			cnt--;
		}
	}
}

统计周围雷的个数

int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	int ret = 0;
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if(i != x || j != y)//周围8个位置,不包括自己
				ret = ret + mine[i][j] - '0';
		}
	}
	return ret;
}

展开周围一片没有雷的区域

如果该位置周围一个雷也没有,则继续打开周围的空白格子直到遇到周围有雷的格子。

void open_area(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	int cnt = get_mine_count(mine, x, y);
	if (cnt == 0)
	{
		show[x][y] = '0';
		for (int i = x - 1; i <= x + 1; i++)
		{
			for (int j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*' && x >= 1 && x <= row && y >= 1 && y <= col)
				{
					open_area(mine, show, ROW, COL, i, j);//递归
				}
			}
		}
	}
	else//直到递归到周围8个位置存在雷的区域
	{
		show[x][y] = cnt + '0';
	}
}

计算已排查位置的个数

int get_win(char board[ROWS][COLS], int row, int col)
{
	int win = 0;
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (board[i][j] != '*' && board[i][j] != '!')
				win++;
		}
	}
	return win;
}

排查雷(包括检测输赢):

首先根据选择,走向不同分支
选择1: 排查雷,首先判断坐标,若不在棋盘范围则重新输入,否则进入下一步判断;该位置若已被排查则重新输入坐标,没有排查过则进行排查,并显示周围雷的个数,同时计算已排查位置的个数
选择2: 标记雷,若该坐标已排查或已标记则重新输入,否则将该坐标置为" ! ",表示已被标记,并打印棋盘信息。
选择3: 取消标记,若该坐标已排查或未被标记则重新输入,否则将“ !”重新置为“ * ”。
其他选择则重新输入。

每选择一次,对win进行判断,若win等于棋盘上非雷个数的总和,则排雷成功,否则继续游戏直到游戏成功或失败。

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int input = 0;
	while (win < (row * col - count))
	{
		printf("请选择->:1.排查雷 2.标记雷 3.取消标记\n");
		scanf("%d", &input);
		if (input == 1)
		{
			printf("请输入要排查的坐标->:");
			scanf("%d%d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				if (show[x][y] == '*' || show[x][y] == '!')
				{
					if (mine[x][y] == '1')
					{
						printf("很遗憾,你被炸死了!!!\n");
						print_board(mine, ROW, COL);
						break;
					}
					else
					{
						open_area(mine, show, ROW, COL, x, y);
						print_board(show, ROW, COL);
						win = get_win(show, ROW, COL);
					}
				}
				else
				{
					printf("该坐标已被排查,请重新输入\n");
				}
				
			}
			else
			{
				printf("坐标非法,请重新输入\n");
			}
		}
		
		else if (input == 2)
		{
			printf("请输入要标记的坐标->:");
			scanf("%d %d", &x, &y);
			//判断坐标合法性
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				if (show[x][y] == '*')
				{
					show[x][y] = '!';
					print_board(show, ROW, COL);
				}
				else if (show[x][y] == '!')
				{
					printf("该位置已被标记,请重新选择!\n");
				}
				else
				{
					printf("该位置已被排查,不能被标记,请重新选择!\n");
				}
			}
			else
			{
				printf("坐标不合法,请重新输入!\n");
			}
		}
		else if (input == 3)
		{
			printf("请输入要取消标记的坐标->:");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				if (show[x][y] == '!')
				{
					show[x][y] = '*';
					print_board(show, ROW, COL);
				}
				else
				{
					printf("该位置不能取消标记,请重新选择!\n");
				}
			}
			else
			{
				printf("坐标不合法,请重新输入!\n");
			}
		}
		else
		{
			printf("输入有误,请重新输入!\n");
		}

	}
	if (win == row * col - count)
	{
		printf("\n恭喜你,排雷成功!\n");
		printf("雷的分布情况:\n");
		print_board(mine, ROW, COL);
	}
}

game.h文件

头文件

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

棋盘的大小以及雷的个数

使用宏定义,方便随时修改棋盘规格以及雷的个数

#pragma once//防止头文件重复调用
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define count 10

主要功能函数的声明

//初始化雷盘
void init_board(char board[ROWS][COLS], int row, int  col);
//打印雷盘
void print_board(char board[ROWS][COLS], int row, int col);
//布置雷
void set_mine(char board[ROWS][COLS], int row, int col);
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

三、完整代码

完整代码上传在gitee

点此跳转扫雷gitee源码

以上就是扫雷游戏的简单实现,细心的小伙伴发现错误欢迎指正。最后感谢您的观看和支持!

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

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

相关文章

RK3568---4G模块驱动实验

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

什么是电子邮件组,为什么要使用它们?

在当今时代&#xff0c;电子邮件无处不在&#xff0c;尤其是对于商业活动而言。电子邮件的重要性不容忽视&#xff0c;因为它在沟通中极为高效。然而&#xff0c;电子邮件也存在降低工作效率和阻碍流程的风险。在这种情况下&#xff0c;电子邮件群组就是最佳的解决方案。什么是…

蓝桥杯刷题-15-异或和之和-拆位+贡献法⭐⭐(⊙o⊙)

蓝桥杯2023年第十四届省赛真题-异或和之和 题目描述 给定一个数组 Ai&#xff0c;分别求其每个子段的异或和&#xff0c;并求出它们的和。或者说&#xff0c;对于每组满足 1 ≤ L ≤ R ≤ n 的 L, R &#xff0c;求出数组中第 L 至第 R 个元素的异或和。然后输出每组 L, R 得到…

C++初阶:stack和queue使用及模拟实现

stack的介绍和使用 stack的介绍 堆栈 - C 参考 (cplusplus.com) 翻译 : 1. stack 是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 2. stack 是作为容器适配器被实现的&#xff0c;容器…

基于单片机和ICL7135多档位数字电压表设计

**单片机设计介绍&#xff0c;基于单片机和ICL7135多档位数字电压表设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机和ICL7135的多档位数字电压表设计是一个结合了硬件与软件技术的综合性项目。这种设计旨在实现一…

数据库的基本使用

一、数据库的简介 RDBMS简介&#xff1a; Relational Database Management System,通过表来表示关系类型。当前主要使用两种类型的数据库:关系型数据库和非关系型数据库。所谓的关系型数据库RDBMS是建立在关系模型基础上的数据库&#xff0c;借助于集合代数等数学概念和方法来…

使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数

使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是否为…

Spingboot落地国际化需求,Springboot按照请求的地区返回信息

文章目录 一、国际化1、概述2、Spring国际化 二、springboot简单使用国际化1、定义MessageSource2、定义message配置文件3、测试 三、根据请求的地区获取信息1、定义message配置文件2、定义配置类3、基础模板工具4、消息模板定义枚举5、测试一下6、总结 一、国际化 1、概述 国…

设计模式-结构型-装饰器模式-decorator

发票基本类 public class Invoice {public void printInvoice() {System.out.println("打印发票正文");} } 发票正文类 public class Decorator extends Invoice {protected Invoice ticket;public Decorator(Invoice ticket) {this.ticket ticket;}Overridepubl…

Java配置自定义校验

1、自定义注解State message、groups、payload package com.zhang.anno;import com.zhang.validartion.StateValidation; import jakarta.validation.Constraint; import jakarta.validation.Payload;import java.lang.annotation.*;import static java.lang.annotation.Eleme…

javaScript中原型链

一、原型链 js 的对象分为普通对象和函数对象。每个对象都有__proto__ 但是只有函数对象 (非箭头函数) 才有 prototype 属性。 new的过程&#xff1a; 1、创建一个空的简单 javaScript对象 2、将空对象的 __proto__连接到该函数的 prototype 3、将函数的this指向新创建的对象…

鲁棒线性模型估计(Robust linear model estimation)

鲁棒线性模型估计 1.RANSAC算法1.1 算法的基本原理1.2 迭代次数N的计算1.3 参考代码 参考文献 当数据中出现较多异常点时&#xff0c;常用的线性回归OLS会因为这些异常点的存在无法正确估计线性模型的参数&#xff1a; W ( X T X ) − 1 X T Y \qquad \qquad W(X^TX)^{-1}X^T…

【docker】Docker 简介

Docker 简介 什么是虚拟化、容器化?为什么要虚拟化、容器化&#xff1f;虚拟化实现方式应用程序执行环境分层虚拟化常见类别虚拟机容器JVM 之类的虚拟机 常见虚拟化实现主机虚拟化(虚拟机)实现容器虚拟化实现容器虚拟化实现原理容器虚拟化基础之 NameSpace 什么是虚拟化、容器…

人体跟随小车(旭日x3派、yolov5、目标检测)

人体跟随小车&#xff08;yolov5、目标检测&#xff09; 前言最终结果接线实现注意 前言 上板运行的后处理使用cython封装了&#xff0c;由于每个版本的yolo输出的形状不一样&#xff0c;这里只能用yolov5-6.2这个版本。 ①训练自己的模型并部署于旭日x3派参考&#xff1a; ht…

RuntimeError: Library cublas64_12.dll is not found or cannot be loaded

运行guillaumekln/faster-whisper-large-v2模型进行语音识别的时候报错了 RuntimeError: Library cublas64_12.dll is not found or cannot be loaded 代码&#xff1a; from faster_whisper import WhisperModelmodel WhisperModel("H:\\model\\guillaumekln\\faster…

【C++】优先级队列(priority_queue)的用法与实现

目录 一、概念&#xff1a; 二、仿函数&#xff08;Functor&#xff09;&#xff1a; 概念&#xff1a; 应用&#xff1a; 三、底层实现&#xff1a; 基本操作&#xff1a; 完整代码&#xff1a; 测试示例&#xff1a; 一、概念&#xff1a; 优先级队列&#xff08;pri…

PostgreSQL入门到实战-第六弹

PostgreSQL入门到实战 PostgreSQL查询语句(三)官网地址PostgreSQL概述PostgreSQL中ORDER BY理论PostgreSQL中ORDER BY实操更新计划 PostgreSQL查询语句(三) 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://www.post…

tcp的全连接队列和半连接队列满时,客户端再connect发生的情况

首先简单介绍下tcp的全连接队列(accept queue)和半连接队列(syn queue)&#xff0c; 1.客户端发起syn请求时&#xff0c;服务端收到该请求&#xff0c;将其放入到syn queue&#xff0c;然后回复acksyn给客户端。 2.客户端收到acksyn&#xff0c;再发送ack给服务端。 3. 服务端从…

3、最大池化maxinmum pooling

了解有关最大池化特征提取的更多信息。 简介 在第二课中,我们开始讨论卷积神经网络(convnet)的基础如何进行特征提取。我们了解了这个过程中的前两个操作是在带有 relu 激活的 Conv2D 层中进行的。 在这一课中,我们将看一下这个序列中的第三个(也是最后一个)操作:通过…

3dmax渲染十几个小时怎么办?3dmax怎么多机渲染

当使用3ds Max进行渲染作业时&#xff0c;如果发现单张图像的渲染时间长达十数小时&#xff0c;这可能是由于计算机硬件配置较低或渲染场景过于复杂所致。为了缩短渲染时间并提高效率&#xff0c;我们可以考虑采用多台计算机进行协同渲染。下面&#xff0c;让我们一起探讨如何通…