首页 > 其他分享 >解锁C语言扫雷:详细攻略与完整代码解析

解锁C语言扫雷:详细攻略与完整代码解析

时间:2024-06-03 12:58:33浏览次数:13  
标签:COLS ROWS int 解锁 mine C语言 char 攻略 printf

目录

  • 在编程的奇妙世界里,我们总是不断探索和挑战自我。今天,我要和大家分享一个令人兴奋的旅程——用 C 语言实现扫雷小游戏。扫雷,这个经典的游戏,承载着无数人的回忆,而通过自己的双手用代码将它重现,更是一种独特的体验。让我们一同走进这个充满逻辑与乐趣的编程之旅吧!

一、游戏分析与设计

1、功能说明

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

2、界面设计

  • 参考经典的扫雷界面,实现出菜单选择界面、扫雷操作界面、扫雷失败界面等。

3、数据结构分析

  • 在扫雷的过程中,随机布置的雷和我们排查的位置都是需要信息来储存的,所以我们需要一个数据结构来储存这些信息。
  • 因为我们需要再99的棋盘上进行雷的布置和排查,所以就先创建出一个99的数组来存放信息。
    在这里插入图片描述
  • 设定如果这个位置有雷,则为1,没有雷,则为0。
    在这里插入图片描述
  • 我们在测试的就会发现有个问题,如果我们排查(4,5)这个坐标时,周围一圈8个位置,会统计出来周围雷的个数是1;但如果我们排查(7,1)这个坐标的时候,同样时访问周围的8个位置,左侧的三个坐标则越界了,怎么解决这个问题呢。
  • 为了防止排查越界这个问题,我们在设计的时候,将排雷的数组扩大一圈,编程1111,但雷还是布置在中间的99区域,周围一圈不布置雷就行,这样就解决了排查越界的问题。
  • 所以我们将存放数据的数组创建成11*11的比较合适。
    在这里插入图片描述
  • 继续分析,我们布置了雷,棋盘上有着雷的信息(1)和非雷的信息(0),如果我们排查出一个位置,这个位置不是雷,但这个坐标周围有一个雷,那我们就要将这个信息记录并打印出来,但这个信息需要储存在哪里呢,如果放在我们布雷的数组里(因为我们之前设定是雷用(1)表示,非雷用(0)表示),同样都是(1),就会造成信息上的混乱和打印的困难。
  • 为了解决这个问题,我们将雷的信息更改为字符,改为‘1’和‘0’,这样就能避免冲突了。
  • 这个方案可以解决雷的信息冲突问题,但在这一个数组上同时存在雷的信息、排查出的雷的个数信息,就会比较混乱,不够方便。所以我们采用另一种方案,就是我们专门设计一个棋盘(用mine数组表示)存放布好雷的信息,再用另一个棋盘(用show数组表示),存放排查出雷的信息。这样就互不干扰了。
  • 在mine数组中排雷,排查到的信息存到show数组中,并将show数组的信息打印作为排查参考。
  • 为了和传统扫雷游戏类似,我们将show数组开始初始化为‘*’,为了保持两个数组的一致性,设计同一个函数进行操作。mine数组也初始化为‘0’,布雷位置改为‘1’。
char mine[11][11]={0};//存放布雷的信息
char show[11][11]={0};//存放排雷的信息

如图:
在这里插入图片描述

4、文件设计结构

  • 我们计划使用三个文件来进行操作:
test.c//用于游戏中的测试逻辑
game.c//用于游戏功能的实现
game.h//用于游戏数据类型和函数的声明

二、扫雷游戏的代码实现

1、逐步讲解

1-1、打印菜单选择界面

test.c

#include<stdio.h>
void menu()//游戏初始选择界面
{
	printf("***********************\n");
	printf("******* 1. play *******\n");
	printf("******* 0. exit *******\n");
	printf("***********************\n");
}
int main(void)
{
	int choice = 0;//之所以放到循环外,是因为需要根据choice来判断是否进行游戏
	do
	{
		menu();		//将界面显示出来
		printf("是否开始游戏:“1”or“0”\n");	//显示输入信息
		scanf("%d", &choice);//输入选项信息
		switch (choice)		//根据选择信息进行相应选项
		{
		case 1:printf("扫雷\n"); break;		//进行游戏
		case 0:printf("游戏结束\n"); break;	//退出游戏
		default:printf("选择错误,请重新选择\n"); break;//重新进入游戏
		}
	} while (choice);//如果进行游戏或选择错误,会再次进入选择界面;如果
	return 0;
}

运行效果:
在这里插入图片描述

1-2、初始化棋盘

test. c

void game()
{
	char mine[ROWS][COLS];//存放布雷信息
	char show[ROWS][COLS];//存放排雷信息
	initboard(mine, ROWS, COLS,'0');//将mine数组初始化为'0'
	initboard(show, ROWS, COLS,'*');//将show数组初始化为'*'
}

☟注意:
1.初始化范围是11* 11的数组,不是9* 9,布雷的时候才是9*9。

game. h

#define ROW 9	//方便后期对棋盘进行修改
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

//函数声明
//初始化棋盘
void initboard(char board[ROWS][COLS], int rows, int cols, char set);

game. c

#include"game.h"//这里需要包含"game.h",否则无法使用ROWS和COLS
//棋盘初始化函数
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

1-3、打印棋盘

☟注意:
因为我们给用户展示的是9 * 9的扫雷界面,所以这里打印只需要打印9 * 9的中间界面即可

test. c

//打印棋盘
displayboard(mine, ROW, COL);//打印mine数组
displayboard(show, ROW, COL);//打印show数组

game. c

//打印棋盘函数
void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("———扫雷游戏———\n");//打印开头边框
	for (i = 0; i <= row; i++)//打印列序号,因为需要和棋盘对齐,所以从0开始
	{
		printf("%d ", i);
	}
	printf("\n");//打印完序号后,记得换行
	for (i = 1; i <= row; i++)//打印列
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)//打印行
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");//打印完一列,进行换行
	}
	printf("——————————\n");//打印结尾边框
}

game. h

//打印棋盘函数声明
void displayboard(char board[ROWS][COLS], int row, int col);

运行效果:
在这里插入图片描述
当然,在游戏过程中,我们只会显示show数组,也就是:
在这里插入图片描述

1-4、布置雷

棋盘准备好了,我们可以开始布置雷了。

test .c

//布置雷
setmine(mine, ROW, COL);
//生成随机数种子
srand((unsigned int)time(NULL));//生成随机数,整个函数只需要执行一次即可

game. h

//设置随机值所需要头文件
#include<stdlib.h>
#include<time.h>
//设置雷的个数
#define EASY_COUNT 10//简易模式下,雷的数量设置为10
//布置雷(函数声明)
void setmine(char mine[ROWS][COLS], int row, int col);

game .c

//布置雷
void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;//布置一个雷,count--,直达为0
	while (count)//雷需要随机布置
	{
		int x = rand() % row + 1;//坐标行生成随机数
		int y = rand() % row + 1;//坐标列生成随机数
		if (mine[x][y] == '0')	//开始布置雷,如果该位置没有雷,则布置雷,count--,如果有雷则跳过
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

☟注意:
雷的位置是随机生成的,随机数的生成请参阅C语言猜数字游戏秘籍:带你轻松玩转一文,有详细的讲解。

  • 运行效果:
    在这里插入图片描述

1-4、排雷

test .c

//从mine数组里找雷,找到的信息传递到show数组里去,所以两个数组都需要用,排查的过程只需要操作中间的布雷区域
findmine(mine,show, ROW, COL);

game. h

//排查雷
void findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

game.c

//计算点击坐标周边雷的个数
int getminecount(char mine[ROWS][COLS], int x, int y)//因为只有findmine函数使用,且在同一文件,不需要另外声明了
{
	return (mine[x - 1][y] + 
		mine[x - 1][y - 1] + 
		mine[x][y - 1] + 
		mine[x + 1][y - 1] + 
		mine[x+1][y] +	
		mine[x + 1][y + 1] + 
		mine[x][y + 1] + 
		mine[x - 1][y + 1] - 8 * '0');//该坐标值周围所有字符的值加起来,减去8个'0',得到的就是相应的数字
}
//排查雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//排出的雷的个数,排出一个就+1
	while (win<(row*col-EASY_COUNT))//需要反复判断是否有雷,所以这里需要一个循环函数
	{
		printf("请输入坐标:\n");
		scanf("%d%*c%d", &x, &y);//这里需要判断坐标的合法性,是否在范围内
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if(mine[x][y] == '1')//如果有雷,则输出信息,且打印出雷的所有位置,跳出循环
			{
				printf("很遗憾,你被炸死了\n");
				displayboard(mine, ROW, COL);
				break;
			}
			else//如果没有雷,则需要获取周围雷的个数
			{
				int count = getminecount(mine, x, y);
				show[x][y] = count + '0';//数字加上'0'就会变成相应的字符型数字;如:1+'0'='1'
				displayboard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	if (win == (row * col - EASY_COUNT))//
	{
		printf("恭喜你,排雷成功!\n");
		displayboard(mine, ROW, COL);
	}
}
  • 在排雷中,输入一个坐标,我们就需要对这个坐标进行判断,是否超出棋盘的边界。
  • 其棋盘中的坐标,也需要判断是否被排查过,如果是雷,结束游戏,不是雷,我就对这个坐标的周围的雷的数量进行统计。
  • 这时,我调用 getminecount函数进行实现。
    在这里插入图片描述
  • 如上图所示,‘0’、‘1’、‘2’、‘3’、‘4’、‘5’的ASCII码值为48、49、50、51、52、53,如果‘1’-‘0’就等于49-48=1,‘3’-‘0’等于51-48=3;由此,我们看出,一个字符型数字减去‘0’,就能得到该值的整型数字。
  • 因为我们坐标中的都是字符型,有雷为‘1’,所以将选中坐标周围的值加起来减去八个‘0’,就能得到雷的个数。
  • 通过不断排雷下去,将除雷以外的71个空位全部排除,我们就取得胜利了。
    运行效果:
    在这里插入图片描述

2、完整代码(加详细注释)

2-1、game.h

#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_COUNT 10//简易模式下,雷的数量设置为10

//函数声明
//初始化棋盘函数
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 mine[ROWS][COLS], int row, int col);
//排查雷
void findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

2-2、game.c

#include"game.h"//这里需要包含"game.h",否则无法使用ROWS和COLS
//棋盘初始化函数
void initboard(char board[ROWS][COLS], int rows, int cols, 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 displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("———扫雷游戏———\n");//打印开头边框
	for (i = 0; i <= row; i++)		//打印列序号,因为需要和棋盘对齐,所以从0开始
	{
		printf("%d ", i);
	}
	printf("\n");					//打印完序号后,记得换行
	for (i = 1; i <= row; i++)		//打印列
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)		//打印行
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");				//打印完一列,进行换行
	}
	printf("——————————\n");//打印结尾边框
}
//布置雷
void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;//布置一个雷,count--,直达为0
	while (count)//雷需要随机布置
	{
		int x = rand() % row + 1;//坐标行生成随机数
		int y = rand() % row + 1;//坐标列生成随机数
		if (mine[x][y] == '0')	//开始布置雷,如果该位置没有雷,则布置雷,count--,如果有雷则跳过
		{
			mine[x][y] = '1';
			count--;
		}
	}

}
//计算点击坐标周边雷的个数
int getminecount(char mine[ROWS][COLS], int x, int y)//因为只有findmine函数使用,且在同一文件,不需要另外声明了
{
	return (mine[x - 1][y] + 
		mine[x - 1][y - 1] + 
		mine[x][y - 1] + 
		mine[x + 1][y - 1] + 
		mine[x+1][y] +	
		mine[x + 1][y + 1] + 
		mine[x][y + 1] + 
		mine[x - 1][y + 1] - 8 * '0');//该坐标值周围所有字符的值加起来,减去8个'0',得到的就是相应的数字
}
//排查雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//排出的雷的个数,排出一个就+1
	while (win<(row*col-EASY_COUNT))//需要反复判断是否有雷,所以这里需要一个循环函数
	{
		printf("请输入坐标:\n");
		scanf("%d%*c%d", &x, &y);//这里需要判断坐标的合法性,是否在范围内
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if(mine[x][y] == '1')//如果有雷,则输出信息,且打印出雷的所有位置,跳出循环
			{
				printf("很遗憾,你被炸死了\n");
				displayboard(mine, ROW, COL);
				break;
			}
			else//如果没有雷,则需要获取周围雷的个数
			{
				int count = getminecount(mine, x, y);
				show[x][y] = count + '0';//数字加上'0'就会变成相应的字符型数字;如:1+'0'='1'
				displayboard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	if (win == (row * col - EASY_COUNT))//
	{
		printf("恭喜你,排雷成功!\n");
		displayboard(mine, ROW, COL);
	}
}

2-3、test.c

#include"game.h"
void menu()//游戏初始选择界面
{
	printf("***********************\n");
	printf("******* 1. play *******\n");
	printf("******* 0. exit *******\n");
	printf("***********************\n");
}
void game()
{
	char mine[ROWS][COLS];//存放布雷信息
	char show[ROWS][COLS];//存放排雷信息
	initboard(mine, ROWS, COLS,'0');//将mine数组初始化为'0'
	initboard(show, ROWS, COLS,'*');//将show数组初始化为'*'
	//棋盘打印
	//displayboard(mine, ROW, COL);//打印mine数组
	displayboard(show, ROW, COL);//打印show数组
	//布置雷
	setmine(mine, ROW, COL);
	//displayboard(mine, ROW, COL);
	//排雷
	findmine(mine,show, ROW, COL);//从mine数组里找雷,找到的信息传递到show数组里去,所以两个数组都需要用,
				//排查的过程只需要操作中间的布雷区域

}
int main(void)
{
	int choice = 0;//之所以放到循环外,是因为需要根据choice来判断是否进行游戏
	srand((unsigned int)time(NULL));//生成随机数,整个函数只需要执行一次即可
	do
	{
		menu();		//将界面显示出来
		printf("是否开始游戏:“1”or“0”\n");	//显示输入信息
		scanf("%d", &choice);//输入选项信息
		switch (choice)		//根据选择信息进行相应选项
		{
		case 1:printf("游戏开始\n"); game(); break;		//进行游戏
		case 0:printf("游戏结束\n"); break;	//退出游戏
		default:printf("选择错误,请重新选择\n"); break;//重新进入游戏
		}
	} while (choice);//如果进行游戏或选择错误,会再次进入选择界面;如果
	return 0;
}

三、结尾

  • 经过一番努力和探索,我们成功地用 C 语言打造出了一个简单却充满趣味的扫雷小游戏。当然,随着不断的学习,也会将这个项目进行不断的完善和改进。
  • 通过这个项目,不仅深入理解了 C 语言的各种特性和编程技巧,更感受到了创造的乐趣和成就感。希望这篇博客能给对 C 语言和游戏开发感兴趣的朋友们带来一些启发和帮助。让我们继续在编程的道路上不断前行,创造更多精彩的作品!期待下一次与大家分享新的编程冒险。

标签:COLS,ROWS,int,解锁,mine,C语言,char,攻略,printf
From: https://blog.csdn.net/lxy370198409/article/details/139397394

相关文章

  • C语言简述
    初识C语言目录初识C语言前言一、C语言是什么?二、第一个C语言程序1.打开vs2022编译器2.创建源文件3.写代码4.main函数5.printf库函数总结前言其实我也不知道该写什么,这个是我第一篇博客,我就讲述一下我自己在课程中所理解到的知识点给大家分享一下。一、C语言是什......
  • C语言简述2
    文章目录前言一、关键字介绍二、字符和ASCCII码表三.字符串和\01.字符串2.\0字符三.转义字符总结前言接这个上一节课知识点讲,我们在上一节已经说了怎么创建第一个C语言程序,现在我们来讲其中一些知识点。一、关键字介绍在C语言中比如if,return,int这些符号被称为......
  • Qt中怎么引用C语言的.c文件?
    Qt窗口项目使用的源文件是.h/.cpp文件,它们是对应C++文件。在实际应用中,你可能有现成的.h/.c文件需要引用。那么,这些文件能够引用吗?又怎么引用呢?以下来讨论这个问题。本例在ubuntu18中Qt5.8.0的Widgets项目编译通过,估计在CentOS和Windows系统也应该可以通过。一般情况下,通过宏“#......
  • Qt中怎么引用C语言的.h文件?
    Qt窗口项目使用的源文件是.h/.cpp文件,它们是对应C++文件。在实际应用中,你可能有现成的.h/.c文件需要引用。那么,这些文件能够引用吗?又怎么引用呢?以下来讨论这个问题。本例在ubuntu18中Qt5.8.0的Widgets项目编译通过,估计在CentOS和Windows系统也应该可以通过。本例要引用的.h文件......
  • 数据结构-单链表操作及代码实现(C语言)
    (一)单链表与线性表支持随机访问的特点相比,单链表的特点是适合插入与删除。结构体定义typedefintElementType;//数据元素类型定义typedefstructLNode//单链表结构体定义{ElementTypedata;//数据域structLNode*next;//存储下一个结点的地址}LNode,*L......
  • 【C语言进阶】--- 动态内存管理
    动态内存管理函数1.malloc函数void*malloc(size_tsize);功能:向堆区的空间中申请一块大小为size个字节的空间,返回指向这块空间的指针如果开辟失败会返回一个NULL指针,因此要检查malloc的返回值,避免返回NULL指针后再访问空指针malloc申请的空间,程序退出后会还给操作系统......
  • 轻松拿捏C语言——【文件操作】
    ......
  • 解锁声音新玩法:AI 配音神器来袭!
    在这个数字化的时代,AI技术的发展为我们的生活带来了许多便利。其中,AI配音神器就是一款备受欢迎的工具,它能够为各种视频、音频内容提供高质量的配音服务。而今天,我要向大家介绍的是我本人自用的AI配音神器版本,它超级稳定,拥有百位主播音色,登陆即可使用!这款AI配音神器......
  • C语言文件操作
    一.文件的先关知识1.1什么是文件?                                                  磁盘上的文件是文件,在程序设计的时候,我们一般将文件分为两种:程序⽂件、数据⽂件(......
  • 【C语言项目实战】使用单链表实现通讯录
                                                                  ......