C语言循序渐进写出扫雷游戏(控制台版)

(一)程序目标

        1.设立菜单。里面要含有游戏的开始界面和后续的选择选项。2.结束一场游戏后再次询问是否继续游玩下去。3.选择了开始游戏后,控制台冒出一个雷盘,待扫的雷用*代替。4.雷盘的行row,列col均为10.5.每次扫一次雷,如果扫到雷则游戏结束,否则冒出周围以自己为中心的8颗待扫的雷中的真实雷数量。6.当雷全部被扫光,游戏结束。

(二)思考过程

我们可以用三个文件将代码分成三个模块。text.c文件包含我们菜单的主体逻辑。game.c则包含游戏实现的逻辑,game.h是头文件,包含子函数的声明和预处理。这三个文件可以使程序有条不紊,每个模块都有自己要做的事。接下来我们开始思考text.c文件。我们设置的菜单可以设成一个子函数Menu,每次调用子函数Menu则打印菜单。同理,设置的游戏也可以是子函数,每次调用函数则开始进行扫雷。

Menu()
{
	printf("*****扫雷游戏*******\n");
	printf("*****1.游戏开始******\n");
	printf("*****0.退出**********\n");
	printf("请输入你的选择\n");
}

接下来我们需要根据玩家输入的数字来设计接下来的子函数。如果玩家输入数字1,则开始游戏。如果玩家输入数字0则结束游戏。因为结束一场游戏后需要再次询问是否继续游玩下去,所以我们考虑到使用do while循环,因为do while循环的特点是先执行再判断是否进行循环,符合我们的程序设定。再根据我们需要玩家输入的数字来进行分支,所以我们考虑使用swtich结构,正好符合我们的程序设置。这里我们用一个子函数gameselect将我们讲的内容包在一起。do while的判断条件正好是用户输入的数字1或0(也恰好适合用来判断真或假。在c语言中0代表假,非0代表真)我们也需要考虑如果玩家不输入合法数字,我们就要考虑再加个default分支告诉玩家输入错误。

gameselect()
{
	int selectnumber = 0;
	scanf("%d", &selectnumber);
	do {
		switch (selectnumber)
		{
		case 1:
			game();
			break;
			Menu();
		case 0:
			break;
		defalut:
			printf("输入不正确,请重新输入\n");
		}
	} while (selectnumber);
}

接下来我们再思考游戏的逻辑,即开始设计子函数game。我们可以定义两个雷盘,一个是打印给玩家的雷盘show,里面全是未排的雷。一个是真正的雷盘mine,里面包含雷的位置信息。show里面可以全装*,而mine可以先装字符0,再通过随机生成雷的技术将雷放进去。玩家在打印给玩家的雷盘show每排一个雷,我们就用真正雷盘上的位置来对比。由于两个雷盘的内容并不一样,我们又得设置一个函数用来初始化雷盘。我们可以设置一个函数Initboard,该函数可以通过参数来初始化雷盘。接下来我们得需要打印玩家所看见的雷盘show,它是玩家的游戏雷盘,这里可以用函数    Displayboard来打印它。两个雷盘初始化后我们就得考虑把雷放进去mine里面了。所以我们再设置一个函数Setmine,里面用来设置雷。既然雷放好了,我们就得考虑排雷了。所以我们再设置一个函数Finemine,里面装着对比雷盘的逻辑,即排雷的实现逻辑。这些子函数的传递都需要设立数组的参数,雷盘的行row,雷盘的列col来传递。

void game()
{
	char show[ROWS][COLS];//定义展示给玩家的雷盘
	char mine[ROWS][COLS];//定义实际上的真正的雷盘
	Initboard(show, ROWS, COLS, '*');//初始化展示给玩家的雷盘
	Initboard(mine, ROWS, COLS, '0');//初始化真正的雷盘
	Displayboard(show, ROW, COL);//打印雷盘
	Setmine(mine, ROW, COL);//放置雷
	Findmine(mine, show, ROW, COL);//排查雷
}

game()的主体逻辑已经写完。接下来我们就要考虑game中的各个函数的实现逻辑了。我们把这四个函数放在game.c文件中。我们先考虑Initboard这个子函数,该函数主要是把两个雷盘给初始化,放进去想放的东西。所以我们就接受主函数传递的参数,通过数组的遍历,即for的嵌套将每个字符放进去数组即可。

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;
		}
	}
}

接下来则是Dispalyboard函数了,该函数用来打印玩家所看到的雷盘,里面需要遍布雷*。为了让玩家好确认雷的坐标,我们可以再雷盘上加个坐标。我们先在第一个for循环(即打印每一行的for循环)第一行,即i=0的时候打印数字123456789 10就可以在雷盘的上方显示列的坐标col。同理,我们在第二个for循环里面(即打印每雷盘的每一列的for循环)打印123456789 10,就可以显示雷盘的行row坐标。但打印col坐标的时候需要i=0开始,不然就把row坐标挤出去一个。

void Displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-------扫雷游戏---------\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

接下来就是Setmine函数了。该函数主要把雷放进mine函数里面。我们可以通过rand,srand()这两个库函数实现伪随机算法,让雷‘1’随机放置。如果要放count个雷的数量,我们就可以用while循环,用count当做while的判断条件,直到count==0时while就会终止放雷‘1’。然后我们定义x,y(放置雷的坐标)用%来规定雷随机产生的位置,防止不在雷盘上。(如果不懂rand,srand,和%的作用可以去查阅资料)count_easy是后面宏定义的雷的数量

void Setmine(char mine[ROW][COL], int row, int col)
{
	int count = count_easy;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

接下来是最后的函数,也是最重要的函数Finemine了。我们主要的逻辑是对比两个雷盘。如果在show数组中玩家排雷的位置在数组mine正好是雷,则游戏结束,排雷失败。否则则统计mine中该位置周围的雷数量。我们可以将雷盘设成数组,正好可以传达雷的坐标,即位置信息。我们可以定义玩家输入的坐标x,y,再可以定义排雷成功的条件win。如果win

int Startistics(char mine[ROWS][COLS], 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';
}

直接返回该位置周围的雷的坐标即可,如果是雷有多少‘1’就有多少‘1’加在一起,不是雷就会加‘0’最后减去8个‘0’刚好就是雷的数量。将函数返回的数字加上字符‘0’就是字符中数字(这个数字恰好就是雷的数量)每排一颗雷就展示一遍show雷盘,与此同时排雷成功条件win++。这样一直循环,直到雷全部排完。当雷排完的数量等于空雷-雷的数量,我们就可以打印成功排雷了,然后把真正的雷盘打印给玩家看

void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < ROW * COL - count_easy)
	{
		printf("请输入坐标\n");
		scanf("%d %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 minenumber = Startistics(mine, x, y);
				show[x][y] = minenumber + '0';
				Displayboard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入\n");
		}
	}
	if (win == ROW * COL - count_easy)
	{
		printf("恭喜你成功排雷!");
		Displayboard(mine, ROW, COL);
	}
}

这个时候我们再把所有函数所需要的声明,宏定义写在game.h这个头文件上。ROW,COL是行,列。ROWS,COLS,是为了防止数组溢出,数组实际的行列。

#pragma once
#include 
#include 
#include 

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

#define ROW  9
#define COL 9

#define count_easy 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[ROW][COL], int row, int col);

void Findmine(char show[ROW][COL], char mine[ROW][COL], int row, int col);


再把这些函数一调用就成为了主函数main(srand的使用和time函数的使用,不懂可以去查资料,翻阅其他的博客即可)

int main()
{
	srand((unsigned int)time(NULL));
	Menu();//扫雷游戏介绍
	gameselect();
	return 0;
}

(三)代码的实现

text.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
Menu()
{
	printf("*****扫雷游戏*******\n");
	printf("*****1.游戏开始******\n");
	printf("*****0.退出**********\n");
	printf("请输入你的选择\n");
}
void game()
{
	char show[ROWS][COLS];//定义展示给玩家的雷盘
	char mine[ROWS][COLS];//定义实际上的真正的雷盘
	Initboard(show, ROWS, COLS, '*');//初始化展示给玩家的雷盘
	Initboard(mine, ROWS, COLS, '0');//初始化真正的雷盘
	Displayboard(show, ROW, COL);//打印雷盘
	Setmine(mine, ROW, COL);//放置雷
	Findmine(mine, show, ROW, COL);//排查雷
}
gameselect()
{
	int selectnumber = 0;
	scanf("%d", &selectnumber);
	do {
		switch (selectnumber)
		{
		case 1:
			game();
			break;
			Menu();
		case 0:
			break;
		defalut:
			printf("输入不正确,请重新输入\n");
		}
	} while (selectnumber);
}
int main()
{
	srand((unsigned int)time(NULL));
	Menu();//扫雷游戏介绍
	gameselect();
	return 0;
}

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;
	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 <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void Setmine(char mine[ROW][COL], int row, int col)
{
	int count = count_easy;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

int Startistics(char mine[ROWS][COLS], 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[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < ROW * COL - count_easy)
	{
		printf("请输入坐标\n");
		scanf("%d %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 minenumber = Startistics(mine, x, y);
				show[x][y] = minenumber + '0';
				Displayboard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入\n");
		}
	}
	if (win == ROW * COL - count_easy)
	{
		printf("恭喜你成功排雷!");
		Displayboard(mine, ROW, COL);
	}
}


game.h

#pragma once
#include 
#include 
#include 

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

#define ROW  9
#define COL 9

#define count_easy 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[ROW][COL], int row, int col);

void Findmine(char show[ROW][COL], char mine[ROW][COL], int row, int col);


(四)写后的话

希望看完这篇博客对你有所启发,我建议看过一遍后要自己动手思考写代码(这过程要全凭自己思考)一步一步实现,就能写出属于自己的不同于别人的代码。如有错误,还请指出!这里是しかし,我们下次再见!

你可能感兴趣的:(游戏,经验分享,c语言,开发语言)