扫雷小游戏【C语言】

news2024/7/7 15:27:07

目录

前言

一、基本实现逻辑

二、实现步骤

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 !好兄弟我们下期再见!

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

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

相关文章

0014-TIPS-pawnyable : Double-Fetch pt_regs

原文 Linux Kernel PWN | 040302 Pawnyable之双取 Double Fetch 题目下载 漏洞代码 #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/sl…

chatgpt赋能python:Python搜索本地文件指南

Python搜索本地文件指南 在进行开发或者日常使用中&#xff0c;需要对本地的文件进行搜索操作是常见的需求。如果你正在学习Python编程&#xff0c;接下来的内容会为你提供一些如何使用Python进行本地文件搜索的指南。 搜索文件 在Python中搜索本地文件可以使用os模块。该模…

杂记 | 使用FRP搭建内网穿透服务

文章目录 01 需求场景02 项目地址03 文件介绍04 编写配置文件4.1 编写frps.ini4.2 编写frpc.ini 05 测试 01 需求场景 1、有一台云服务器&#xff0c;Linux&#xff08;CentOS7&#xff09;系统 2、云服务器有公网ip&#xff0c;公网可以访问 3、本地电脑是家用笔记本&#xf…

AN10833-MIFARE type identification procedure.pdf

1.2范围 本文档描述了如何区分MIFARE接口卡IC系列的成员。ISO/IEC 14443-3描述了类型A的初始化和防冲突程序&#xff0c;该程序为所有MIFARE卡提供卡类型信息。 MIFARE卡与ISO/IEC 14443-3兼容。因此&#xff0c;已经存在的应用可以很容易地扩展到分别使用较新的MIFARE芯片和所…

017、数据库管理之监控

监控工具 TiDB数据库的监控体系TiDB Dashboard数据库监控系统数据流转监控系统访问地址 报警系统报警级别指标系统常用监控指标Service Port Status 常用监控指标PD的常用监控指标TiDB-Server的常用监控指标TiKV 的常用监控指标Dashboard的常用指标 实验&#xff1a; 通过监控工…

Flink 学习六 Flink 窗口计算API

Flink 学习六 Flink 窗口计算API 1.窗口 (window)概念和分类 窗口 window 是处理无限流的核心就是把无界的数据流,按照一定的规则划分成一段一段的有界的数据流(桶),然后再这个有界的数据流里面去做计算; 2.分类体系 2.1 滚动窗口 相邻窗口之间是没有数据重合 window 大小…

Geocomputation (2)Attribute data operations

Geocomputation &#xff08;2&#xff09;Attribute data operations 属性数据操作 来源&#xff1a;https://github.com/geocompx/geocompy 1.准备 #| echo: false import pandas as pd import matplotlib.pyplot as plt pd.options.display.max_rows 6 pd.options.disp…

chatgpt赋能python:Python搜索函数:快速、高效的搜索利器

Python 搜索函数&#xff1a;快速、高效的搜索利器 如果你经常使用 Python 编程语言&#xff0c;那么你应该知道搜索数据集的重要性。用于搜索的函数在 Python 中是关键而常用的工具。Python 搜索函数是一种高效、快速的搜索利器&#xff0c;它可以让你轻松地找到你需要的数据…

C生万物 | 聊聊枚举与联合体的用法

文章目录 枚举1、枚举类型的定义2、枚举的使用3、枚举的优点 联合体1、联合体类型的定义2、联合体的特点3、联合体大小的计算一道经典面试题&#xff1a;判断当前计算机的大小端存储 总结与提炼 本文&#xff0c;我们就来谈谈C语言中的枚举和联合体&#xff0c;因为这两块知识点…

6月21日第壹简报,星期三,农历五月初四

6月21日第壹简报&#xff0c;星期三&#xff0c;农历五月初四坚持阅读&#xff0c;静待花开1. 中国LPR“对称降息”10个基点&#xff0c;房贷迎今年首次“降息”。2. 孟加拉国正式申请加入金砖国家&#xff0c;中方&#xff1a;欢迎更多伙伴早日加入金砖大家庭。3. 历时近2年波…

7、DuiLib动态创建控件

文章目录 1、动态创建控件2、纯代码方式动态创建控件3、基于构建好的 XML 动态创建控件&#xff08;CDialogBuilder&#xff09; 1、动态创建控件 在实际业务场景中&#xff0c;并不是所有界面元素都可以通过 XML 预先定义好的&#xff0c;有时候我们需要根据数据库或者服务器…

【计算机组成原理】主存储器

目录 一、存储体系基本概念 二、主存储器的基本组成 三、SRAM和DRAM 四、只读存储器ROM 五、主存储器与CPU的连接 六、双端口RAM和多模块存储器 一、存储体系基本概念 一个二进制位&#xff08;bit&#xff09;是构成存储器的最小单位&#xff1b;字节&#xff08;8bit&…

总结uwsgi的安装、配置与设置开机自启

一、uwsgi的安装与测试 1、uwsgi安装 pip install uwsgi# 查看uwsgi版本号 uwsgi –version 如果服务器安装anconda&#xff0c;有可能安装不上去&#xff0c;建议使用以下命令&#xff1a; conda install -c conda-forge uwsgi 2、测试uwsgi 创建test.py文件,并写下以下…

C++基础(2)——函数高级和函数对象

前言 本文主要介绍了C中函数高级相关的内容 3.1&#xff1a;函数默认参数 在函数定义的时候可以给形参赋初值&#xff0c;如果函数在调用的时候有传入参数&#xff0c;就使用传入的参数&#xff0c;如果没有就用默认的。 注意事项 1&#xff1a;如果某个参数有了默认值&…

Scilab安装与入门

说明&#xff1a;Scilab主要是用于信号处理&#xff0c;我本次用它来进行滤波仿真 地址&#xff1a; Scilab 2023.1.0 | Scilab https://www.scilab.org/download/scilab-2023.1.0 下载好了&#xff0c;进行安装看看 下一步&#xff0c;接受许可协议 选择安装路径到D盘下了…

chatgpt赋能python:Python数值互换:让你的编程更加高效

Python数值互换&#xff1a;让你的编程更加高效 Python是一种高级编程语言&#xff0c;其灵活性和易于学习的特点使其在科学计算、数据分析和人工智能等领域变得非常流行。作为Python的用户&#xff0c;很多时候你可能需要将数值类型之间进行转换&#xff0c;这样可以提高你的…

第6章 面向对象

第6章 面向对象 6.1. Scala包 ​ 包的命名 ​ 包的导入 Scala中基本的import导入语法和Java完全一致 import java.util.List import java.util._ // Scala中使用下划线代替Java中的星号Java中import导入的语法比较单一&#xff0c;Scala对此进行扩展&#xff0c;Scala中的im…

Cadence Allegro PCB设计88问解析(二十八) 之 Allegro中使用Anti Etch分割平面

一个学习信号完整性仿真的layout工程师 最近看到关于Anti Etch的设置&#xff0c;因为本人之前在layout设计是使用过这个命令。后来去到别的公司就不用了&#xff0c;从网上看到说这个命令是用来负片设计的。在这里在说下正片和负片的概念&#xff1a; 正片&#xff1a;是指在a…

Git团队代码规范

Git团队代码规范 1. 分支的定义2. 约束2.1 远程命名2.2 拉取代码2.3 新建Issues2.3 代码规范2.4 MR提交 本文章讲解Git代码管理中团队应该遵守的一些规则&#xff0c;让大家可以愉快的一起开发工作。 本篇文章需要结合Git代码提交规范-实践篇 一起食用哟~ 上一节我们已经讲了如…

Vue----Vue属性绑定

【原文链接】Vue----Vue属性绑定 通过v-bind:xxx 的方式即可实现Vue的属性绑定&#xff0c;比如如下代码&#xff0c;为div标签增加class属性&#xff0c;class的属性值通过msg变量提供 <template><div v-bind:class"msg">测试属性绑定</div> &l…