C语言两百行代码实现简易扫雷

news2024/9/23 3:30:59

在这里插入图片描述

文章目录

  • 前言
  • 一.代码实现
  • 二.设计思路
    • main()函数搭建框架
    • reset ( )函数
    • dis_play( )函数
    • setmine( )函数
    • player_move( )函数


前言

扫雷应该是我们接触到的第一个电脑游戏,用c语言实现扫雷对初学者来说是一个不错的锻炼
编写扫雷只需要用到数组、函数和生成随机数的知识,所以比较适合成为编程学习者编写的第一个小游戏。

如果不熟悉生成随机数的知识,可以去我的上一篇文章看看《C生成随机数》


一.代码实现

第一部分是源码复制就可以使用,每一个自定义函数在第二部分设计思路中都有详细解释,结合代码实现设计思路理解会有一个更好的效果

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS 11
#define COLS 11
#define MINE_NUMBER 10



void reset(char arr[ROWS][COLS], char ch)//将数组的每一个元素赋值为给定值
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			arr[i][j] = ch;
		}
	}
}

void setmine(char arr[ROWS][COLS])//利用生成随机数的知识使用随机的行号列标在mine数组放置地雷直到将宏定义的地雷数放置完
{
	int mine_number = MINE_NUMBER;
	int row = rand() % 9 + 1;//生成一到九的行号
	int col = rand() % 9 + 1;//生成一到九的列标

	while (mine_number > 0)
	{
		if (arr[row][col] == '0')//用来不是地雷的地方才能放置地雷
		{
			arr[row][col] = '1';//安装地雷
			mine_number--;
		}
		else
		{
			row = rand() % 9 + 1;
			col = rand() % 9 + 1;
		}

	}

void display(char arr[ROWS][COLS])//借助于‘|’和‘-’,将数组以游戏界面的显示打印出来
{
	for (int i = 0; i <= COL; i++)
	{
		printf(" %d  ", i);//打印行号
	}
	printf("\n\n");
	for (int i = 1; i <= ROW; i++)//第一个和第二个for进去分别打印一行数组和一条分割线
	{                             //数组行为:(空格)元素(空格)|(空格)元素(空格)|(空格)元素(空格)  
								  //分割线为: -      -      -   |   -      -     -   |   -     -      -
		printf(" %d  ", i);//打印列标

		for (int j = 1; j <= COL; j++)
		{

			printf(" %c ", arr[i][j]);
			if (j < COL)
			{
				printf("|");//为了美观,不打印最后一个'|'
			}

		}
		printf("\n");
		printf("    ");


		for (int k = 1; k <= COL; k++)
		{
			printf("---");
			if (k < COL)
			{
				printf("|");//为了美观,不打印最后一个'|'
			}

		}
		printf("\n");
	}

}


}
char nearmine(char mine[ROWS][COLS], int row, int col)//返回所选坐标字符型的地雷数量
{
	return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1]
		+ mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - 7 * '0');
}

int player_move(char mine[ROWS][COLS], char show[ROWS][COLS])//首先输入合法坐标然后玩家移动,每一步结果只有两个:踩到雷还有没踩到
                                                             
{
	int row = 0, col = 0;
	int flag = 1;
	do//输入合法坐标,在行号列标范围内,并且没有被走过
	{
		printf("\n---------------------------------------输入坐标,中间用空格隔开:>");
		scanf("%d %d", &row, &col);
		if (row >= 1 && row <= 9 && col >= 1 && col <= 9 && show[row][col] == '*')
			flag = 0;
		else
			printf("输入错误\n");
	} while (flag);
	if (mine[row][col] == '1')//踩到雷,返回-1
		return -1;
	else
	{
		show[row][col] = nearmine(mine, row, col);//nearmine将会返回字符型的地雷数
		                                          //将用来展示的show数组的相应坐标的元素赋值为周围的地雷数
		return 1;
	}

}

void meau()//菜单
{
	printf("------------------\n");
	printf("|     1.play     |\n");
	printf("|----------------|\n");
	printf("|     0.exit     |\n");
	printf("------------------\n");

}


void game()
{
	
	char mine[ROWS][COLS] ;//mine:地雷
	char show[ROWS][COLS] ;
	reset(mine,'0');//初始化mine为全零
	reset(show,'*');//初始化reset为全*
	setmine(mine);//安置地雷
	//display(mine);
	display(show);//展现
	int count = 0;
	
	do
	{
		int move = player_move(mine, show);
		if (move==-1)//踩到雷游戏结束
		{
			printf("\n---------------------------------------踩到地雷,游戏结束>\n");
			display(mine);
			break;

		}
		else if (move == 1)//没有踩到雷则步数count加一
		{
			count++;
			if (count == ROW * COL - MINE_NUMBER)//判断是否走完了步数,走完了游戏就成功了
			{
				printf("\n---------------------------------------排除地雷,游戏结束>\n");
				display(mine);
				break;

			}

		}
		
		display(show);
		
	} while (1);

	

}

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	do
	{
		meau();
		scanf("%d", &input);
		switch (input)
		{
		case 1 :
			game(); break;
		case 0 :
			break;
		default:
			printf("输入错误,请重新输入\n"); break;

		}

	} while (input);
	return 0;

	
	
}

二.设计思路

main()函数搭建框架

1.main()函数搭建框架:像所有的电脑游戏一样,我们需要一个菜单,通过菜单选择进入游戏和退出游戏,当一盘游戏结束时可以再次选择进入或者退出,菜单用printf()打印就可以解决,循环的进入游戏用do while()循环就可以解决

void meau()//菜单
{
	printf("------------------\n");
	printf("|     1.play     |\n");
	printf("|----------------|\n");
	printf("|     0.exit     |\n");
	printf("------------------\n");

}


void game()
{
    char mine[ROWS][COLS] = {0};//mine:地雷
	char show[ROWS][COLS] = {0};//show: 展示。0
	
  ········
}

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	do
	{
		meau();
		scanf("%d", &input);
		switch (input)
		{
		case 1 :
			game(); break;
		case 0 :
			break;
		default:
			printf("输入错误,请重新输入\n"); break;

		}

	} while (input);
	return 0;

	
	
}

当我们输入1,进入case 1时,将运行game()函数,game函数中的代码为具体游戏的实现
我们需要一个容器来存放游戏数据,二维数组是最好的选择,我们创建一个字符数组mine存放数据,一般扫雷游戏都是9X9的棋盘界面,所以我们可以设置行数和列数都为9,但在实际代码运行中9行9列还是不够的,会出现数组下标越界,所以最好在原来的上下左右各放一行将原来的9x9数组包起,所以我们实际需要一个11x11的数组,由于我们经常会使用,不妨对它宏定义一下

#define ROW 9
#define COL 9

#define ROWS 11
#define COLS 11

考虑到扫雷游戏中每选择一步如果没有踩雷,都会修改字符数组mine[ROW][COL]的相应元素来显示周围的地雷个数,这种修改会对游戏造成影响,所以不妨再定义一个数组show[ROW][COL],专门显示地雷数量,将判断和显示分离开

char mine[ROW][COL]:存放放置地雷和非地雷数据,我们用字符‘0’表示安全区;字符‘1’表示地雷,通过赋值填满数组
char show[ROW][COL]:当初用来展示的数组,初始化时全部赋值为字符‘*’,每选择一步,就在相应元素处返回周围地雷个数(mine数组相应元素周围‘1’的个数)

reset ( )函数

reset() :我们完成一局扫雷,还想下该怎么办?这时候我们构造一个函数reset(),将mine和show数组的每一个元素重置为‘0’和‘*’

该函数实际功能就是把接收到的数组中全部元素赋值为给定符号

reset()函数,遍历数组,赋值为给定字符

void reset(char arr[ROWS][COLS], char ch)//将数组的每一个元素赋值为给定值
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			arr[i][j] = ch;
		}
	}
}

dis_play( )函数

dis_play( ):我们扫雷肯定需要“雷区”来显示我们的数据,我们通过构造一个函数dis_play()将数组打印出来来实现这一功能

dis_play()功能就是借助’|'和‘_’等特殊字符,将数组以类棋盘形式打印出来

void display(char arr[ROWS][COLS])//借助于‘|’和‘-’,将数组以游戏界面的显示打印出来
{
	for (int i = 0; i <= COL; i++)
	{
		printf(" %d  ", i);//打印行号
	}
	printf("\n\n");
	for (int i = 1; i <= ROW; i++)//第一个和第二个for进去分别打印一行数组和一条分割线
	{                             //数组行为:(空格)元素(空格)|(空格)元素(空格)|(空格)元素(空格)  
								  //分割线为: -      -      -   |   -      -     -   |   -     -      -
		printf(" %d  ", i);//打印列标

		for (int j = 1; j <= COL; j++)
		{

			printf(" %c ", arr[i][j]);
			if (j < COL)
			{
				printf("|");//为了美观,不打印最后一个'|'
			}

		}
		printf("\n");
		printf("    ");


		for (int k = 1; k <= COL; k++)
		{
			printf("---");
			if (k < COL)
			{
				printf("|");//为了美观,不打印最后一个'|'
			}

		}
		printf("\n");
	}

}



运行代码,打印雷区
在这里插入图片描述

运行代码,打印show
在这里插入图片描述

setmine( )函数

setmine( ):经过reset()初始化后的mine数组中是没有地雷的,我们需要利用生成随机数的知识放置地雷,这时我们构建一个函数完成

使用头文件#include<stdlib.h>中的rand()函数可以生成一个0~32767的伪随机数,但使用rand()前先要使用srand()设置伪随机数起点
起点只要写一次,我们将srand((unsigned)time(NULL))定义在主函数,此处是一种固定的写法,time()函数需要引<time.h>头文件
将rand()%9+1即%ROW+1可以生产1~9这九个随机数,可以用来做数组的行号和列标

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));//设置起点的固定写法
	do
	{
		meau();
		scanf("%d", &input);
		switch (input)
		{
		case 1 :
			game(); break;
		case 0 :
			break;
		default:
			printf("输入错误,请重新输入\n"); break;

		}

	} while (input);
	return 0;

	
	
}

void setmine(char arr[ROWS][COLS])//利用生成随机数的知识使用随机的行号列标在mine数组放置地雷直到将宏定义的地雷数放置完
{
	int mine_number = MINE_NUMBER;
	int row = rand() % 9 + 1;//生成一到九的行号
	int col = rand() % 9 + 1;//生成一到九的列标

	while (mine_number > 0)
	{
		if (arr[row][col] == '0')//用来不是地雷的地方才能放置地雷
		{
			arr[row][col] = '1';//安装地雷
			mine_number--;
		}
		else
		{
			row = rand() % 9 + 1;
			col = rand() % 9 + 1;
		}

	}
	

运行代码–setmine()前
在这里插入图片描述

运行代码–setmine()后
在这里插入图片描述

player_move( )函数

player_move( ):扫雷用的棋盘打印好了,地雷放置好了下面要实现的就是移动这一个操作了,扫雷的移动只有两个结果:踩到雷还有没踩到雷,游戏扫胜利就是把除了雷之外的地块都走一次。另外,当我们每一次没有踩到雷时,要返回这个地块周围的地雷数>>

player_move()函数函数分为两个功能,第一个功能实现合法输入,输入的行号列标都应该在1~9这个范围中,另外,之前排除过的部分就不能再踩了。第二功能实现判断,踩到了雷就返回-1,没有踩到就返回1,并且应该赋值周围地雷雷数给show数组的相应元素,这个操作我们可以构造nearmine()函数完成,由于是字符型数组,nearmine()函数也要返回字符型的数字

char nearmine(char mine[ROWS][COLS], int row, int col)//返回所选坐标周围8格字符型的地雷数量
{
   //return 周围8格地雷数的ascll码值用下面这种方法就可以完成
	return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1]
		+ mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - 7 * '0');
}

int player_move(char mine[ROWS][COLS], char show[ROWS][COLS])//首先输入合法坐标然后玩家移动,每一步结果只有两个:踩到雷还有没踩到
                                                             
{
	int row = 0, col = 0;
	int flag = 1;
	do//输入合法坐标,在行号列标范围内,并且没有被走过
	{
		printf("\n---------------------------------------输入坐标,中间用空格隔开:>");
		scanf("%d %d", &row, &col);
		if (row >= 1 && row <= 9 && col >= 1 && col <= 9 && show[row][col] == '*')
			flag = 0;
		else
			printf("输入错误\n");
	} while (flag);
	if (mine[row][col] == '1')//踩到雷,返回-1
		return -1;
	else
	{
		show[row][col] = nearmine(mine, row, col);//nearmine将会返回字符型的地雷数
		                                          //将用来展示的show数组的相应坐标的元素赋值为周围的地雷数
		return 1;
	}



}

game( )

在game()函数我们编写程序去接收player_move( )函数返回的“-1”和“1”,接收到“1”,踩到地雷,游戏结束
接收到“1”,步数count++,并且判断conut是否等于非地雷地块的数量,一旦相等,游戏胜利,并且结束

void game()
{
	
	char mine[ROWS][COLS] ;//mine:地雷
	char show[ROWS][COLS] ;
	reset(mine,'0');//初始化mine为全零
	reset(show,'*');//初始化reset为全*
	setmine(mine);//安置地雷
	//display(mine);
	display(show);//展现
	int count = 0;
	
	do
	{
		int move = player_move(mine, show);
		if (move==-1)//踩到雷游戏结束
		{
			printf("\n---------------------------------------踩到地雷,游戏结束>\n");
			display(mine);
			break;

		}
		else if (move == 1)//没有踩到雷则步数count加一
		{
			count++;
			if (count == ROW * COL - MINE_NUMBER)//判断是否走完了步数,走完了游戏就成功了
			{
				printf("\n---------------------------------------排除地雷,游戏结束>\n");
				display(mine);
				break;

			}

		}
		
		display(show);
		
	} while (1);

	

}

整理不易,如果有帮助的话,那就给个三联吧,谢谢

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

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

相关文章

Java文件与IO流

首先我们要清楚什么是流&#xff0c;正如其名&#xff0c;很形象&#xff0c;流就是像水一样的东西&#xff0c;具有方向性&#xff0c;在java中 &#xff0c;流大概就是类 接下来&#xff0c;我们要对输入输出流有一个基本认识&#xff0c;什么是输入输出流呢&#xff1f; 输入…

漏斗分析、 python学习路径地图、数据科学技能书知识地图、数据安全治理解决方案、AIGC发展研究、经营的本质…| 本周精华...

▲点击上方卡片关注我&#xff0c;回复“8”&#xff0c;加入数据分析领地&#xff0c;一起学习数据分析&#xff0c;持续更新数据分析学习路径相关资料~&#xff08;精彩数据观点、学习资料、数据课程分享、读书会、分享会等你一起来乘风破浪~&#xff09;回复“小飞象”&…

【计算机组成原理】(四)原码补码的加减乘除

各种码的作用&#xff1a; 模运算的性质&#xff1a; -3&#xff08;-1&#xff09;*129 90*129 211*129 332*129 -15&#xff08;-2&#xff09;*129 我们发现等号右边都是9&#xff0c;相当于等号的左边的数除去12的余数都是9 那我们就说这几个等好左边的数&#xff0…

从零玩转设计模式之原型模式-yuanxingmoshi

title: 从零玩转设计模式之原型模式 date: 2022-12-11 20:05:35.488 updated: 2022-12-23 15:35:44.159 url: https://www.yby6.com/archives/yuanxingmoshi categories: - 设计模式 tags: - 设计模式 - 原型模式 什么是原型模式设计模式? 原型模式是一种软件设计模式&#…

【Rust 日报】2023-05-19 Bevy主分支添加了对WebGPU的支持

cascade-protocol Rust实现的 Cascade information reconciliation protocol for Quantum Key Distribution (QKD) 量子键分布&#xff1f;好像是密码学相关的。 隔行如隔山&#xff0c;小编也不懂这个具体是干啥的。感兴趣的同学可以前去研究。 https://github.com/gftea/casc…

SSR 应用与原 CSR 应用变更同步问题实践

在上一篇介绍《天猫汽车商详页的SSR改造实践》一文中提到过&#xff0c;为免影响线上应用&#xff0c;我们的一体化应用&#xff08;后面简称称 SSR 应用&#xff09;是在原 CSR 项目基础上另起的应用仓库。 背景 当商详业务有新需求迭代&#xff0c;CSR 仓库发生了变化&#x…

Java基础学习(18)反射、动态代理

Java基础学习 一、反射1.1 什么是反射1.2 获取class对象 二、综合练习2.1 保存信息2.2 文件的动态创建 三、动态代理3.1 什么是动态代理3.2 创建代理 一、反射 1.1 什么是反射 反射允许对封装类的字段&#xff0c;方法和构造函数的信息进行编程访问 个人理解&#xff1a; 就是…

【JVM】5. 本地方法接口和本地方法栈

文章目录 5.1. 什么是本地方法&#xff1f;5.2. 为什么使用Native Method&#xff1f;5.3. 本地方法栈 5.1. 什么是本地方法&#xff1f; 简单地讲&#xff0c;一个Native Method是一个Java调用非Java代码的接囗。一个Native Method是这样一个Java方法&#xff1a;该方法的实现…

【007】C++数据类型之原码、补码、反码

C数据类型之原码、补码、反码 引言一、原码、补码、反码的概述二、为什么要使用补码&#xff1f;三、对数据的存四、对数据的取总结 引言 &#x1f4a1; 作者简介&#xff1a;专注于C/C高性能程序设计和开发&#xff0c;理论与代码实践结合&#xff0c;让世界没有难学的技术。包…

KingbaseES V8R3 集群运维案例--sys_rewind恢复备库节点

​ 案例说明&#xff1a; 在KingbaseES V8R3集群执行failover切换后&#xff0c;原主库被人为误(未配置recovery.conf)启动&#xff1b;或者人为promote备库为主库后。需要将操作节点再重新加入集群&#xff0c;此时节点与主库的timeline将出现分叉&#xff0c;导致节点直接加入…

ChatGLM-6B 本地部署指南!

Datawhale干货 作者&#xff1a;宋志学&#xff0c;Datawhale成员 注意事项-写在最前 显卡需要至少6GB的显存使用GPU部署模型需要自行安装torch和与自己显卡匹配的CUDA、cudnn 下载ChatGLM-6B 在GitHub上下载chatglm-6b的源码&#xff0c;地址如下 https://github.com/THUDM/C…

信息安全-应用安全-SCA技术:SBOM应用实践初探

目录 软件供应链安全治理 供应链安全概述 风险治理重点 何为SBOM SBOM的元素 SBOM的格式 SBOM使用场景 如何使用SBOM 选择SBOM工具 SBOM与风险情报关联 围绕SBOM建立管理流程 关于SBOM的思考 现代软件都是组装的而非纯自研。随着开源组件在数字化应用中的使用比例越…

【C++】运算符重载与赋值运算符重载(显式与默认)的特性与格式以及前置++,后置++重载

文章目录 前言一、运算符重载二、赋值运算符重载1. 赋值运算符重载格式&#xff1a;2. 用户没有显式实现时&#xff0c;编译器会生成一个默认赋值运算符重载&#xff0c;以值的方式逐字节拷贝。3. 赋值运算符只能重载成类的成员函数不能重载成全局函数 三、前置与后置 前言 C为…

【Linux】Linux小程序-进度条

目录 一、\r和\n的理解 二、行缓冲区概念 三、进度条源代码 一、\r和\n的理解 \r&#xff1a;回车&#xff1b; \n&#xff1a;换行&#xff1b; 那么请问这两个有什么区别呢&#xff1f; 比如&#xff1a;我们在编写内容的时候&#xff0c;一行没有写完的情况下&#xff0c;需…

【信息安全案例】——软件解密技术(以OllyDbg为例)

目录 &#x1f552; 1. 软件解密技术&#x1f558; 1.1 概述&#x1f558; 1.2 爆破&#x1f558; 1.3 跟踪注册&#x1f558; 1.4 写出注册 &#x1f552; 2. 破解相关问题&#x1f558; 2.1 破解程度&#x1f558; 2.2 破解线索 &#x1f552; 3. 实验&#xff1a;使用 OllyD…

分布式系统原理

高可用是指系统无中断的执行功能的能力&#xff0c;代表了系统的可用程度&#xff0c;是进行系统设计时必须要遵守的准则之一。 而高可用的实现方案&#xff0c;无外乎就是冗余&#xff0c;就存储的高可用而言&#xff0c;问题不在于如何进行数据备份&#xff0c;而在于如何规避…

MacOS Ventura 13.4 (22F66) 带 OC 引导双分区黑苹果镜像

苹果今日向 Mac 电脑用户推送了 macOS 13.4 更新&#xff08;内部版本号&#xff1a;22F66&#xff09;&#xff0c;本次更新距离上次发布隔了 41 天&#xff0c;主要解决了与Apple Watch自动解锁、蓝牙键盘、屏幕使用时间和VoiceOver相关的问题&#xff0c;推荐大家安装升级。…

Vue 级联组件添加按钮并添加点击事件加传参

我这里采用的是jqvue实现的此功能&#xff0c;首先是要把按钮追加进去&#xff0c;当然头开始写真实dom会导致页面上也追加显示&#xff0c;但是我想实现的是在级联组件上追加所以&#xff0c;选择创建虚拟dom&#xff0c;然后传参这点实在是研究试错了半天&#xff0c;最后选择…

聚观早报|ChatGPT 推出官方 iOS App;大疆称将接收OPPO哲库工程师

今日要闻&#xff1a;ChatGPT 推出官方 iOS App&#xff1b;大疆称将接收OPPO哲库工程师&#xff1b;菜鸟、盒马启动上市计划&#xff1b;苹果公司限制员工使用ChatGPT&#xff1b;张勇&#xff1a;阿里云将分拆上市 ChatGPT 推出官方 iOS App 北京时间 5 月 19 日&#xff0c…

苹果扩充AirTag应用场景,苹果Find My应用更加广泛

根据美国商标和专利局&#xff08;USPTO&#xff09;公示的清单&#xff0c;苹果近日获得了一项技术专利&#xff0c;将追踪器附着人体或者服装上&#xff0c;从而监测健康和活动数据。 AirTag 不仅可以追踪某件事物之外&#xff0c;还可以通过安装在人体的不同位置&#xff0c…