当前位置:网站首页>C语言实现扫雷游戏(完整版)

C语言实现扫雷游戏(完整版)

2022-07-06 09:19:00 是王久久阿

三子棋一样,扫雷的核心思想也是二维数组的利用。

扫雷游戏还用到:循环语句分支语句函数的递归以及指针等知识,具体知识在前几篇文章中有过讲解。

利用C语言在命令窗口实现扫雷游戏:

1、输入方式:玩家输入坐标,如果不是雷,游戏继续;如果是雷,游戏结束。

2、坐标的判定:如果该坐标周边有雷,则显示周围雷的信息;如果该坐标周围没有雷,则一次展开多项,直到展开到雷的附近。

3、游戏结束判定:当玩家触碰到雷,或者棋盘上所有非雷的区域被玩家全部找到。 

                

目录

主菜单

游戏部分

1.设置棋盘及初始化

2.布雷

3.打印棋盘

4.Cue提示雷的信息

5.Unfold展开功能

6.Guess排查

试玩

整体代码

(一)game.h

(二)game.c

(三)text.c


主菜单

主菜单用do while循环结构,实现玩完一次游戏不过瘾,还可以接着玩。(do while 循环可以保证整个循环至少运行一次)

//text.c
#include "game.h"
void menu()//菜单
{
	printf("********************\n");
	printf("*****  1.paly  *****\n");
	printf("*****  0.exit  *****\n");
	printf("********************\n");
}
int main()
{
	int input = 0;
	do 
	{
		menu();
		printf("请选择(1/0):>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

为了之后测试可以精确找到bug位置所在,我们要养成每写完一部分代码,就测试一下的良好习惯。这里我们测试一下主菜单部分的代码是否可以正常运行。

游戏部分

1.设置棋盘及初始化

(一)设置棋盘

棋盘的规划:

mine棋盘:存储布雷的信息(不给玩家显示)

show棋盘:玩家游戏的棋盘。(给玩家显示)

棋盘的行和列:

1、通过#define定义常量的方式来设定,方便以后代码的更新。

2、为了之后之后访问二维数组时不越界,设置棋盘时,行和列都要+2。(如果想玩9×9的棋盘,那么在最初设置棋盘时要设置成11×11)

//game.h
//设置棋盘的行和列
#define ROW 11
#define COL 11

//text.c
void game()
{
	//将布雷的信息存储到mine中
	char mine[ROW][COL] = { 0 };
	//将显示的信息存储到show中
	char show[ROW][COL] = { 0 };
}

(二)初始化

Initboard:利用for循环遍历二维数组的每一个元素,将棋盘初始化。

初始化mine棋盘:布雷的棋盘初始化全为’0‘。(这里的0为字符0)

初始化show棋盘:显示的棋盘初始化全为’*‘。

//game.h
//初始化
void Initboard(char board[ROW][COL], int row, int col, char set);
//game.c
//初始化棋盘
void Initboard(char board[ROW][COL], int row,int col,char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}
//text.c
void game()
{
	//将布雷的信息存储到mine中
	char mine[ROW][COL] = { 0 };
	//将显示的信息存储到show中
	char show[ROW][COL] = { 0 };
	//布雷的棋盘初始化全为0
	Initboard(mine, ROW, COL, '0');
	//显示的棋盘初始化全为*
	Initboard(show, ROW, COL, '*');
}

定义完棋盘后,进入调试模式,通过监视窗口来监视我们的两副棋盘是否正确。

2.布雷

雷的数量也是用define定义,方便之后的修改。

Setting:通过rand随机生成雷的坐标,每次生成坐标时需要判断该坐标是否合法。并且注意,坐标的范围是棋盘游戏的范围,而不是最初设定时的范围。

(若要使用rand函数,需要在主函数中对rand函数进行修饰:srand((unsigned int)time(NULL));,否则每次生成的随机值将一样)

//game.h
//布雷和打印的行、列
#define ROWS ROW-2
#define COLS COL-2
//布雷的数量
#define Easy 10

//game.c
//布雷
void Setting(char board[ROW][COL], int rows, int cols)
{
	int count = Easy;
	while (count)
	{
		int x = rand() % rows + 1;
		int y = rand() % rows + 1;
		if (board[x][y] != 1)
		{
			board[x][y] = '1';
			count--;
		}
	}
}
//text.c
void game()
{
	//将布雷的信息存储到mine中
	char mine[ROW][COL] = { 0 };
	//将显示的信息存储到show中
	char show[ROW][COL] = { 0 };
	//布雷的棋盘初始化全为0
	Initboard(mine, ROW, COL, '0');
	//显示的棋盘初始化全为*
	Initboard(show, ROW, COL, '*');
	//布雷
	Setting(mine, ROWS, COLS);
}
//main函数
srand((unsigned int)time(NULL));

布置完雷后,也是进入调试模式来检查代码运行是否正确。

3.打印棋盘

Dispaly:只需要打印show棋盘即可,定义这里打印棋盘与初始化的方法一致,都是用for循环遍历每个元素。唯一不同的地方在于为了方便输入坐标,这里将行号和列号在第一行和第一列打印。(打印完行号后需要换行,打印列号则不用)

//game.h
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols);
//text.c
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= rows; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= rows; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= cols; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

4.Cue提示雷的信息

在扫雷游戏中,如果该坐标不是雷,并且周围一圈有雷时,会提示周围雷的个数。

 Cue:将玩家输入的x、y参数传给Cue函数,用for循环遍历周围一圈的坐标,用临时变量count来记录周围雷的个数。将布设的雷的坐标设定为’1‘。

//提示
int Cue(char mine[ROW][COL], int x, int y)
{
	int i = 0;;
	int j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
			{
				count++;
			}
		}
	}
	return count;
}

5.Unfold展开功能

在扫雷游戏中,如果该坐标周边有雷,则显示周围雷的信息;如果该坐标周围没有雷,则一次展开多项,直到展开到雷的附近。

这里主要利用Unfold函数的递归,配合定义的Cue函数来实现。

递归需要满足以下两点:

1、设定递归结束条件。

  • 坐标超限
  • 该坐标是雷
  • 该坐标已被查找
  • 该坐标已标记雷的信息

2、已查找过的坐标不可重复查找,否则将会陷入死递归。

  • mine[x][y] = '2'
  • 将查找过的坐标标记为非0,非1的数字即可

此外,还需要在函数外部定义count,count为棋子的总数-布雷的数量,用来评判游戏结束。

外部传count参数时需要进行取地址,内部函数递归时,count已经是地址了,不要再取地址了。

//多个展开
void Unfold(char mine[ROW][COL], char show[ROW][COL], int x, int y,int* count)
{
	int i = 0;
	int j = 0;
	//终结标志:
	//1、坐标超限
	//2、该坐标是雷
	//3、该坐标已被查找
	if (1 <= x && x <= 9 && 1 <= y && y <= 9 && mine[x][y] == '0' && show[x][y] == '*')
	{
		mine[x][y] = '2';//查找过的坐标在mine里进行标记

		if (Cue(mine, x, y))//周围有雷提示雷的数量
		{
			show[x][y] = '0' + Cue(mine, x, y);//将整数转换成字符
			(*count)--;
		}
		else
		{
			show[x][y] = ' ';//没有雷就显示空格
			(*count)--;
			for (i = -1; i <= 1; i++)//针对(x,y)周围八个格子进行查找
			{
				for (j = -1; j <= 1; j++)
				{
				  Unfold(mine, show, x + i, y + j,count);//进行递归,这里的count已经是地址了,不要再取地址了
				}
			}
		}
	}
}

6.Guess排查

在玩家输入坐标后,需要对该坐标进行判断:

  • 坐标非法,重新输入
  • 坐标合法,进入Unfold函数,检查是否展开,并提示雷的信息
  • 坐标是炸弹,游戏结束

这里循环的依据就是count,当count为0时,跳出循环,宣布游戏结果。

//game.h
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols);

//game.c
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
	int x = 0;
	int y = 0;
	//记录排查次数
	int count = rows * cols - Easy;
	while (count)
	{
		scanf("%d %d", &x, &y);
		//判断坐标是否正确
		//1、坐标在范围内
		//2、坐标没被使用
		if (1 <= x && x <= 9 && 1 <= y && y <= 9 && show[x][y] == '*')
		{
			if (mine[x][y] != '1')
			{
				printf("Continue......\n");//不是雷就继续
				show[x][y] = ' ';
				Display(show, ROWS, COLS);
				count--;
			}
			else
			{
				printf("很遗憾,再接再厉\n");
				DisplayEnd(mine,show, ROWS, COLS);//将布雷的情况打印出来。
				break;
			}	
		}
		else
			printf("坐标非法,请重新输入\n");
	}
	if (count == 0)
	{
		printf("恭喜你\n");
		Display(show, ROWS, COLS);
	}
}

在游戏结束后,还需要将游戏的结果及布雷的情况打印出来,这里不单单只是打印mine棋盘,需要将mine和show棋盘结合一下。

//打印结局
void DisplayEnd(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
	{
		int i = 0;
		int j = 0;
		for (i = 0; i <= rows; i++)
		{
			printf("%d ", i);
		}
		printf("\n");
		for (i = 1; i <= rows; i++)
		{
			printf("%d ", i);
			for (j = 1; j <= cols; j++)
			{
				if (mine[i][j] == '1')//如果是雷就打印出来
				{
					printf("%c ",mine[i][j]);//与打印棋盘唯一不同之处
				}
				else
				{
					printf("%c ", show[i][j]);
				}
			}
			printf("\n");
		}
		printf("\n");
	}
}

试玩

看来运气并不好,没有展开一片区域,换个坐标试试。

尝试输入错误的坐标,检查系统是否能检测出来。

 (4,5)明显是个雷,让我们看一下结果是否如我们预料到那样。

通过更改define定义的常量,来更改游戏的难度。当然也可以把扫雷做成界面的形式,具体的UI设计也非常简单,这里只讲基础的算法知识,UI的设计可以自行学习。

整体代码

整体代码如下,当然还有很多值得优化的地方,大家有任何想法可以互相交流。

(一)game.h

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//设置棋盘的行、列
#define ROW 11
#define COL 11
//布雷和打印的行、列
#define ROWS ROW-2
#define COLS COL-2
//布雷的数量
#define Easy 10

//初始化
void Initboard(char board[ROW][COL], int row, int col, char set);
//布雷
void Setting(char board[ROW][COL], int rows, int cols);
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols);
//打印结局
void DisplayEnd(char mine[ROW][COL], char show[ROW][COL], int rows, int cols);
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols);

(二)game.c

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}
//布雷
void Setting(char board[ROW][COL], int rows, int cols)
{
	int count = Easy;
	while (count)
	{
		int x = rand() % rows + 1;
		int y = rand() % rows + 1;
		if (board[x][y] != 1)
		{
			board[x][y] = '1';
			count--;
		}
	}
}
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <= rows; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= rows; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= cols; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
//打印结局
void DisplayEnd(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
	{
		int i = 0;
		int j = 0;
		for (i = 0; i <= rows; i++)
		{
			printf("%d ", i);
		}
		printf("\n");
		for (i = 1; i <= rows; i++)
		{
			printf("%d ", i);
			for (j = 1; j <= cols; j++)
			{
				if (mine[i][j] == '1')
				{
					printf("@ ");
				}
				else
				{
					printf("%c ", show[i][j]);
				}
			}
			printf("\n");
		}
		printf("\n");
	}
}
//提示周围雷的信息
int Cue(char mine[ROW][COL], int x, int y)
{
	int i = 0;;
	int j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
			{
				count++;
			}
		}
	}
	return count;
}
//多个展开
void Unfold(char mine[ROW][COL], char show[ROW][COL], int x, int y,int* count)
{
	int i = 0;
	int j = 0;
	//终结标志:
	//1、坐标超限
	//2、该坐标是雷
	//3、该坐标已被查找
	if (1 <= x && x <= 9 && 1 <= y && y <= 9 && mine[x][y] == '0' && show[x][y] == '*')
	{
		mine[x][y] = '2';//查找过的坐标在mine里进行标记

		if (Cue(mine, x, y))//周围有雷提示雷的数量
		{
			show[x][y] = '0' + Cue(mine, x, y);//将整数转换成字符
			(*count)--;
		}
		else
		{
			show[x][y] = ' ';//没有雷就显示空格
			(*count)--;
			for (i = -1; i <= 1; i++)//针对(x,y)周围八个格子进行查找
			{
				for (j = -1; j <= 1; j++)
				{
				  Unfold(mine, show, x + i, y + j,count);//进行递归,这里的count已经是地址了,不要再取地址了
				}
			}
		}
	}
}
	//排查
	void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
	{
		int x = 0;
		int y = 0;
		//记录排查次数
		int count = rows * cols - Easy;
		while (count)
		{
			printf("请输入坐标:>\n");
			scanf("%d %d", &x, &y);
			//判断坐标是否正确
			//1、坐标在范围内
			//2、坐标没被使用
			if (1 <= x && x <= 9 && 1 <= y && y <= 9 && show[x][y] == '*')
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾,再接再厉\n");
					DisplayEnd(mine, show, ROWS, COLS);//将布雷的情况打印出来。
					break;
				}
				else
				{
					//不是雷就继续
					Unfold(mine, show, x, y,&count);
					Display(show, ROWS, COLS);
					printf("Continue......\n");
				}
			}
			else
				printf("坐标非法,请重新输入\n");
		}
		if (count == 0)
		{
			printf("恭喜你\n");
			Display(show, ROWS, COLS);
		}
	}

(三)text.c

#define _CRT_SECURE_NO_WARNINGS

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

void game()
{
	//将布雷的信息存储到mine中
	char mine[ROW][COL] = { 0 };
	//将显示的信息存储到show中
	char show[ROW][COL] = { 0 };
	//布雷的棋盘初始化全为0
	Initboard(mine, ROW, COL, '0');
	//显示的棋盘初始化全为*
	Initboard(show, ROW, COL, '*');
	//布雷
	Setting(mine, ROWS, COLS);
	//打印棋盘
	Display(show, ROWS, COLS);
	//排查
	Guess(mine, show, ROWS, COLS);
}



int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择(1/0):>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

原网站

版权声明
本文为[是王久久阿]所创,转载请带上原文链接,感谢
https://blog.csdn.net/why1472587/article/details/125007595