C语言项目实战——扫雷

news2025/1/16 5:52:55

目录

1.前言

2.完整流程

2.1规划书

2.2代码部分

2.2.1文件的结构设计

2.2.2变量的创建

2.2.3菜单的基本实现

2.2.4初始化期棋盘

2.2.5输出完整棋盘

2.2.6埋雷的实现

2.2.7查询周围雷的数量

2.2.8扫雷的实现

 2.2.9完整代码

3.总结


1.前言

哈喽大家好吖,今天笔者就前期基本所学手搓了一个扫雷的小项目,考察的知识点很简单仅有基本的分支,循环,函数调用,数组的基本应用,相信你也可以做到滴,快来试试喔~

2.完整流程

2.1规划书

虽然完成扫雷的代码量并不大,但我们还是要理清楚我们需要完成的功能和运行的基本逻辑。

 

这是一张扫雷的基本界面,通过观察和实际游玩可分析得出:

基本功能包括:

  • 游戏可以通过菜单实现继续玩或者退出游戏
  • 扫雷的棋盘是9*9的格⼦
  • 默认随机布置10个雷
  • 可以排查雷
  • 如果位置不是雷,就显⽰周围有⼏个雷
  • 如果位置是雷,就炸死游戏结束
  • 把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束

在梳理完需要实现的功能以后,就可以开始逐步实现以上功能咯。(当然笔者目前无法做出上述照片如此精良的扫雷,只能做一个简略版滴~)

2.2代码部分

2.2.1文件的结构设计

扫雷的代码量虽然不大,但是已经不适合用单文件来完成了,于是乎我们就把,游戏逻辑的基本实现,相关游戏函数的声明,以及游戏相关函数的实现分别打包,方便管理。

我们创建以下三个文件:

test.c //游戏的测试逻辑 
game.c //游戏中函数的实现
game.h //游戏需要的数据类型和函数声明等

完成后是这个样子。

另外,在正式敲代码前,我们应先在game.h这个头文件下加上

#pragma once

 #pragma once 是一个预处理指令,用来防止头文件的内容在一个编译单元中被多次包含。这与传统的头文件保护(也称为包含保护或头文件守卫)非常相似,后者通常使用#ifndef, #define, 和 #endif来实现。

2.2.2变量的创建

在扫雷这个游戏中,我们一共需要用到以下几个变量:

  • 棋盘一(origin)用于记录是否埋雷
  • 棋盘二(infor)用于记录雷的相关信息
  • 常量row,col分别用于记录扫雷中最核心的9*9区域
  • 常量rows,cols用于记录大棋盘11*11区域
  • 常量easy用于记录所要标记的雷

 实现分别如下(常量记得要在head.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 easy 10//记录简单难度有十个雷

 俩个棋盘的创建我放在了game函数中。

void game() {
	char origin[rows][cols];
	char infor[rows][cols];//创建俩个棋盘

	//初始化棋盘
	init(origin, rows, cols, '0');
	init(infor, rows, cols, '*');

	//打印棋盘用于调试
	print(infor, row, col);

	//开始埋雷
	setboom(origin, row, col);
	//print(origin, row, col);
	
	//开始排雷
	findboom(origin, infor, row, col);

}

2.2.3菜单的基本实现

为了菜单的基本美观,我们在实现的时候可以进行反复调试,代码如下:

void menu() {
    printf("------------------------\n");
	printf("------欢迎来到扫雷------\n");
	printf("------------------------\n");
	printf("----------菜单----------\n");
	printf("-------1.开始游戏-------\n");
	printf("-------2.结束游戏-------\n");//基础菜单实现
	printf("---------3.彩蛋---------\n");
	return;
}
int main() {
    int n = 0;//创建选项变量
    srand((unsigned int)time(NULL));
	do {
		menu();
		printf("请选择选项\n");
        scanf("%d", &n);
		switch (n) {
		case 1:game(); break;
		case 2:printf("已退出\n"); n = 0; break;
		case 3:printf("彩蛋\n"); n = 0; break;
		default:printf("请重新输入\n");
		}
	} while (n);//do while的巧妙利用
	
	return 0;
}

菜单的基本功能的实现是通过switch语句, 如果要玩游戏输入1进入game函数,如果选择退出或者彩蛋选项,将会在case语句中将n置为0从而跳出do while语句,最终实现是这个样子:

2.2.4初始化期棋盘

从这一部分往下,就是开始要分别写出实现每个小功能的函数,记得在game.c中写完函数要在game.h完成声明。

对于初始化,我们将origin这个棋盘全部置为字符'0',将infor这个棋盘全部置为'*'。

game.c中:

void init(char board[rows][cols], int rows1, int cols1, char set) {
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

函数的声明我在这就不完成了,就是一个小小体力活, 最后完整代码会体现出来。效果如下:

2.2.5输出完整棋盘

输出的时候,除了将9*9的核心区域打印出来,还要记得标记行列(1~9)以及x,y轴,增加可玩性。

void print(char board[rows][cols], int row1, int col1) {
	printf("-----蘸豆---爽-----\n");
	printf("0 1 2 3 4 5 6 7 8 9 y\n");
	for (int i = 1; i <= row1; i++) {
		printf("%d ", i);
		for (int j = 1; j <= col1; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("x\n");
}

最终效果如下:

2.2.6埋雷的实现

由于埋雷的随机性,这里需要一个c语言中的随机数函数rand。

rand用法:

在C语言中,rand() 是一个标准库函数,用于生成一个伪随机数。这个函数定义在 <stdlib.h> 头文件中。rand() 函数返回一个在 0 到 RAND_MAX 之间的整数,其中 RAND_MAX 是在 <stdlib.h> 中定义的一个宏,其值至少为 32767

由于 rand() 生成的是伪随机数,因此在每次程序运行时,如果不进行任何设置,它将生成相同的随机数序列。为了得到不同的随机数序列,通常在程序开始时使用 srand() 函数来设置随机数生成器的种子。种子的值通常来自于系统时间或者其他变化的数据。

这里就是用时间作为种子:srand((unsigned int)time(NULL)); (在主函数中书写了出来)

埋雷的时候我们使用while循环进行操作,另外有一个小小的注意点,如果该点已经被埋过雷了,注意排除,代码如下:

void setboom(char board[rows][cols], int row1, int col1) {
	int count = easy;//记录雷数
	while (count) {
		int x = rand() % 9 +1;
	    int y = rand() % 9 +1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	}
}

如果该位置为'0'就进行埋雷操作,如果为'1'就重复该循环。

2.2.7查询周围雷的数量

在正式开始扫雷函数的实现前,我们还需要先实现查找以该点为中心(非雷)周围八个格子雷的数量。实现这个功能有很多方式,这我采用了ascii码的一些基本特性。 

int set_infor(char board[rows][cols], int x, int y) {
	return (board[x - 1][y - 1] + board[x][y - 1] +
		board[x - 1][y] + board[x][y + 1]
		+ board[x + 1][y - 1] + board[x + 1][y]
		+ board[x + 1][y + 1] + board[x - 1][y + 1] - 8 * '0');
}

因为我创建的俩个棋盘都是char类型,所以说这个方法就可以使用。

2.2.8扫雷的实现

这块部分比较核心,以下是思路讲解:

  • 创建下x,y变量用于记录坐标,创建win用于标记当前排除了多少个区域。
  • 主体函数为while循环,循环条件为win变量仍然小于9*9总格数减去总雷数,那么胜利条件自然是win变量等于9*9总格数减去总雷数。
  • 接下来讲解while主体里面思路:
  • 先让玩家输入x,y坐标,并判断该坐标是否为坐标上的合法目标,如果不是就让玩家重新输入,如果该坐标合法就进行下一步。
  • 判断如果玩家选择的坐标为雷,则该玩家被炸死并跳出循环。
  • 如果选择的非雷,就打印出该位置上周围八个格子的雷数。

最终代码如下:

void findboom(char origin1[rows][cols], char infor1[rows][cols], int row1, int col1) {
	int x = 0;
	int y = 0;
	int win = 0;//用于记录当前排掉了多少个雷
	while (win < row1 * col1 - easy) {
		printf("请输入你要排查的x坐标>__");
		scanf("%d", &x);
		printf("请输入你要排查的y坐标>__");
		scanf("%d", &y);
		if (x > 0 && y > 0 && x <= row1 && y <= col1) {
			if (origin1[x][y] == '1') {
				printf("你个憨批你被炸死了\n");
				break;
			}
			else {
				int num = set_infor(origin1, x, y);
				infor1[x][y] = num + '0';
				print(infor1, col1, row1);
				win++;
			}
		}
		else {
			printf("该坐标不在棋盘上,你这个笨蛋\n请重新输入\n");
		}
	}
	if (win == row1 * col1 - easy) {
		printf("恭喜你!你胜利咯\n");
		print(origin1, row1, col1);
	}
}

 运行如下图:

 2.2.9完整代码

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 easy 10//记录简单难度有十个雷

void menu();

void init(char board[rows][cols], int rows1, int cols1, char set);

void setboom(char board[rows][cols], int row1, int col1);

void print(char board[rows][cols], int row1, int col1);

int set_infor(char board[rows][cols], int x, int y);

void findboom(char origin1[rows],char infor1[rows][cols],int row1,int col1);

 game.c:

#define  _CRT_SECURE_NO_WARNINGS 1
//相关函数的定义
#include"game.h"
void menu() {
    printf("------------------------\n");
	printf("------欢迎来到扫雷------\n");
	printf("------------------------\n");
	printf("----------菜单----------\n");
	printf("-------1.开始游戏-------\n");
	printf("-------2.结束游戏-------\n");//基础菜单实现
	printf("---------3.彩蛋---------\n");
	return;
}

void init(char board[rows][cols], int rows1, int cols1, 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 print(char board[rows][cols], int row1, int col1) {
	printf("-----蘸豆---爽-----\n");
	printf("0 1 2 3 4 5 6 7 8 9 y\n");
	for (int i = 1; i <= row1; i++) {
		printf("%d ", i);
		for (int j = 1; j <= col1; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("x\n");
}

void setboom(char board[rows][cols], int row1, int col1) {
	int count = easy;//记录雷数
	while (count) {
		int x = rand() % 9 +1;
	    int y = rand() % 9 +1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	}
}

int set_infor(char board[rows][cols], int x, int y) {
	return (board[x - 1][y - 1] + board[x][y - 1] +
		board[x - 1][y] + board[x][y + 1]
		+ board[x + 1][y - 1] + board[x + 1][y]
		+ board[x + 1][y + 1] + board[x - 1][y + 1] - 8 * '0');
}

void findboom(char origin1[rows][cols], char infor1[rows][cols], int row1, int col1) {
	int x = 0;
	int y = 0;
	int win = 0;//用于记录当前排掉了多少个雷
	while (win < row1 * col1 - easy) {
		printf("请输入你要排查的x坐标>__");
		scanf("%d", &x);
		printf("请输入你要排查的y坐标>__");
		scanf("%d", &y);
		if (x > 0 && y > 0 && x <= row1 && y <= col1) {
			if (origin1[x][y] == '1') {
				printf("你个憨批你被炸死了\n");
				break;
			}
			else {
				int num = set_infor(origin1, x, y);
				infor1[x][y] = num + '0';
				print(infor1, col1, row1);
				win++;
			}
		}
		else {
			printf("该坐标不在棋盘上,你这个笨蛋\n请重新输入\n");
		}
	}
	if (win == row1 * col1 - easy) {
		printf("恭喜你!你胜利咯\n");
		print(origin1, row1, col1);
	}
}

test.c:

#define  _CRT_SECURE_NO_WARNINGS 1//游戏逻辑的实现
#include"game.h"

void game() {
	char origin[rows][cols];
	char infor[rows][cols];//创建俩个棋盘

	//初始化棋盘
	init(origin, rows, cols, '0');
	init(infor, rows, cols, '*');

	//打印棋盘用于调试
	print(infor, row, col);

	//开始埋雷
	setboom(origin, row, col);
	//print(origin, row, col);
	
	//开始排雷
	findboom(origin, infor, row, col);

}

int main() {
    int n = 0;//创建选项变量
	srand((unsigned int)time(NULL));
	do {
		menu();
		printf("请选择选项\n");
        scanf("%d", &n);
		switch (n) {
		case 1:game(); break;
		case 2:printf("已退出\n"); n = 0; break;
		case 3:printf("彩蛋是这个~\n"); n = 0; break;
		default:printf("请重新输入\n");
		}
	} while (n);//do while的巧妙利用
	
	return 0;
}

3.总结

以上就是扫雷的基本功能实现了,其实还有许多可以扩展,例如点开一个非雷区自动展开周围其他非雷区域,调整难度等,大家都可以自己尝试尝试哦~。都看到这里了,希望大家对我多多支持喔,大家一起进步~

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

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

相关文章

Linux计划任务书以及定时任务的编写

一、程序可以通过两种方式执行&#xff1a; 手动执行利用调度任务&#xff0c;依据一定的条件自动执行 自动执行可通过一下两个命令来实现: &#xff08;1&#xff09;At &#xff08;单一工作调度&#xff09; &#xff08;2&#xff09;Cron &#xff08;循环工作调度&a…

Android进阶:2021大厂Android面试经验,含BATJM大厂_2021大厂android进阶面试指南目录

在面试的过程中我深深的感受到&#xff0c;对于一个优秀的安卓开发来说&#xff0c;首先摆在第一位的还是他/她作为一个软件工程师的基本素养。无论你是做前端还是后端&#xff0c;最后定义你的优秀程度的还是作为软件工程师的基本素养&#xff0c;学习能力和编程能力&#xff…

VC2022 + google test

还要从项目开始说。 刚来项目组&#xff0c;主体是医疗仪器中位机部分&#xff0c;基本的部署结构&#xff1a; 上位机UI(Ubuntu 18 java) 中位机(ARMUbuntu 18, C) 下位机&#xff08;N个下位机子系统&#xff0c;控制步进电机&#xff0c;各种传感器&#xff0c;反射计…

免费ChatGPT合集——亲测免费

1、YesChat 无需登录 网址&#xff1a;YesChat-ChatGPT4V Dalle3 Claude 3 All in One Freehttps://www.yeschat.ai/ 2. 讯飞星火 要登录 讯飞星火大模型-AI大语言模型-星火大模型-科大讯飞 3.通义千问 要登录 通义我是通义&#xff0c;一个专门响应人类指令的…

ios不兼容Svg Wave的动画的解决方法

近日也是用上了SvgWave&#xff0c;十分的好看 Svg Wave - A free & beautiful gradient SVG wave Generator. 大家感兴趣的也可以了解一下 【场景】 使用SvgWave的Animate&#xff0c;并生成svg代码使用&#xff0c;windows web端、朋友的安卓移动端都能够正常执行动画…

37.WEB渗透测试-信息收集-企业信息收集(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;36.WEB渗透测试-信息收集-企业信息收集&#xff08;3&#xff09;-CSDN博客 关于主域名收…

lt Redis变慢的原因及排查解决方法

前言 Redis 作为优秀的内存数据库&#xff0c;其拥有非常高的性能&#xff0c;单个实例的 OPS 能够达到 10W 左右(5-10W)。但也正因此如此&#xff0c;当我们在使用 Redis 时&#xff0c;如果发现操作延迟变大的情况&#xff0c;就会与我们的预期不符。 你也许或多或少地&…

python基础知识—while和for循环(三)

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 一&#xff1a;while循环1.1程序的三种执行流程1.2while循环1.3循环变量和死循环 二&#xff1a;for循环2.1for循环2.2range 一&…

【库函数】Linux下动态库.so和静态库.a的生成和使用

目录 &#x1f31e;1. Linux下静态库和动态库的基本概念 &#x1f31e;2. 动态库 &#x1f30a;2.1 动态库如何生成 &#x1f30d;2.1.1 文件详情 &#x1f30d;2.1.2 编译生成动态库 &#x1f30a;2.2 动态库如何使用 &#x1f30d;2.2.1 案例 &#x1f30d;2.2.2 动态…

【jQuery】看一眼就会用的jquery库之续章!

jQuery&#xff08;js框架&#xff09; 17、操作节点 创建节点&#xff1a; 创建节点只需要将元素放在jQuery的工厂函数中//创建一个button按钮let $btn$("<input typebutton>");//创建一个列表项let $li$("<li>选项</li>");添加节点…

【PostgreSQL】Postgres数据库安装、配置、使用DBLink详解

目录 一、技术背景1.1 背景1.2 什么是 DBLink 二、安装配置 DBLink2.1 安装 DBLink2.2 配置 DBLink1. 修改 postgresql.conf2. 修改 pg_hba.conf 三、DBLink 使用3.1 数据准备3.2 DBLink 使用1. 创建 DBLink 连接2. 使用 DBLink 进行查询3. 使用 DBLink 进行增删改4. 使用 DBLi…

鲁棒控制理论学习:静态状态反馈H∞控制器

鲁棒性&#xff0c;即系统的健壮性&#xff0c;是指在异常和危险情况下系统能够维持其功能和性能的能力。在控制系统中&#xff0c;鲁棒性表现为系统在参数摄动下维持某些性能的特性。例如&#xff0c;当控制系统面临输入错误、磁盘故障、网络过载或有意攻击等挑战时&#xff0…

视频质量评价PSNR的两种计算方法

PSNR&#xff08;峰值信噪比&#xff09; 峰值信号的能量与噪声的平均能量之比&#xff0c;本质的是比较两张图像像素值差异&#xff0c;用途较广&#xff0c;目前仍作为对照其他指标的基线。PSNR的单位是dB&#xff0c;数值越大表示失真越小。 mn单色图像 I 和K&#xff0c; …

SpringCloud系列(17)--将服务消费者Consumer注册进Zookeeper

前言&#xff1a;在上一章节中我们把服务提供者Provider注册进了Zookeeper&#xff0c;而本章节则是关于如何将服务消费者Consumer注册进Zookeeper 1、再次创建一个服务提供者模块&#xff0c;命名为consumerzk-order80 (1)在父工程下新建模块 (2)选择模块的项目类型为Maven并…

WIFI加密方式对无线速率的影响

文章目录 无线加密三种选择&#xff1a;WEP、WPA和WPA2测试平台和测试方法非加密和WEP加密测试 结果差别巨大非加密条件下 300M无线路由实测WEP加密条件下 300M无线路由实测 TKIP加密算法&#xff1a;WPA与WPA2成绩低迷WPA加密&#xff08;TKIP加密算法&#xff09;条件下 300M…

TypeScript入门第一天,所有类型+基础用法+接口使用

表示逻辑值&#xff1a;true 和 false。在JavaScript和TypeScript里叫做boolean | | 数组类型 | 无 | 声明变量为数组。 // 在元素类型后面加上[] let arr: number[] [1, 2]; // 或者使用数组泛型&#xff0c;Array<元素类型> let arr: Array [1, 2]; | | 元组…

大数据—数据采集DataX

一、DataX介绍 官网&#xff1a; DataX/introduction.md at master alibaba/DataX GitHub DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。 DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、…

翻译《The Old New Thing》 - Why .shared sections are a security hole

Why .shared sections are a security hole - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20040804-00/?p38253 Raymond Chen 2004年08月04日 许多人会推荐使用共享数据节作为在应用程序的多个实例之间共享数据的一种方式。这听起来是个好…

立即刷新导致请求的response没有来得及加载造成的this request has no response data available

1、前端递归调用后端接口 const startProgress () > {timer.value setInterval(() > {if (progress.value < 100) {time.value--;progress.value Math.ceil(100 / wait_time.value);} else {clearInterval(timer.value);progress.value 0;timer.value null;time.…

pytest-asyncio:协程异步测试案例

简介&#xff1a;pytest-asyncio是一个pytest插件。它便于测试使用异步库的代码。具体来说&#xff0c;pytest-asyncio提供了对作为测试函数的协同程序的支持。这允许用户在测试中等待代码。 历史攻略&#xff1a; asyncio并发访问websocket Python&#xff1a;协程 - 快速创…