【C语言】扫雷(保姆级教程+内含源码)

news2024/11/18 18:39:01

C系列文章目录


前言

一,模块化编程

二,游戏思路与逻辑

三,实现游戏步骤/过程 

1,菜单界面(menu)

2,实现多行多列扫雷 

        3, 实现多个雷

4,棋盘初始化 

5,棋盘的打印 

6,布置雷的信息 

7,玩家输入雷实现步骤 

8,统计输入坐标周围有多少雷

四,结果运行

五,模块化代码实现 

1、test.c

2、game.c 

3、game.h 


前言

通过本章我们可以学习到使用C语言写的扫雷小游戏,该教程堪称保姆级,小白都可学习


一,模块化编程

  1. 可维护性:模块化编程将代码划分为独立的模块,每个模块负责特定的任务或功能。这样,在需要修改或调试某个功能时,只需关注相关的模块,而不需要涉及整个程序。这大大简化了维护和调试的工作,使得代码更易于理解和修改。

  2. 重用性:模块化编程鼓励开发人员将一些常用的功能封装成模块,然后在不同的项目中重复使用。这样可以避免重复编写相同的代码,减少了开发工作量,提高了开发效率。同时,通过不断重用经过测试和验证的模块,可以提高代码的可靠性和稳定性。

  3. 可扩展性:当需要添加新的功能或修改现有功能时,模块化编程能够提供更好的可扩展性。由于模块之间的依赖关系明确定义和管理,可以单独修改或替换某个模块,而不会影响到其他模块。这种灵活性使得系统更容易适应变化和演化。

  4. 并行开发:模块化编程允许多个开发人员并行工作,每个人负责开发和测试不同的模块。这样可以提高开发效率,缩短项目的开发周期。同时,模块化编程也方便团队协作和沟通,降低了开发过程中的冲突和合并的风险。

  5. 可测试性:模块化编程使得单元测试更容易进行。每个模块都是相对独立的,可以单独测试其功能和性能。这样可以更容易地发现和修复问题,提高软件的质量和稳定性。

总之,模块化编程的优势包括可维护性、重用性、可扩展性、并行开发和可测试性。这些优势使得代码更易于理解、修改和维护,提高了开发效率和软件质量。

在学习扫雷前,我们先了解模块化编程,模块化编程的思想可以让我们更好的进行编程,让我们更好的理解接下来的扫雷过程。

二,游戏思路与逻辑

1.创建菜单函数提醒玩家选择是否玩游戏。

2.创建一个main()函数,让我们选择是否游戏

3.当我们选择玩游戏,就进入一个扫雷的游戏函数

4.我们在游戏函数中,创建两个二维数组,一个让人不知道那个是雷,一个放的雷信息

5.开始对游戏函数进行功能填充

  1. 首先,进行雷的初始化棋盘。
  2. 然后,再打印出雷的初始化棋盘。注意:一定是要先进行 初始化 然后再 打印棋盘。
  3. 接着,就可以布置雷的信息了。
  4. 最后,输入排查雷的坐标。

6.检查出的坐标是不是雷,布置雷存放的是字符(1) 没有放置的是字符(0)
7.输入坐标的时候一共有④种情况:《很遗憾,你被炸死了》、《非法坐标了,请重新输入》、《该坐标被占用,请重新输入》、《恭喜你,排雷成功》 
8.然后,再回到步骤①,是否选择 进入游戏 以及 退出游戏

三,实现游戏步骤/过程 

1,菜单界面(menu)

菜单界面函数实际上就像是我们的一个界面,就好比是游戏的界面目录,餐馆当中的菜单。一样的道理。这个是库函数就有的我们只需要直接引用下即可。示例代码如下

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

2,实现多行多列扫雷 

#define ROW 9
#define COL 9//现实棋盘的行数
#define ROWS ROW+2//初始化棋盘的行数,是为了后面的统计雷的方便
#define COLS COL+2

#define 宏定义在这里的好处:

1,方便以后程序的修改,不用以后对程序进行修改时,每个相同的变量都改变,只需对宏定义上进行修改。
2,提高程序的运行效率,更加方便模块化。

3, 实现多个雷

#define EASY_COUNT 10//存放10个雷

我们只需要改一下EASY_COUNT后面的数字,就可以让我们更改有存放多少雷。

4,棋盘初始化 

打印棋盘,本质上是打印数组的内容。如下所示

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set; //将接受到的set字符,初始化数组
		}
	}
}

char set 是实参传递到形参的字符。

 实参数组名 行 可以进行省略,但是 列 不能进行省略。 

5,棋盘的打印 

打印棋盘,本质上是打印数组的内容。

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("----------扫雷-----------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);//打印提醒行
	}
	for (i = 1; i <= row; i++)
	{
		printf("\n");
		printf("%d ", i);//打印提醒列
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//传参的字符打印
		}
	}
	printf("\n----------扫雷-----------\n");
}

效果图如下:

 

6,布置雷的信息 

在存放雷的数组内布置雷。

//设置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = Lie;
	while(count)
	{
		int x = rand() % row + 1;//横坐标1~9
		int y = rand() % col + 1;//纵坐标1~9
		if (board[x][y] == '0')
		{
			board[x][y] = '1';//存放雷
			count--;
		}
	}
}

这里还用到了一个知识点【随机数】

在实际开发中,我们往往需要一定范围内的随机数,过大或者过小都不符合要求,那么,如何产生一定范围的随机数呢?我们可以利用取模的方法:

int a = rand() % 10;    //产生0~9的随机数,注意10会被整除
如果要规定上下限:
int a = rand() % 30 + 11;    //产生11~41的随机数
分析:取模即取余,rand()%30+11 我们可以看成两部分:rand()%30 是产生 0~30 的随机数,后面+11保证 a 最小只能是 11,最大就是 30+11=41

使用 <time.h> 头文件中的 time() 函数即可得到当前的时间(精确到秒),就像下面这样:

srand((unsigned)time(NULL));

7,玩家输入雷实现步骤 

这里的玩家输入坐标,在玩家输入下棋的时候,定义了个静态局部变量,在执行代码的时候。玩游戏的时候会提醒一次, 输入第一个坐标记得空一格!每次进入游戏只有一次,这里主要就是用到了 静态局部变量 就可以保证上一次的值不会被销毁。

检查坐标处是不是雷,布置存放的是字符'1',没有放置雷存放的是字符'0'。

判断坐标输入合法性几种情况:

1.很遗憾,你被炸死了!
2.该坐标非法了,请重新输入。
3.该坐标被占用,请重新输入。
4.恭喜你,排雷成功

//排查雷
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - Lie)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &x,&y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标被占用,请重新输入:>");

			}
			else
			{
				if (mine[x][y] == '1')
				{
					printf("很抱歉,你被炸死了");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else
				{
					int count = get_mine(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
					win++;
				}
			}
		}
		else
		{
			printf("该坐标非法,请重新输入:>");
		}
	}
	if (win == row * col - Lie)
	{
		printf("恭喜你,扫雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

8,统计输入坐标周围有多少雷

//统计雷
int get_mine(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y - 1] +
		board[x - 1][y] +
		board[x - 1][y + 1] +
		board[x + 1][y + 1] +
		board[x + 1][y] +
		board[x + 1][y - 1] +
		board[x][y - 1] +
		board[x][y + 1] - 8 * '0');//统计坐标周围多少雷
}

四,结果运行

我们将存放80个雷,方便我们测试功能。

五,模块化代码实现 

1、test.c

测试游戏的逻辑。

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
mnue()
{
	printf("*******-------扫雷----------********\n");
	printf("*******      1.play         ********\n");
	printf("*******      0.exit         ********\n");
	printf("*******-------扫雷----------********\n");
}
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);
	//排查雷
	FindMine(show, mine, ROW, COL);
}	
int main()
{
	int input = 0;
	do
	{
		mnue();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("请重新选择:");
			break;
		}
	} while (input);
	return 0;
}

2、game.c 

游戏和相关函数实现。

#define _CRT_SECURE_NO_WARNINGS
#include "game.h" 
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set; //将接受到的set字符,初始化数组
		}
	}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("----------扫雷-----------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);//打印提醒行
	}
	for (i = 1; i <= row; i++)
	{
		printf("\n");
		printf("%d ", i);//打印提醒列
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//传参的字符打印
		}
	}
	printf("\n----------扫雷-----------\n");
}
//设置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = Lie;
	while(count)
	{
		int x = rand() % row + 1;//横坐标1~9
		int y = rand() % col + 1;//纵坐标1~9
		if (board[x][y] == '0')
		{
			board[x][y] = '1';//存放雷
			count--;
		}
	}
}
//统计雷
int get_mine(char board[ROWS][COLS], int x, int y)
{
	return (board[x - 1][y - 1] +
		board[x - 1][y] +
		board[x - 1][y + 1] +
		board[x + 1][y + 1] +
		board[x + 1][y] +
		board[x + 1][y - 1] +
		board[x][y - 1] +
		board[x][y + 1] - 8 * '0');//统计坐标周围多少雷
}
//排查雷
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - Lie)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &x,&y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标被占用,请重新输入:>");

			}
			else
			{
				if (mine[x][y] == '1')
				{
					printf("很抱歉,你被炸死了");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else
				{
					int count = get_mine(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
					win++;
				}
			}
		}
		else
		{
			printf("该坐标非法,请重新输入:>");
		}
	}
	if (win == row * col - Lie)
	{
		printf("恭喜你,扫雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

3、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 Lie 80
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//设置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);


总结

本篇文章详细的,有体系的介绍了扫雷的结构,方便我们小白的更加理解C语言的数组。

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

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

相关文章

连接器信号完整性仿真教程 六

连接器信号完整性仿真教程五中,讲了波导端口中同轴波导端口及多Pin波导端口的设置。本将继续以实例演示的方式讲解波导端口中的微带(Microstrip Line)波导端口的设置及其在连接器信号完整性仿真中的应用。 一 微带线(Microstrip Line) 由介基材(Dielectric Substrate)及…

正则表达式速通

简介 正则表达式&#xff0c;我们可以看作通配符的增强版&#xff0c;可以帮我们匹配指定规则的字符串&#xff0c;在计算机中应用广泛&#xff0c;比如说爬虫、网站的登录表单等。 原视频&#xff1a;https://www.bilibili.com/video/BV1da4y1p7iZ 学习正则表达式的常用工具…

【LeetCode每日一题】——566.重塑矩阵

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 简单 三【题目编号】 566.重塑矩阵 四【题目描述】 在 MATLAB 中&…

【C++ 程序设计】实战:C++ 实践练习题(1~10)

目录 01. 二维数组 02. 奇偶性 03. 指针与变量 04. 员工薪资 05. 整型值&#xff08;%4d 进行格式化&#xff09; 06. 求三个数中的最大值和最小值 07. 同一字母次数统计 08. 字符串回文判断 09. 闰年判断 10. 交换两个双精度数 01. 二维数组 #include <stdio.…

系统学习Linux-MySQL主从复制及读写分离(五)

部署MySQL主从异步复制 在MySQL Master上的配置NTP时间同步服务器 [rootlocalhost ~]# rpm -q ntpntp-4.2.6p5-28.el7.centos.x86_64[rootlocalhost ~]# vim /etc/ntp.conf #添加两行server 127.127.1.0fudge 127.127.1.0 stratum 8 启动NTP服务 [rootlocalhost ~]# system…

pytorch(续周报(1))

文章目录 2.1 张量2.1.1 简介2.1.2 创建tensor2.1.3 张量的操作2.1.4 广播机制 2.2 自动求导Autograd简介2.2.1 梯度 2.3 并行计算简介2.3.1 为什么要做并行计算2.3.2 为什么需要CUDA2.3.3 常见的并行的方法&#xff1a;网络结构分布到不同的设备中(Network partitioning)同一层…

C++入门篇2---类和对象(上)

看前须知:何为面向对象&#xff1f; 面向对象&#xff08;Object-oriented&#xff09;是一种计算机编程的方法论和编程范式。面向对象的核心思想是将数据&#xff08;对象&#xff09;和操作&#xff08;方法&#xff09;封装在一起&#xff0c;形成一个相互关联和相互作用的…

Diffusion mdoel: Denoising Diffusion Probabilistic Models论据解读及实现(一)

论文地址&#xff1a;https://arxiv.org/pdf/2006.11239.pdf 1 正向加噪推导 **核心公式1 ** x t α ˉ t ∗ x 0 1 − α ˉ t z t x_t\sqrt{\bar \alpha_t}*x_{0}\sqrt{1-\bar \alpha_t}z_t xt​αˉt​ ​∗x0​1−αˉt​ ​zt​ 证明如下&#xff1a; α t 1 − β…

芯片制造详解.刻蚀原理.学习笔记(五)

本篇笔记是看完原视频后的整理和补充&#xff0c;建议各位观看原视频&#xff0c;这里附上地址。 如何雕刻芯片&#xff1a;刻蚀原理&#xff5c;芯片制造详解05 芯片制造详解.刻蚀原理.学习笔记 五 一、刻蚀技术的分类二、刻蚀中三个关键指标2.1 刻蚀速率2.2 选择比2.3 方向性…

linux配置固定ip

为什么要固定ip&#xff1f; 虚拟机是利用DHCP&#xff08;动态获取ip&#xff09;&#xff0c;ip地址会频繁变化&#xff0c;不利于远程连接linux&#xff0c;同时会改变映射关系。 VMware 第一步&#xff1a;在workstation中配置IP地址网关和网段 1.点击虚拟网络编辑器 …

RISCV - 4 ISA 扩展名命名约定

RISCV - 4 ISA 扩展名命名约定 1 Case Sensitivity2 Base Integer ISA3 Instruction-Set Extension Names4 Version Numbers5 Underscores6 Additional Standard Extension Names7 Supervisor-level Instruction-Set Extensions8 Hypervisor-level Instruction-Set Extensions9…

28_计算机网络(Computer Networks)基础

本篇介绍计算机网络的基础知识。 文章目录 1. 计算机网络历史2. 以太网" (Ethernet)2.1 以太网" (Ethernet)的简单形式及概念2.2 指数退避解决冲突问题2.3 利用交换机减少同一载体中设备2.4 互联网&#xff08;The Internet&#xff09;2.5 路由(routing)2.6 数据包…

【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换读写分离】—— 案例实战

&#x1f4a7; S p r i n g A O P 主从数据源切换 读写分离 自定义注解案例实战&#xff01; \color{#FF1493}{Spring AOP 主从数据源切换 读写分离 自定义注解 案例实战&#xff01;} SpringAOP主从数据源切换读写分离自定义注解案例实战&#xff01;&#x1f4a7; …

第三课:运算符

1.算数运算符 int a 20; int b 10; System.out.println(a b); // 30 System.out.println(a - b); // 10 System.out.println(a * b); // 200 System.out.println(a / b); // 2 System.out.println(a % b); // 0 --->模运算相当于数学中除法的余数 ⚠关于/除法 public s…

QEMU源码全解析19 —— QOM介绍(8)

接前一篇文章&#xff1a;QEMU源码全解析18 —— QOM介绍&#xff08;7&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 上一回讲到了Q…

2023 云原生编程挑战赛火热报名中!导师解析 Serverless 冷启动赛题

大赛介绍 第四届云原生编程挑战赛&#xff0c;是由阿里云主办&#xff0c;云原生应用平台、天池联合承办的云原生著名品牌赛事。 自 2015 年开始&#xff0c;大赛已经成功举办了八届&#xff0c;并从 2020 年开始升级为首届云原生编程挑战赛&#xff0c;共吸引了超过 53000 支…

python爬虫基本功(三)--爬虫所需网络协议知识超详细总结(下)

前言 大家好&#xff0c;这里是Kaiser&#x1f44f;。本文内容是衔接 &#x1f449;python爬虫基本功(二)—爬虫所需网络协议知识超详细总结&#xff08;上&#xff09;&#x1f448; 一文&#xff0c;为达更好的阅读效果&#xff0c;以及防止知识点出现“断层”&#xff0c;使…

Java方法的使用(重点:形参和实参的关系、方法重载、递归)

目录 一、Java方法 * 有返回类型&#xff0c;在方法体里就一定要返回相应类型的数据。没有返回类型&#xff08;void&#xff09;&#xff0c;就不要返回&#xff01;&#xff01; * 方法没有声明一说。与C语言不同&#xff08;C语言是自顶向下读取代码&#xff09;&#…

数电基础知识学习笔记

文章目录&#xff1a; 一&#xff1a;逻辑门 1.逻辑门电路的分类 1.1 按逻辑&#xff08;逻辑门&#xff09; 1.1.1 逻辑定义 1.1.2 常见数字电路相关符号 1.1.3 电路图表示 1.1.4 逻辑门电路图像符号 1.2 按电路结构 1.3 按功能特点 2.高低电平的含义 3.常见的门…

给定长度值length,把列表切分成每段长度为length的N段列表,Kotlin

给定长度值length&#xff0c;把列表切分成每段长度为length的N段列表&#xff0c;Kotlin import kotlin.random.Randomfun main(args: Array<String>) {var source mutableListOf<String>()val end Random.nextInt(30) 1for (i in 0 until end) {source.add(i.…