当前位置:网站首页>用C写小游戏(扫雷)

用C写小游戏(扫雷)

2022-08-06 08:11:46深夜吞食

前言

说到扫雷大家都不陌生吧,特别是windowsXP开始的菜单中找到的小游戏中最吸引我们的便是扫雷了。从WindowsXP到win7微软都一直保留着这个游戏,然而在win10后我们再次熟悉的方式打开时却发现过往的回忆已经封存。1992年成立的"国际禁止地雷运动”的组织,他们曾见证过这些地雷带来的伤害,于是要求删除Windows操作系统中的《扫雷》游戏,原因就是《扫雷》伤害了冒着生命危险排雷的勇士以及众多地雷受害者。因此这个小游戏便不能在window中存在了。 不能玩不代表我们不能实现一个属于自己的扫雷游戏,就当是实现孩时回忆吧!
这次实现的扫雷游戏用到的知识点不会太多:函数,二维数组即可。

实现的基本要素

菜单

看过上一篇的三子棋,我们这次的菜单也是差不多这样实现。给予玩家选择:玩或是退出

void Meun()
{
    
	printf("|----------------------------------------|\n");
	printf("| 1.开始游戏 0.退出游戏 |\n");
	printf("|----------------------------------------|\n");
}

main函数的主体逻辑

int main()
{
    
	int input = 0;
	do
	{
    
		menu();//菜单
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
    
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			system("cls");
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	return 0;
}

雷区设置与打印

1. 雷区

在实现之前我们先了解了扫雷的游戏规则。
在这里插入图片描述

我们点开的第一步就是红色方框内小红圈的位置,那么周围的1代表的是在这个9宫格内必定有一个雷,那么就好理解了,左下角就是雷。
我们游戏的有效区范围是如下图展示9 * 9
在这里插入图片描述
那我们不妨想到二维数组设置成9 * 9就可以了。这样想就错了,为什么呢?前面我们说到我们雷的提示区范围得是9宫格,但是我们的雷要是刚好就在边缘,99的数组是不是就不能够解决这个问题了。所以我们在设置数组的时候得多加两行两列,上下各加一行,左右各加一列,这样我们就得设置1111的二维数组了。

//雷区有效区我们用ROW和COL表示,我们改变ROW和COL就可以随意改变雷区大小。 
#define ROW 9
#define COL 9
//二维数组大小我们用ROWS和COLS来表示下标。
#define ROWS ROW + 2
#define COLS COL + 2

#define MINE 10 //雷的数目

在这里我们又得思考一个问题,雷的设置和雷区打印全部都放在同一个数组里面,这样雷和提示还有显示区全部打印出来全部紊乱了。为了解决这个问题,我们创建两个二维数组,一个用来存储雷,一个用来展示给玩家看。
游戏主体我们封装在game函数里面

void game()
{
    
	//定义用于存放雷和显示雷的数组
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
}

数组创建好了后我们进行初始化,在放雷的数组里面我们全部初始化为0。在展示的数组里面我们全部初始化;

void InitBoard(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;   //set表示要初识化的字符
		}
	}
}

void game()
{
    
	//定义用于存放雷和显示雷的数组
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
}

2.布置雷

大体布局准备好后我们开始布置雷,雷的生成当然不能是固定的那玩起来得多无聊。所以我们依照以往一样生成随机数来布置雷的位置。生成的随机数可能会覆盖原来的雷,所以我们还要考虑防止重复一个位置生成雷。

void SetMine(char board[ROWS][COLS], int row, int col)
{
    
	int count = MINE; //宏定义雷,好让我们随意改变
	while (count)
	{
    
		int x = rand() % row + 1;      //随机生成雷的坐标
		int y = rand() % col + 1;
		if (board[x][y] == '0')        //检查该位置是否已经有雷
		{
    
			board[x][y] = '1';
			count--;                  //每生成一个雷就减一次
		}
	}
}

3. 打印雷区

雷布置好了没有我们不知道,所以我们设置一个函数来打印出来,即可打印放雷的数组,也可以打印展示部分。

void PrintBorad(char board[ROWS][COLS], int row, int col)
{
    
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
    
		for (j = 1; j <= col; j++)
		{
    
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
	SetMine(mine, ROW, COL);
	PrintfBoard(mine, ROW, COL);
	PrintfBoard(show, ROW, COL);

在这里插入图片描述

在这里我们打印的雷位置和展示部分,1代表都是雷,0表示安全区。但是这样打印出来的难免会不知道哪行哪列,到时我们输入坐标找都会找到眼花,所以我们将打印函数改进美观显示行和列
在这里插入图片描述

void PrintBoard(char board[ROWS][COLS], int row, int col)
{
    
	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("|");
		printf("\n");
	}
	for (i = 0; i <= 2 * row+2; i++)
		printf("-");
	printf("\n");
}

雷范围标识与排雷

1. 雷范围标识

在这里插入图片描述

如图红色标识九宫格内数字1代表这个九宫格内只有一个雷,黑色标识的范围内数字2代表有两个雷。所以这些数字都是提示玩家雷的大体位置,那么问题来了怎么去提示位置呢?我们在九宫格内除去中间的发现一个雷我们就计数++即可,因此我们可以这样设计

int MineCount(char board[ROWS][COLS], int x, int y)
{
    
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
    
		for (j = y - 1; j <= y + 1; j++)
		{
    
			if (board[i][j] == '1')
			{
    
				count++;
			}
		}
	}
	return count;
}

2. 排雷

排雷的时候我们得考虑以下几个问题:

  1. 玩家坐标是否走过同样的位置
  2. 玩家是不是踩雷
  3. 玩家排雷坐标是不是超过雷区或者输入其他字符
  4. 排雷结束的条件

我们把玩家下的坐标值对比放雷的数组,然后打印出来的结果用展示数组来呈现给玩家观看。

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
	int x = 0;
	int y = 0; 
	int judge = 0; //判断是否排完雷
	int* sz = judge;
	while (judge < row * col - MINE) // 若是坐标还没有排完雷继续游戏
	{
    
		printf("请输入坐标->");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)   //判断坐标合法性
		{
    
			if (mine[x][y] == '1')
			{
    
				system("cls");
				printf("很遗憾,你被炸死了!\n");
				PrintBoard(mine, row, col);     
				break;
			}
			else
			{
    
				if (show[x][y] != '*')   //判断是否重复排查
				{
    
					printf("该坐标已被排查,请重新输入!\n");
					continue;  //直接进入下一次循环
				}
				else
				{
    
					ShowBlanks(mine, show, row, col, x, y, sz);  //展示一片除提示雷的空白
					system("cls");  
					PrintBoard(show, row, col);  
				}
			}
		}
		else
		{
    
			printf("输入错误,请重新输入!\n");
			system("pause"); 
			getchar(); 防止输入错误造成死循环
		}
	}
	if (judge == row * col - MINE_COUNT)
	{
    
		system("cls");
		printf("恭喜你,排雷成功!\n");
		PrintBoard(show, row, col);
		return;
	}
}

玩家要一个一个坐标的下难免会对这个游戏失去兴趣。这时我们就要实现一个函数来展示在有雷的附近才停下来,这样就可以防止一个一个的输入坐标9*9这么多的次数了。
在这里插入图片描述

递归展示空白时我们得考虑一下会不会造成死递归。

void ShowBlanks(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* sz)
{
    
	if (x >= 1 && x <= row && y >= 1 && y <= col)  //判断坐标是否为排查范围内
	{
    
		int num = MineCount(mine, x, y);   //获取坐标周围雷的个数
		if (num == 0)
		{
    
			(*sz)++;
			show[x][y] = ' ';   //如果该区域没有雷就设置成空格
			int i = 0;
			int j = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
    
				for (j = y - 1; j <= y + 1; j++)
				{
    
					if (show[i][j] == '*')    //防止已经排查过的坐标再次递归
						ShowBlanks(mine, show, row, col, i, j, sz);
				}
			}
		}
		else
		{
    
			(*sz)++;
			show[x][y] = num + '0';
		}
	}
}

代码展示

#include<stdio.h>
#include<windows.h>
#include<time.h>
#include<stdlib.h>
  
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define MINE 10 

void Meun()
{
    
	printf("|----------------------------------------|\n");
	printf("| 1.开始游戏 0.退出游戏 |\n");
	printf("|----------------------------------------|\n");
}

void InitBoard(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 SetMine(char board[ROWS][COLS], int row, int col)
{
    
	int count = MINE; 
	while (count)
	{
    
		int x = rand() % row + 1;     
		int y = rand() % col + 1;
		if (board[x][y] == '0')       
		{
    
			board[x][y] = '1';
			count--;                  
		}
	}
}

void PrintBoard(char board[ROWS][COLS], int row, int col)
{
    
	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("|");
		printf("\n");
	}
	for (i = 0; i <= 2 * row+2; i++)
		printf("-");
	printf("\n");
}

int MineCount(char board[ROWS][COLS], int x, int y)
{
    
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
    
		for (j = y - 1; j <= y + 1; j++)
		{
    
			if (board[i][j] == '1')
			{
    
				count++;
			}
		}
	}
	return count;
}

void ShowBlanks(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* sz)
{
    
	if (x >= 1 && x <= row && y >= 1 && y <= col)  
	{
    
		int num = MineCount(mine, x, y);   
		if (num == 0)
		{
    
			(*sz)++;
			show[x][y] = ' ';  
			int i = 0;
			int j = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
    
				for (j = y - 1; j <= y + 1; j++)
				{
    
					if (show[i][j] == '*')    
						ShowBlanks(mine, show, row, col, i, j, sz);
				}
			}
		}
		else
		{
    
			(*sz)++;
			show[x][y] = num + '0';
		}
	}
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
	int x = 0;
	int y = 0; 
	int judge = 0; 
	int* sz = &judge;
	while (judge < row * col - MINE) 
	{
    
		printf("请输入坐标->");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)   
		{
    
			if (mine[x][y] == '1')
			{
    
				system("cls");
				printf("很遗憾,你被炸死了!\n");
				PrintBoard(mine, row, col); 
				printf("1是雷0是安全区\n");    
				break;
			}
			else
			{
    
				if (show[x][y] != '*')   
				{
    
					printf("该坐标已被排查,请重新输入!\n");
					continue;  
				}
				else
				{
    
					ShowBlanks(mine, show, row, col, x, y, sz);  
					system("cls");  
					PrintBoard(show, row, col);  
				}
			}
		}
		else
		{
    
			printf("输入错误,请重新输入!\n");
			system("pause"); 
			getchar(); 
		}
	}
	if (judge == row * col - MINE)
	{
    
		system("cls");
		printf("恭喜你,排雷成功!\n");
		PrintBoard(show, row, col);
		return;
	}
}

void game()
{
    
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	SetMine(mine, ROW, COL);
	system("cls");   //清除菜单,美观整洁
	PrintBoard(show, ROW, COL);	
	FindMine(mine, show, ROW, COL);
}

int main()
{
    
	//设置随机数的种子
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
    
		Meun();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
    
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			system("cls");
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	return 0;
}

以上就是扫雷全部内容,感谢大家支持!!

在这里插入图片描述

原网站

版权声明
本文为[深夜吞食]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_57663528/article/details/126087140

随机推荐