C语言制作扫雷游戏(基础版赋源码)

目录

引言

扫雷游戏的分析和设计

        扫雷网页游戏分析

        主页

        游玩过程

        扫雷游戏的设计

扫雷游戏的实现

        分文件操作

        正式教学

        1.main()函数(主函数)

        2.test()函数(大整体逻辑实现)

        3.game函数(正式进入游戏)

        数组创建

        整体代码逻辑整理

        game.h头文件

        扫雷.c源文件

        各函数实现       

        chu函数(初始化数组)

        game.c

        扫雷.c

        函数实现

        set函数(布置雷)

        game.c

        扫雷.c

        函数实现

        print函数(打印一次明盘)

        game.c

        扫雷.c

        函数实现

        Findlei函数(排查雷函数)

        game.c

        扫雷.c

        函数实现

三个文件的源码

        game.h

        game.c

        扫雷.c

结语:


 

引言

        扫雷相信大家都玩过,那么该篇我将会一步步教如何去用基础的语言知识来制作一个简易的扫雷游戏,我们将会用(C语言数组和函数详解-CSDN博客)该篇中所曾讲到过的多文件的方式来进行游戏的制作,除开C语言基础的那部分外,该篇我们还会用到rand函数,srand函数和time函数来进行随机性的布置雷,

若不懂这三个函数可跳转至该博客查看学习(用C语言基础做一个猜数字小游戏-CSDN博客),这里就不重复赘述了,

该篇基础版的教程看完理解后如果想要实现更多功能,可跳转至C语言制作扫雷游戏(拓展版赋源码)-CSDN博客进行学习,那么,接下来就正式进入正文啦————————>

                                


扫雷游戏的分析和设计

        在对扫雷游戏进行分析时,我们需要先知道扫雷游戏的布局和功能,所以我们可以先看一下网页版的扫雷,在线扫雷游戏的网址是扫雷游戏网页版 - Minesweeper

        好,那我们来看一下扫雷游戏具体实现了什么功能

        扫雷网页游戏分析

        主页

        先看刚进去时的页面,首先通过看这个页面,我们可以发现,他带有难度选择(会在下一篇扫雷拓展里实现),左上角为雷的数量,右上角为游玩的时间(所涉及知识较多,不实现),中间的笑脸可以重新开始,由此,我们可以通过观察游戏的页面得到,基础版的扫雷我们需要实现       

        随机布置雷的操作

        棋盘大小的控制

        游戏结束后自选继续游戏或结束游戏

        这三个操作

        

        游玩过程

        那接下来,我们来分析一下游玩时他的变化,如图,我们可以发现,游玩时,点击某处,他会通过反馈给玩家点周围有几个雷,所以我们需要通过编程来实现选点后该点周围有几个雷的信息反馈

        如果位置不是雷,就显示周围有几个雷

        如果位置是雷,就炸死游戏结束

        把所有雷之外的所有非雷都找出来,排雷成功,游戏结束

        因为该篇是基础版的扫雷,不会像这个图片中那般做到选择一个点从而展开一整片,而是返回该点附近有几个点,因为展开一整片会用到函数递归的知识,若感兴趣可以看我下一篇发布的博客(之后会更新链接到这个括号里)

        

        那么,扫雷游戏的分析已经分析完了,接下来就是设计扫雷游戏了

        扫雷游戏的设计

        我们通过上面的分析,将要制作的扫雷游戏的功能列举出来

        扫雷游戏功能说明:

                使用控制台(就是运行时的黑框框)实现经典的扫雷游戏

                游戏可以通过菜单实现继续玩或者退出游戏
                扫雷的棋盘为9*9格子(下一篇会讲自定义格子)
                默认随机布置10个雷(下一篇会讲自定义雷数)
                可以排查雷
                        如果位置不是雷,就显示周围有几个雷(下一篇会讲清空一片)
                        如果位置是雷,就炸死游戏结束
                        把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束

 

扫雷游戏的实现

        分文件操作

        首先,在讲解代码前,我们需要创建三个文件,命名随你们,如图

        game.h用于存放头文件以及函数的声明

        game.c用于存放自定义函数的功能实现的源码

        扫雷.c用于实现扫雷功能

        注:把头文件存放在game.h时,就只需要调用一个game.h即可了,不需要再在源文件里写过多头文件

        

        正式教学

        那么,接下来,就开始正式教学

        1.main()函数(主函数)

        首先,一开始,我们肯定是在主源文件里写代码,然后再去其他源文件写实现函数,那么,我们先在(扫雷.c)的源文件里写主函数,如下,然后test函数里,就是我们具体要进行的下一步操作,即(给菜单,让用户选择,根据不同的选择,执行不同的操作)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
	test();
	return 0;
}

        

        2.test()函数(大整体逻辑实现)

        接下来,我们写test函数时,首先我们需要创建一个菜单,即menu()函数,进行输入指引,如果开始游玩,就要求输入1,如果结束游玩,就要求输入0,再通过循环来进行判断,就可以完美实现扫雷游戏功能中(可以通过菜单实现继续玩或者退出游戏)这个的功能,那循环应该用什么来进行判断跳出与否呢,那就是创建一个变量,通过变量的输入来进行判断

        如下方代码,使用do while循环,先不判断,进入其中,然后先调用 menu 函数输出菜单,接下来输出提示语“请选择:”让玩家进行选择,然后根据输入情况通过switch语句来返回对应的提示语,具体看下方代码,这样就完成了整个代码的框架

        下方代码中game函数就是正式的游戏实现函数。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

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

void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏开始\n");
            game();
			break;
		case 0:
			printf("欢迎下次游玩\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

 

        3.game函数(正式进入游戏)

        那么整体框架写完了,接下来就来实现游戏进行时的步骤了,首先,在进行游戏前,需要进行前置操作,就如数组的创建和初始化,等等

        数组创建

        既然我们这个扫雷的棋盘大小是9*9的棋盘,那么我们就可以创建数组来表示这个棋盘,那么肯定会有俩个棋盘,一个是不表露在外的用于判断该处是否是雷的暗盘(可以“0”表示非雷,“1”表示雷,一开始全初始化成‘0’),和表露在外,显示给人看的明盘(一开始全初始化成“ * ”)

        那么数组创建时候,该开多大呢,是开9*9的数组吗,明显不是,因为若开的是9*9,那么数组下标就是0-8,既不方便进行后续的坐标排雷,而且若是最外围的那一圈坐标,在排雷时还会导致数组越界(因为会探查坐标的四周有几个雷),如图

        

        所以,我们应该开11*11的数组才是最好的

        

        

        整体代码逻辑整理

        那么,数组创建注意事项讲完了,我们就可以开始讲如何一步步实现扫雷了

        如下方代码,我们根据代码来进行分析

        首先,为了方便,我们可以把头文件的#include <stdio.h>放入game.h的头文件中,这样只需要调用game.h头文件就可以了,接下来,为了完成扫雷游戏,我们肯定要分布进行,分几步呢,如下:

        1.创建数组(我这里cun数组表示暗盘,show数组表示明盘)

        2.初始化数组(我这里自定义了chu函数来初始化数组)

        3.在暗盘上随机布置雷(我这里自定义了set函数来布置雷)

        注:因为我是用的rand函数来进行随机布置雷,所以需要在game函数的开头调用srand函数和time函数来初始化“种子”(自然,这些函数的头文件也是放在game.h中)

        若看到这里了不知道这三函数是什么,请跳转至用C语言基础做一个猜数字小游戏-CSDN博客 学习完相关知识后再来接着观看,这里就不过多赘述了

        4.布置完雷后肯定要打印一次棋盘(明盘),此时应全为“ * ”(我这里自定义了print函数来打印棋盘)

        5.排查雷,通过输入坐标来排查雷,给出对应反馈(我这里自定义了Find雷来排查与反馈)

        OK,步骤说完了,我们先来看看头文件,和当前源文件的代码(其实这俩个文件的代码已经全了,后续操作都在game.c里操作了),然后再接着讲每个函数的具体实现(这些函数的具体实现就放在game.c里了

        game.h头文件

        都是函数和变量的声明以及宏定义

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define Chang 9//好处是,想更改棋盘大小时候直接改这里就好了,不用在程序里一个个改,方便很多
#define Kuan 9

#define Changs Chang+2
#define Kuans Kuan+2

#define Leisum 10

void chu(char arr[Changs][Kuans], int hang, int lie, char fu);

void print(char arr[Changs][Kuans], int lie, int hang);

void set(char arr[Kuans][Changs], int hang, int lie, int ge);

void Findlei(char yin[Kuans][Changs], char xian[Kuans][Changs], int lie, int hang);

        扫雷.c源文件

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

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

void game()
{
    srand((unsigned int)time(NULL));

	//完成扫雷游戏
	char cun[Kuans][Changs] = { 0 };
	char show[Kuans][Changs] = { 0 };

	//初始化函数
	chu(cun, Kuans, Changs, '0');
	chu(show, Kuans, Changs, '*');
	
	//布置雷
	set(cun, Kuan, Chang, Leisum);

	//打印棋盘
	print(show, Kuan, Chang);

	//排查雷
	Findlei(cun, show, Kuan, Chang);
}

void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏开始\n");
            game();
			break;
		case 0:
			printf("欢迎下次游玩\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

那么看完这里,想必对于实现扫雷的大体框架都已经通透了,那么接下来,就让我们对这一个个自定义函数的思路进行讲解与分析。

 

        各函数实现       

        注:接下来的代码全在game.c中实现

 

        chu函数(初始化数组)

        我们先看代码

        game.c

void chu(char arr[Kuans][Changs],int hang,int lie,char fu)
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
			arr[i][j] = fu;
	}
}

        扫雷.c

chu(cun, Kuans, Changs, '0');
chu(show, Kuans, Changs, '*');

        函数实现

        通过看这俩个代码应该不难看出,首先,Kuans与Changs在game.h中已宏定义过,分别表示棋盘的行和列加2的结果,原因在最开始就已经分析过了,然后最后一个参数表示需要初始化成的字符,第一个参数表示要初始化哪个函数,我们只需要分别将暗盘和明盘进行初始化即可

        函数体内很简单,想实现就套俩层for循环全部遍历一遍并赋值即可

 

        set函数(布置雷)

        这个地方就用到了之前提过的rand函数,srand函数和time函数来取随机值,我们先看代码

        game.c

void set(char arr[Kuans][Changs],int hang,int lie,int ge)
{
	while (ge--)
	{
		int h = 1 + rand() % hang;
		int l = 1 + rand() % lie;
		if (arr[h][l] == '0')
		{
			arr[h][l] = '1';
		}
		else
			ge++;
	}
}

        扫雷.c

set(cun, Kuan, Chang, Leisum);

        函数实现

        第一个参数为要传递的数组

        第二个参数为棋盘宽度

        第三个参数为棋盘长度

        第四个参数为需要放置的雷的数量(game.h头文件中已宏定义为10)

        为什么这次传的参数是棋盘的宽度和长度而不用加2呢,因为随机放置的雷只需要放在下标为1-9的范围即可

        这个也很容易实现,只需要在进行这个函数操作前,先调用一次srand函数来初始化“种子”(这个操作在写在game函数或test函数里,别写这里)后,然后通过while循环判断是否所有雷都放完了,若没有放完,就通过rand函数给行与列分别一个符合下标的随机值,若那个坐标中的暗盘不为“1”,就变为“1”,ge--,若为“1”,就ge++,当这次随机失败,重新随机,直到10个雷全部放完结束循环,任务完成

 

        print函数(打印一次明盘)

        这个函数顾名思义,就是把目前的棋盘打印出来,但是注意,打印的是明盘,别把暗盘也打出来了,先看代码

        game.c

void print(char arr[Kuans][Changs], int hang, int lie)
{
	for (int i = 0; i <= lie; i++)
		printf("%d ", i);
	printf("\n");
	for (int i = 1; i <= hang; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= lie; j++)
			printf("%c ", arr[i][j]);
		printf("\n");
	}
}

        扫雷.c

print(show, Kuan, Chang);

        函数实现

        这个代码就很简单,跟chu函数其实大差不差

        第一个参数为要传的数组

        第二个参数为要传的宽度

        第三个参数为要传的长度(传的长宽是棋盘的长宽,即为9)

        具体代码实现时候,可以直接像chu函数那样,只需要把chu函数的范围改成1-9,然后赋值改成输出即可,但为什么不这么做呢

        因为,为了之后排查雷的时候方便,所以能输出指示坐标的话,就会明显很多,所以可以先打印一行,表示列标,然后在接下来的每一行时,先打印一下第几行,再把该行的所有字符隔开输出,输出完再换行即可,我们可以进行测试一下,如图,输出的棋盘效果是这样的,是不是看着就清晰很多了

 

        Findlei函数(排查雷函数)

        这个函数也是容易实现的,就是需要注意细节和错误输入时的信息反馈,那我们先来看代码

        game.c

void Findlei(char yin[Kuans][Changs], char xian[Kuans][Changs], int lie, int hang)
{
	int x = 0, y = 0;
	int ge = 0;
	while (1)
	{
		printf("请输入要排查的坐标(行 列):");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
			printf("输入的坐标有误,请重新输入:");
		else if (xian[x][y] != '*')
			printf("该坐标已排查过,请重新输入:");
		else
		{
			if (yin[x][y] == '1')
			{
				system("cls");
				printf("很遗憾,你被炸死了,游戏结束\n");
				print(yin, Kuan, Chang);
				break;
			}
			else
			{
				int fang[8][2] = { {0,1},{0,-1},{-1,0},{1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
				xian[x][y] = '0';
				for (int i = 0; i < 8; i++)
				{
					if (yin[x + fang[i][0]][y + fang[i][1]] == '1')
						xian[x][y]++;
				}
				ge++;
				system("cls");
				print(xian, Kuan, Chang);
				if (ge + Leisum == Kuan * Chang)
				{
					printf("恭喜你,排雷成功\n");
					break;
				}
			}
		}
	}
}

        扫雷.c

Findlei(cun, show, Kuan, Chang);

        函数实现

        代码看似比较长,其实内部逻辑很简单,我们先来看一下它的参数后再来讲逻辑

        第一个参数传的是暗盘数组

        第二个参数传的是明盘数组

        第三个参数传的是盘的宽度

        第四个参数传的是盘的长度

        为什么要传俩个数组呢,因为判断时候需要用到暗盘数组,输出时候需要用到明盘数组。

        那为什么宽度不选长度和宽度加2的Changs和Kuans呢,因为我们需要猜的下标只需要Chang和Kuan即可,且后续调用输出print函数时也用不到Changs与Kuans

        讲完参数后,我们来讲讲这个函数的逻辑实现,首先我们先创建3个变量,2个变量分别表示行和列(即代码中的x,y),剩余的那个变量表示已经排除的雷的个数(即代码中的ge)

        接下来,我们使用while(1)的方式创造一个死循环,直到达到特定条件再用break出循环(排到雷了或者雷排完了)在循环里,先给出提示让玩家按要求输入要选择排雷的坐标,然后根据玩家的输入进行反馈(若输入没问题就进入下一步,若输入的坐标越界了或者输入的坐标之前已经输入过了,就进行适当的提示反馈),进入下一步后,

        若选中的地址为‘1’,就反馈被炸死,然后输出一次暗盘(告诉玩家所有埋雷位置)跳出循环结束本次游戏,

        若选中的地址为‘0’,就将该明盘上的该坐标位置的‘ * ’变为这个位置附近有几颗雷,怎么变就是先将该坐标赋值为‘0’,然后创建一个数组,表示周围八个方位,然后通过循环,将八个方位全遍历一遍,若遇到 ‘1’,则进行一次++,遍历完后,输出一次明盘,然后再用排除雷的个数与所以没雷的个数进行比较,若个数相同,则代表排雷成功,反馈游戏胜利即可,然后跳出循环,结束游戏。如此,所有基础功能都实现了,代码完成,是不是很简单

                                      


三个文件的源码

        关于扫雷方面的基础已经全部讲解完啦,接下来附上三个文件的源码

        为了程序运行时的美观,可以用上system("cls")来清屏,

        game.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define Chang 9//好处是,想更改棋盘大小时候直接改这里就好了,不用在程序里一个个改,方便很多
#define Kuan 9

#define Changs Chang+2
#define Kuans Kuan+2

#define Leisum 10

void chu(char arr[Changs][Kuans], int hang, int lie, char fu);

void print(char arr[Changs][Kuans], int lie, int hang);

void set(char arr[Kuans][Changs], int hang, int lie, int ge);

void Findlei(char yin[Kuans][Changs], char xian[Kuans][Changs], int lie, int hang);

        game.c

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

//初始化棋盘
void chu(char arr[Kuans][Changs],int hang,int lie,char fu)
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
			arr[i][j] = fu;
	}
}

//打印棋盘
void print(char arr[Kuans][Changs], int hang, int lie)
{
	for (int i = 0; i <= lie; i++)
		printf("%d ", i);
	printf("\n");
	for (int i = 1; i <= hang; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= lie; j++)
			printf("%c ", arr[i][j]);
		printf("\n");
	}
}

//布置雷
void set(char arr[Kuans][Changs],int hang,int lie,int ge)
{
	while (ge--)
	{
		int h = 1 + rand() % hang;
		int l = 1 + rand() % lie;
		if (arr[h][l] == '0')
		{
			arr[h][l] = '1';
		}
		else
			ge++;
	}
}

void Findlei(char yin[Kuans][Changs], char xian[Kuans][Changs], int lie, int hang)
{
	int x = 0, y = 0;
	int ge = 0;
	while (1)
	{
		printf("请输入要排查的坐标(行 列):");
		scanf("%d %d", &x, &y);
		if (x<1 || x>hang || y<1 || y>lie)
			printf("输入的坐标有误,请重新输入:");
		else if (xian[x][y] != '*')
			printf("该坐标已排查过,请重新输入:");
		else
		{
			if (yin[x][y] == '1')
			{
				system("cls");
				printf("很遗憾,你被炸死了,游戏结束\n");
				print(yin, Kuan, Chang);
				break;
			}
			else
			{
				int fang[8][2] = { {0,1},{0,-1},{-1,0},{1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
				xian[x][y] = '0';
				for (int i = 0; i < 8; i++)
				{
					if (yin[x + fang[i][0]][y + fang[i][1]] == '1')
						xian[x][y]++;
				}
				ge++;
				system("cls");
				print(xian, Kuan, Chang);
				if (ge + Leisum == Kuan * Chang)
				{
					printf("恭喜你,排雷成功\n");
					break;
				}
			}
		}
	}
}

        扫雷.c


//扫雷游戏的拓展
//	是否可以选择游戏难度
//	简单 9*9棋盘,10个雷
//	中等 16*16棋盘 40个雷
//	困难 30*16棋盘 99个雷
//	自定义	
//
//	如果排查位置不是雷,周围也没有雷,可以展开一片
//	是否可以标记雷
//	是否可以加上排雷的时间显示
//	等等等
#define _CRT_SECURE_NO_WARNINGS

#include "game.h"
void menu()
{
	printf("********************\n");
	printf("*****  1.play  *****\n");
	printf("*****  0.exit  *****\n");
	printf("********************\n");
}

void game()
{
	//完成扫雷游戏
	char cun[Kuans][Changs] = { 0 };//全部初始化为0
	char show[Kuans][Changs] = { 0 };//全部初始化为*
	//初始化函数
	chu(cun, Kuans, Changs, '0');
	chu(show, Kuans, Changs, '*');
	
	//布置雷
	set(cun, Kuan, Chang, Leisum);

	//打印棋盘
	print(show, Kuan, Chang);


	//排查雷
	Findlei(cun, show, Kuan, Chang);
}

void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls");
			printf("游戏开始\n");
			game();
			break;
		case 0:
			system("cls");
			printf("欢迎下次游玩\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

 

结语:

        那么,基础版的扫雷游戏的功能就能全部实现啦,如果想要让朋友玩的话肯定不可能直接把这些源码发给朋友吧,这个时候,我们可以将VS里的Debug改成Release,然后调试运行一下,如图

        

        然后调试完后,可以打开存放这个项目的文件夹,找到x64文件夹,再点开Release,里面有个应用程序,直接发给朋友,朋友就可以点开玩啦,如图

        

        这样就可以啦,那么本篇文章的内容就到此为止啦,希望对你有帮助,谢谢观看,(如果觉得写的不错的话可以分享给朋友哟

        拓展扫雷也已完成,感兴趣可去C语言制作扫雷游戏(拓展版赋源码)-CSDN博客学习,新增可以自选难度,或者自定义,然后能标记雷,展开一片等等操作

                                

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值