C语言实现扫雷小游戏

扫雷游戏是一款非常经典的小游戏,也能在一定程度上锻炼我们的思维,那么如何利用C语言实现一个扫雷游戏呢,这次我们就用最基本的一些C语言语法来具体的实现。

目录

游戏玩法

主函数的设计

game()函数的设计

棋盘设计

生成棋盘

放置雷

判断游戏状态

 如何判断输赢

输出周围雷的个数

代码优化

游戏试玩

源码展示

头文件

创建的函数

主函数


游戏玩法

要设计这样一个游戏,首先要了解它的玩法:在一个N *M的格子里一共随机布置了n个雷,当我们把所有的不是雷的地方全部找出来时,就算游戏过关;如果不幸在途中点到了地雷,那么游戏失败。每点到一个不是雷的位置,都会显示一个数字来表示其周围一圈共有几个雷。

主函数的设计

 首先创建主函数,要包含菜单,进入游戏、选择难度等功能,具体实现与上期大同小异:

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择要进行的操作:>(1/0)");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			puts("玩游戏");
			game();
			break;
		case 0:
			puts("退出游戏");
			break;
		default:
			puts("操作错误,即将返回菜单");
			Sleep(1000);//系统暂停1秒
            //这里的Sleep函数需要引入头文件才可使用
		}
	} while (input);

	return 0;
}

game()函数的设计

在开始之前,首先定义一些后续要用到的宏:

#define ROW 9  //玩家看到的棋盘行数
#define COL 9  //玩家看到的棋盘列数
#define ROWS ROW + 2//实际棋盘行数
#define COLS COL + 2//实际棋盘列数
#define COUNTS 10//设置雷的个数

棋盘设计

当我们把菜单等外观功能处理好以后,就可以进入正题,设计game()函数,首先我们可以看一个现成的扫雷游戏:

C语言实现扫雷小游戏_第1张图片

 显然我们首先需要一个棋盘,可以通过遍历一个二维数组来创建,我们可以设计字符0为安全,字符1为雷。这样设计的好处是,后续计算周围有几个雷的时候,直接把这些1加起来就可以了,代码上也会更简便一些。不过这样设计的话可能会产生一个问题,如果该区域周围有一个雷,那么该区域就会显示为1,这个1显然不是雷,不过计算机却会把它当作是一个雷,因此在设计上我们可以直接设计两个棋盘,一个用来存放地雷,另一个用以向用户展示并游戏。也就是一个为原始棋盘init_board(用来存放地雷),另一个为player_board(用来显示)。

生成棋盘

首先生成一个9 * 9棋盘:(根据传入的set值的不同输出的初始化棋盘也不同)

void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
    //这里的set为传入的初始化符号,想初始化成什么就传什么符号就好了
    //这样函数的复用性就更强
	int i = 0;
	int j = 0;
	for (i = 0; i< rows; i++)
	{ 
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//初始化棋盘
		}
	}
}

初始化后我们可以打印出棋盘来显示结果:

void print_board(char board[ROWS][COLS], int row, int col)
{
	system("cls");//每次打印前清理屏幕,更加美观
	int i = 0;
	int j = 0;
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

结果如下图所示:

C语言实现扫雷小游戏_第2张图片

 用于存放雷的棋盘也可以通过该函数打印。

 这里数组行和列均设计为比实际行和列大2,这样设计的原因主要是避免后续排雷操作时数组越界。比如在第0行第0列计算周围雷的个数时就会造成数组越界问题。

放置雷

接着需要在棋盘中放置雷,假设放置10个雷,需要用到随机数法(随机数法在上期有讲到,感兴趣的可以去了解一下),用到的头文件包括,注意在放置雷的时候就不用考虑数组越界的问题,因此直接传入ROW COL作为参数即可:

void set_board(char board[ROWS][COLS], int row, int col, int count)
{
	int i = 0;
	while (i < count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//得到的x,y值均为1-9之间的数
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			i++;
		}//保证不重复
	}
}

此时在打印棋盘可以得到:

C语言实现扫雷小游戏_第3张图片

 当然这个棋盘是不能被看到的,我们所看到的棋盘需要在创建一个新的数组来显示,也就是所谓的表棋盘。

设计好棋盘并且布置好雷以后,就可以设计游戏的具体玩法了。

判断游戏状态

首先需要我们输入一个合法坐标,再进行后续的判断:

void play_board(char player_board[ROWS][COLS], char hidden_board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = ROW * COL - COUNTS;//判断何时停下
	while (count)
	{
		printf("请输入一个有效坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (player_board[x][y] == '*')//判断该位置是否已经被占用
			{
				if (hidden_board[x][y] == '1')
				{
					puts("你被炸死了");//判断该位置是否安全
					print_board(hidden_board, ROWS, COLS);//展示原始数组
					break;
				}
				else
				{
					int ret = find_board(hidden_board, x, y);//得到周围雷的个数
					player_board[x][y] = ret + '0';
					print_board(player_board, ROWS, COLS);
					count--;
				}
			}
			else
				puts("该坐标已被占用,请重新输入");
		}
		else
			puts("坐标非法,请重新输入");
	}
}

 此时如果输入的坐标是安全的,进入find_board()函数计算其周围雷的元素。

 如何判断输赢

其实play_board()函数里面的count判断就可以了,每输入一一次坐标,只要是合法的并且安全的,count--,count的总数为棋盘横×列-雷的个数,当count为0是判断获胜。

输出周围雷的个数

由于我们设置的雷均为字符1,所以把周围8个字符加起来再减去8 * '0'就可以得到周围雷的个数,因此函数可以这样实现:

char find_board(char board[ROWS][COLS], int x, int y)
{
	return board[x - 1][y - 1] +
		board[x - 1][y] +
		board[x - 1][y + 1] +
		board[x][y - 1] +
		board[x][y + 1] +
		board[x + 1][y - 1] +
		board[x + 1][y] +
		board[x + 1][y + 1] - 8 * '0' ;
	//由于字符10-字符0 = 数字10
	//得到的结果即为该坐标周围的雷的个数
}

此时我们的扫雷小游戏就完成了,其中game()函数如下:

void game()
{
	char hidden_board[ROWS][COLS] = { 0 };
	char player_board[ROWS][COLS] = { 0 };
	//创建棋盘
	//存放地雷的棋盘
	init_board(hidden_board, ROWS, COLS, '0');
	//布置雷
	set_board(hidden_board, ROW, COL, COUNTS);
	//打印棋盘
	/*print_board(hidden_board, ROW, COL);
    //这一步是为了后续寻找BUG时更加方便,实际上这一行代码是不需要的*/

	//用户游戏的棋盘
	init_board(player_board, ROWS, COLS, '*');
	print_board(player_board, ROW, COL);
	//进入游戏
	play_board(player_board, hidden_board, ROW, COL);

}

不过这样和我们所认识的扫雷还不太一样,比如说所排查的位置如果周围8个均没有雷,应该展开一片,而我们的代码并不能满足这样的要求,因此可以进行优化。

代码优化

当所排查坐标周围有雷的时候,显然是不用更改代码的,当该位置坐标周围雷的个数为0时,就需要单独写一个函数来展开所有的0。代码如下:

void open_board(char hidden_board[ROWS][COLS], char player_board[ROWS][COLS], int row, int col, int x, int y)
{
	int ret = find_board(hidden_board, x, y);
	if (ret == 0)
	{
		player_board[x][y] = ' ';
		if (x - 1 > 0 && y > 0 && player_board[x - 1][y] == '*')
			open_board(hidden_board, player_board, row, col, x - 1, y);
		if (x > 0 && y - 1 > 0 && player_board[x][y - 1] == '*')
			open_board(hidden_board, player_board, row, col, x, y - 1);
		if (x > 0 && y + 1 > 0 && player_board[x][y + 1] == '*')
			open_board(hidden_board, player_board, row, col, x, y + 1);
		if (x + 1 > 0 && y > 0 && player_board[x + 1][y] == '*')
			open_board(hidden_board, player_board, row, col, x + 1, y);
        //判断所选位置的上下左右是否可选,可选则在进入递归判断
	}
	else
		player_board[x][y] = ret + '0';
}

如果添加这样一个函数的话,我们的count值就很难有正确的值,这时几乎不可能获胜(可选的坐标全选完了依然未获胜),那么我们的play_board()函数就需要这么修改一下:

void play_board(char player_board[ROWS][COLS], char hidden_board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = ROW * COL - COUNTS;//判断何时停下
	while (1)
	{
		printf("请输入一个有效坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (player_board[x][y] == '*')//判断该位置是否已经被占用
			{
				if (hidden_board[x][y] == '1')
				{
					print_board(hidden_board, ROW, COL);//展示原始数组
					puts("你被炸死了");
					Sleep(3000);
					break;
				}
				else
				{
					int ret = find_board(hidden_board, x, y);//得到周围雷的个数
					player_board[x][y] = ret + '0';
					open_board(hidden_board, player_board, row, col, x, y);
					print_board(player_board, ROW, COL);
                   int final_ret = is_win(player_board, row, col);
					if (final_ret == COUNTS)
					{
						puts("恭喜你,获胜了");
						break;
					}
				}
			}
			else
				puts("该坐标已被占用,请重新输入");
		}
		else
			puts("坐标非法,请重新输入");
	}
}

同时需要再写入一个函数来判断游戏是否结束:原理是查找player_board数组里面'*'存在的个数

int is_win(char player_board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (player_board[i][j] == '*')
				count++;
		}
	}
	return count;//返回的值为剩余'*'的个数
	//一共有几个雷就就应该有几个'*'剩下时判胜
}

游戏试玩

C语言实现扫雷小游戏_第4张图片

 

C语言实现扫雷小游戏_第5张图片

C语言实现扫雷小游戏_第6张图片

 

C语言实现扫雷小游戏_第7张图片

 

 

源码展示

头文件

#include
#include
#include
#include
#define ROW 9  //玩家看到的棋盘行数
#define COL 9  //玩家看到的棋盘列数
#define ROWS ROW + 2//实际棋盘行数
#define COLS COL + 2//实际棋盘列数
#define COUNTS 10//设置雷的个数
void init_board(char board[ROWS][COLS], int rows, int cols, char set);
void set_board(char board[ROWS][COLS], int row, int col, int count);
void print_board(char board[ROWS][COLS], int rows, int cols);
void play_board(char player_board[ROWS][COLS], char hidden_board[ROWS][COLS], int row, int col);

创建的函数

#include"check_game.h"
void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i< rows; i++)
	{ 
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//初始化棋盘
		}
	}
}
void print_board(char board[ROWS][COLS], int row, int col)
{
    system("cls");//每次打印前清理屏幕,更加美观
	int i = 0;
	int j = 0;
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void set_board(char board[ROWS][COLS], int row, int col, int count)
{
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//得到的x,y值均为1-9之间的数
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}//保证不重复
	}
}
int find_board(char board[ROWS][COLS], int x, int y)
{
	return board[x - 1][y - 1] +
		board[x - 1][y] +
		board[x - 1][y + 1] +
		board[x][y - 1] +
		board[x][y + 1] +
		board[x + 1][y - 1] +
		board[x + 1][y] +
		board[x + 1][y + 1] - (8 * '0') ;
	//由于字符10-字符0 = 数字10
	//得到的结果即为该坐标周围的雷的个数
}
void open_board(char hidden_board[ROWS][COLS], char player_board[ROWS][COLS], int row, int col, int x, int y)
{
	int ret = find_board(hidden_board, x, y);
	if (ret == 0)
	{
		player_board[x][y] = ' ';
		if (x - 1 > 0 && y > 0 && x - 1 <= 9 && y <= 9 && player_board[x - 1][y] == '*')
			open_board(hidden_board, player_board, row, col, x - 1, y);
		if (x > 0 && y - 1 > 0 && x <= 9 && y <= 9 && player_board[x][y - 1] == '*')
			open_board(hidden_board, player_board, row, col, x, y - 1);
		if (x > 0 && y + 1 > 0 && x <= 9 && y + 1<= 9 && player_board[x][y + 1] == '*')
			open_board(hidden_board, player_board, row, col, x, y + 1);
		if (x + 1 > 0 && y > 0 && x + 1 <= 9 && y <= 9 && player_board[x + 1][y] == '*')
			open_board(hidden_board, player_board, row, col, x + 1, y);
	}
	else
		player_board[x][y] = ret + '0';
}

int is_win(char player_board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (player_board[i][j] == '*')
				count++;
		}
	}
	return count;//返回的值为剩余'*'的个数
	//一共有几个雷就就应该有几个'*'剩下时判胜
}
void play_board(char player_board[ROWS][COLS], char hidden_board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入一个有效坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (player_board[x][y] == '*')//判断该位置是否已经被占用
			{
				if (hidden_board[x][y] == '1')
				{
					print_board(hidden_board, ROW, COL);//展示原始数组
					puts("你被炸死了");
					Sleep(3000);
					break;
				}
				else
				{
					int ret = find_board(hidden_board, x, y);//得到周围雷的个数
					player_board[x][y] = ret + '0';
					open_board(hidden_board, player_board, row, col, x, y);
					print_board(player_board, ROW, COL);
					int final_ret = is_win(player_board, row, col);
					if (final_ret == COUNTS)
					{
						puts("恭喜你,获胜了");
						break;
					}
				}
			}
			else
				puts("该坐标已被占用,请重新输入");
		}
		else
			puts("坐标非法,请重新输入");
	}
}

主函数

#include"check_game.h"
void menu()
{
	puts("***********************************");
	puts("**************1.play***************");
	puts("**************0.exit***************");
	puts("***********************************");
}
void game()
{
	char hidden_board[ROWS][COLS] = { 0 };
	char player_board[ROWS][COLS] = { 0 };
	//创建棋盘
	//存放地雷的棋盘
	init_board(hidden_board, ROWS, COLS, '0');
	//布置雷
	set_board(hidden_board, ROW, COL, COUNTS);
	//打印棋盘
	/*print_board(hidden_board, ROW, COL);
	//这一步是为了后续寻找BUG时更加方便,实际上这一行代码是不需要的*/
	//用户游戏的棋盘
	init_board(player_board, ROWS, COLS, '*');
	print_board(player_board, ROW, COL);
	//进入游戏
	play_board(player_board, hidden_board, ROW, COL);

}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择要进行的操作:>(1/0)");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			puts("玩游戏");
			game();
			break;
		case 0:
			puts("退出游戏");
			break;
		default:
			puts("操作错误,即将返回菜单");
			Sleep(1000);//系统暂停1秒
		}
	} while (input);

	return 0;
}

你可能感兴趣的:(c语言,开发语言)