扫雷实现详解【递归展开+首次必展开+标记雷+取消标记雷】

扫雷

  • 一.扫雷设计思路
  • 二.扫雷代码逐步实现
    • 1.创建游戏菜单
    • 2.初始化棋盘
    • 3.打印棋盘
    • 4.随机布置雷
    • 5.统计周围雷的个数
    • 6.递归展开棋盘
    • 7.标记雷
    • 8.删除雷的标记
    • 9.保证第一次排雷的安全性+棋盘必定展开
    • 10.排查雷
    • 11.判断输赢
  • 三.扫雷总代码
  • 四.截图

一.扫雷设计思路

 1.创建游戏菜单。
 2.初始化棋盘。
 3.打印棋盘。
 4.随机布置雷。
 5.选择:排查雷,标记雷,或取消标记雷。
 7.循环,直到游戏结束。
 8.当然其中的细节处还有很多,带着你一步一步实现。

二.扫雷代码逐步实现

1.创建游戏菜单

void Menu()
{
	printf("-------------------------------------\n");
	printf("|              扫雷游戏!           |\n");
	printf("|        0.exit        1.play       |\n");
	printf("-------------------------------------\n");
}

当然可以美化一下,发挥你天马行空的想象力!

2.初始化棋盘

  • 扫雷的过程中,布置的雷的信息排查出的周围雷的信息周围雷的个数)都需要存储,所以我们需要⼀定的数据结构来存储这些信息。不妨用9 x 9的棋盘上布置的雷的信息排查出的周围雷的信息(周围雷的个数),我们首先想到的就是创建⼀个9 x 9的数组来存放这些信息。1代表雷,0代表不是雷。

如图:
在这里插入图片描述
由于我们还要统计某个坐标周围雷的个数,所以要访问周围数组元素

  • 假设我们排查(2,5)这个坐标时,我们访问周围的⼀圈8个黄色位置,统计周围雷的个数是1。
  • 假设我们排查(8,6)这个坐标时,我们访问周围的⼀圈8个黄色位置,统计周围雷的个数时,最下面的三个坐标就会越界
  • 为了防止越界,我们在设计的时候,给数组扩大一圈,雷还是布置在中间的9 x 9的坐标上,周围一圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11 x 11是比较合适。

如图:
在这里插入图片描述

  • 再继续分析,我们在棋盘上布置了雷,棋盘上雷的信息(1)非雷的信息(0),假设我们排查了某⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录存储并打印出来,作为排雷的重要参考信息。那这个雷的个数信息存放在哪里呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产生混淆和打印上的困难。

  • 我们肯定有办法解决,比如:雷和非雷的信息不要使用数字,使用某些字符就行,这样就避免冲突了,但是这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便。

  • 我们采用另外⼀种方案,设置两个棋盘,我们专门给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不干扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。

  • 为了保持神秘,show数组开始时初始化为字符 * ,为了保持两个数组的类型⼀致,可以使用同⼀套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’。

如图:
在这里插入图片描述

给出伪代码参考:

#define ROW 9//扫雷的行数
#define COL 9//扫雷的列数
#define ROWS ROW + 2//创建二维数组的行数
#define COLS COL + 2//创建二维数组的列数
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;
		}
	}
}
//创建两个字符二维数组
char mine[ROWS][COLS];//存放该点是不是雷    (0代表不是雷,1代表是雷)
char show[ROWS][COLS];//存放该点周围有多少雷(数字代表周围有多少雷,*代表未知)

3.打印棋盘

代码1:

//定义打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	//辅助观察棋盘
	for (i = 0;i <= row;i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	//打印棋盘
	for (i = 1;i <= row;i++)
	{
		printf("%d ", i);//辅助观察棋盘
		int j = 0;
		for (j = 1;j <= col;j++)
		{
			printf("\033[36m%c \033[0m", board[i][j]);//打印蓝色字符
		}
		printf("\n");
	}
	printf("--------扫雷--------\n");//辅助观察棋盘
}

为了使棋盘看起来更舒服,我们可以美化一下棋盘。
代码2:

//定义打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
	printf("\033[34m-----------------扫雷-----------------\033[0m\n");//辅助观察棋盘
	int i = 0;
	//辅助观察棋盘
	printf("0 |");
	for (i = 1;i <= row;i++)
	{
		printf(" %-2d", i);
		if (i < row)
		{
			printf("|");
		}
	}
	printf("\n");
	printf("--|");
	for (i = 1; i <= col; i++)
	{
		printf("---");
		if (i < row)
		{
			printf("|");
		}
	}
	printf("\n");
	//打印棋盘
	for (i = 1;i <= row;i++)
	{
		printf("%-2d|", i);//辅助观察棋盘
		int j = 0;
		for (j = 1;j <= col;j++)
		{

			if (board[i][j] == '1' || board[i][j] == '5')
			{
				printf("\033[36m %c \033[0m", board[i][j]);//打印蓝色
			}
			else if (board[i][j] == '2' || board[i][j] == '6')
			{
				printf("\033[32m %c \033[0m", board[i][j]);//打印绿色
			}
			else if (board[i][j] == '3' || board[i][j] == '7')
			{
				printf("\033[31m %c \033[0m", board[i][j]);//打印红色
			}
			else if (board[i][j] == '4' || board[i][j] == '8')
			{
				printf("\033[35m %c \033[0m", board[i][j]);//打印紫色
			}
			else if (board[i][j] == '!')
			{
				printf("\033[31m %c \033[0m", board[i][j]);//打印红色
			}
			else
			{
				printf(" %c ", board[i][j]);//打印白色
			}
			if (j < col)
			{
				printf("|");
			}
		}
		//辅助观察棋盘
		printf("\n");
		if (i < row)
		{
			printf("--|");
		}
		for (j = 1; j <= col; j++)
		{
			if (i < row)
			{
				printf("---");
				if (j < col)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

效果如下:
在这里插入图片描述

4.随机布置雷

  • 这里用到了随机数,不知道的小伙伴,可以去我写的猜字小游戏看看
  • 点击即可前往猜字小游戏
//定义布置雷的函数
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = MINE_NUMBER;//雷的个数
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] != '1')
		{
			mine[x][y] = '1';//如果该坐标不是雷,则布置雷
			count--;
		}
	}
}

5.统计周围雷的个数

代码1:暴力取遍

//统计周围雷的个数的函数1.0
int AroundMineCount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y] + mine[x - 1][y - 1] +
		mine[x][y - 1] + mine[x + 1][y - 1] +
		mine[x + 1][y] + mine[x + 1][y + 1] +
		mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}

代码2:循环取遍

//统计周围雷的个数的函数
int AroundMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = x - 1;i <= x + 1;i++)
	{
		int j = 0;
		for (j = y - 1;j <= y + 1;j++)
		{
			count += mine[i][j] - '0';
		}
	}
	return count;
}

6.递归展开棋盘

  • 已知该坐标不是雷,若周围没有雷,该坐标赋为空,再向外扩展(达到展开一片的效果),否则只显示该坐标周围雷的个数。
//已知该坐标不是雷,若周围没有雷,该坐标赋为空,再向外扩展(达到展开一片的效果),否则只显示该坐标周围雷的个数
void ExpandAround(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//限制在棋盘内展开,防止下标越界
	{
		int count = AroundMineCount(mine, x, y);//获取雷数
		if (count == 0)//周围没雷,递归展开
		{
			show[x][y] = ' ';//周围没雷,赋值为空格
			int i = 0;
			for (i = x - 1;i <= x + 1;i++)
			{
				int j = 0;
				for (j = y - 1;j <= y + 1;j++)
				{
					if (show[i][j] == '*')//只对位排查的坐标进行展开,防止死循环
					{
						ExpandAround(mine, show, i, j);
					}
				}
			}
		}
		else
		{
			show[x][y] = count + '0';//周围有雷,显示雷数
		}
	}
}

7.标记雷

//定义标记雷的函数(用!标记)
void MarkMine(char show[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 && show[x][y] != '!')
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				system("cls");
				PrintBoard(show, ROW, COL);
				break;
			}
			else
			{
				printf("该坐标已经被排查过,无法标记,请重新输入\n");
			}
		}
		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '!')
		{
			printf("该坐标已经被标记过了,无法二次标记,请重新输入\n");
		}
		else
		{
			printf("该坐标越界,请重新输入\n");
		}
	}
}

8.删除雷的标记

//定义删除雷的标记的函数
void DelectMarkMine(char show[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 && show[x][y] == '!')
		{
			show[x][y] = '*';
			system("cls");
			PrintBoard(show, ROW, COL);
			break;
		}
		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '!')
		{
			printf("该坐标未被标记过,无法删除标记,请重新输入\n");
		}
		else
		{
			printf("该坐标越界,请重新输入\n");
		}
	}
}

9.保证第一次排雷的安全性+棋盘必定展开

//定义第一次排雷是安全的,不是雷并且可以达到展开一片的效果,增加了可玩性
void FirstIsSafe(char mine[ROWS][COLS], char show[ROWS][COLS],int x, int y)
{
	//若该点不是雷且周围无雷,直接展开
	if (mine[x][y] != '1' && AroundMineCount(mine, x, y) == 0)
	{
		ExpandAround(mine, show, x, y);
	}
	//否则分情况
	else
	{
		//先判断该点是不是雷
		if (mine[x][y] == '1')//该点为雷,将其移走
		{
			mine[x][y] = '0';
			while (1)
			{
				int x1 = rand() % ROW + 1;
				int y1 = rand() % COL + 1;
				if (mine[x1][y1] != '1' && x1 != x && y1 != y)
				{
					mine[x1][y1] = '1';
					break;
				}
			}
		}
		//以及将雷移走了,在判断周围是否有雷
		if (AroundMineCount(mine, x, y) != 0)//该点周围有雷,将它们移走
		{
			int count = AroundMineCount(mine, x, y);//计算周围雷的数量
			int i = 0;
			for (i = x - 1;i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					mine[i][j] = '0';//将周围的雷变为无雷的状态
				}
			}
			while (count)
			{
				int x1 = rand() % ROW + 1;
				int y1 = rand() % COL + 1;
				if ((x1 != x - 1 && y1 != y - 1)&&
					(x1 != x - 1 && y1 != y)&&
					(x1 != x - 1 && y1 != y + 1)&&
					(x1 != x && y1 != y - 1)&&
					(x1 != x && y1 != y)&&
					(x1 != x && y1 != y + 1)&&
					(x1 != x + 1 && y1 != y - 1)&&
					(x1 != x + 1 && y1 != y)&&
					(x1 != x + 1 && y1 != y + 1))//不能将雷移回来
				{
					if (mine[x1][y1] != '1')
					{
						mine[x1][y1] = '1';//如果该坐标不是雷,则布置雷
						count--;
					}
				}
			}
		}
		ExpandAround(mine, show, x, y);
	}
}

10.排查雷

  • 代码1:不能实现展开一片的效果
//定义排查雷的函数1.0:不能实现展开一片的效果
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int ret1 = 0;
	while (1)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')//判断输入的坐标是否合法且未被排查过
		{
			if (mine[x][y] == '1')//该坐标是雷
			{
				system("cls");//清空屏幕——头文件windows.h
				printf("很遗憾,你被雷炸死了,游戏失败!\n");
				printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
				PrintBoard(mine, ROW, COL);
				ret1 = 1;
				break;
			}
			else//该坐标不是雷
			{
				system("cls");//清空屏幕——头文件windows.h
				int count = AroundMineCount(mine, x, y);
				show[x][y] = count + '0';//该坐标要存放雷的个数
				int ret2 = IsWin(show, row, col);
				if (ret2 == 1)
				{
					printf("恭喜你!找到了所有的雷,游戏胜利!\n");
					printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
					PrintBoard(mine, row, col);
				}
				else
				{
					PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
				}
				break;
			}
		}
		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '*')//该坐标已经被排查过
		{
			printf("该坐标已经被排查过,请重新输入坐标\n");
		}
		else//越界
		{
			printf("输入的坐标错误,请重新输入\n");
		}
	}
	return ret1;
}
  • 代码2:可以实现展开一片的效果
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int ret1 = 0;
	while (1)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过
		{
			if (show[x][y] == '*' || show[x][y] == '!')
			{
				if (show[x][y] == '*')
				{
				again:
					if (mine[x][y] == '1')//该坐标是雷
					{
						system("cls");//清空屏幕——头文件windows.h
						printf("很遗憾,你被雷炸死了,游戏失败!\n");
						printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
						PrintBoard(mine, ROW, COL);
						ret1 = 1;
						break;
					}
					else//该坐标不是雷
					{
						ExpandAround(mine, show, x, y);//直接展开
						system("cls");//清空屏幕——头文件windows.h
						int ret2 = IsWin(show, row, col);
						if (ret2 == 1)
						{
							printf("恭喜你!找到了所有的雷,游戏胜利!\n");
							printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
							PrintBoard(mine, row, col);
						}
						else
						{
							PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
						}
						break;
					}
				}
				else
				{
					int select = 0;
					printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");
					while (1)
					{
						printf("请输入(0/1):>");
						scanf("%d", &select);
						if (select == 0)
						{
							return 0;
						}
						else if (select == 1)
						{
							goto again;
						}
						else
						{
							printf("输入错误,请重新输入\n");
						}
					}
				}
			}
			else
			{
				printf("该坐标已经被排查过,请重新输入\n");
			}
		}
		else//越界
		{
			printf("该坐标越界,请重新输入\n");
		}
	}
	return ret1;
}
  • 代码3:结合FirstIsSafe函数,第一次排查确保该点不是雷以及周围没有雷,达到网页版扫雷的效果,由于是随机排雷,不能保证该点以及周围有无雷,可以将雷,神不知鬼不觉地换掉。
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//第一次排查确保该点不是雷以及周围没有雷,达到网页版扫雷的效果
	//由于是随机排雷,不能保证该点以及周围有无雷,可以将雷,神不知鬼不觉地换掉
	static int FindMineCount = 1;
	if (FindMineCount == 1)
	{
		int x = 0;
		int y = 0;
		while (1)
		{
			printf("请输入你要排查的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{

				FirstIsSafe(mine, show, x, y);
				FindMineCount++;
				system("cls");
				//PrintBoard(mine, ROW, COL);
				PrintBoard(show, ROW, COL);
				return 0;
			}
			else
			{
				printf("输入的坐标越界,请重新输入\n");
			}
		}
	}
	//以后雷的位置就固定下来了
	else
	{
		int x = 0;
		int y = 0;
		int ret1 = 0;
		while (1)
		{
			printf("请输入你要排查的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过
			{
				if (show[x][y] == '*' || show[x][y] == '!')
				{
					if (show[x][y] == '*')
					{
					again:
						if (mine[x][y] == '1')//该坐标是雷
						{
							system("cls");//清空屏幕——头文件windows.h
							printf("很遗憾,你被雷炸死了,游戏失败!\n");
							printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
							PrintBoard(mine, ROW, COL);
							ret1 = 1;
							break;
						}
						else//该坐标不是雷
						{
							ExpandAround(mine, show, x, y);//直接展开
							system("cls");//清空屏幕——头文件windows.h
							int ret2 = IsWin(show, row, col);
							if (ret2 == 1)
							{
								printf("恭喜你!找到了所有的雷,游戏胜利!\n");
								printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
								PrintBoard(mine, row, col);
							}
							else
							{
								PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
							}
							break;
						}
					}
					else
					{
						int select = 0;
						printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");
						while (1)
						{
							printf("请输入(0/1):>");
							scanf("%d", &select);
							if (select == 0)
							{
								return 0;
							}
							else if (select == 1)
							{
								goto again;
							}
							else
							{
								printf("输入错误,请重新输入\n");
							}
						}
					}
				}
				else
				{
					printf("该坐标已经被排查过,请重新输入\n");
				}
			}
			else//越界
			{
				printf("该坐标越界,请重新输入\n");
			}
		}
		FindMineCount++;
		return ret1;
	}
}

11.判断输赢

  • 利用剩余的*数目等于雷的数目,返回1,代表游戏胜利,否则返回0,代表游戏继续。
//定义游戏胜利的函数
int IsWin(char show[ROWS][COLS], int row, int col)
{
	int count = 0;
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')//统计show中*的个数
			{
				count++;
			}
		}
	}
	if (count == MINE_NUMBER)//若剩余的*数目等于雷的数目,返回1,代表游戏胜利
	{
		return 1;
	}
	return 0;//否则返回0,代表游戏继续
}

三.扫雷总代码

  • game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>

#define ROW 9//扫雷的行数
#define COL 9//扫雷的列数

#define ROWS ROW + 2//创建二维数组的行数
#define COLS COL + 2//创建二维数组的列数

#define MINE_NUMBER 10//设置雷的个数

//声明初始化棋盘的函数
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

//声明打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col);

//声明布置雷的函数
void SetMine(char mine[ROWS][COLS], int row, int col);

//声明标记雷的函数
void MarkMine(char show[ROWS][COLS], int row, int col);

//声明删除雷的标记的函数
void DelectMarkMine(char show[ROWS][COLS], int row, int col);

//声明第一次排雷是安全的,不是雷并且可以达到展开一片的效果,增加了可玩性
void FirstIsSafe(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

//声明排查雷的函数
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//声明游戏胜利的函数
int IsWin(char show[ROWS][COLS], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//printf("\033[36m%c \033[0m", board[i][j]);//打印蓝色
//printf("\033[31m%c \033[0m", board[i][j]);//打印红色

//定义初始化棋盘的函数
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 PrintBoard(char board[ROWS][COLS], int row, int col)
//{
//	int i = 0;
//	//辅助观察棋盘
//	for (i = 0;i <= row;i++)
//	{
//		printf("%d ", i);
//	}
//	printf("\n");
//	//打印棋盘
//	for (i = 1;i <= row;i++)
//	{
//		printf("%d ", i);//辅助观察棋盘
//		int j = 0;
//		for (j = 1;j <= col;j++)
//		{
//			printf("\033[36m%c \033[0m", board[i][j]);//打印蓝色
//		}
//		printf("\n");
//	}
//	printf("--------扫雷--------\n");//辅助观察棋盘
//}

//定义打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
	printf("\033[34m-----------------扫雷-----------------\033[0m\n");//辅助观察棋盘
	int i = 0;
	//辅助观察棋盘
	printf("0 |");
	for (i = 1;i <= row;i++)
	{
		printf(" %-2d", i);
		if (i < row)
		{
			printf("|");
		}
	}
	printf("\n");
	printf("--|");
	for (i = 1; i <= col; i++)
	{
		printf("---");
		if (i < row)
		{
			printf("|");
		}
	}
	printf("\n");
	//打印棋盘
	for (i = 1;i <= row;i++)
	{
		printf("%-2d|", i);//辅助观察棋盘
		int j = 0;
		for (j = 1;j <= col;j++)
		{

			if (board[i][j] == '1' || board[i][j] == '5')
			{
				printf("\033[36m %c \033[0m", board[i][j]);//打印蓝色
			}
			else if (board[i][j] == '2' || board[i][j] == '6')
			{
				printf("\033[32m %c \033[0m", board[i][j]);//打印绿色
			}
			else if (board[i][j] == '3' || board[i][j] == '7')
			{
				printf("\033[31m %c \033[0m", board[i][j]);//打印红色
			}
			else if (board[i][j] == '4' || board[i][j] == '8')
			{
				printf("\033[35m %c \033[0m", board[i][j]);//打印紫色
			}
			else if (board[i][j] == '!')
			{
				printf("\033[31m %c \033[0m", board[i][j]);//打印红色
			}
			else
			{
				printf(" %c ", board[i][j]);//打印白色
			}
			if (j < col)
			{
				printf("|");
			}
		}
		//辅助观察棋盘
		printf("\n");
		if (i < row)
		{
			printf("--|");
		}
		for (j = 1; j <= col; j++)
		{
			if (i < row)
			{
				printf("---");
				if (j < col)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

//定义布置雷的函数
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = MINE_NUMBER;//雷的个数
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] != '1')
		{
			mine[x][y] = '1';//如果该坐标不是雷,则布置雷
			count--;
		}
	}
}

//统计周围雷的个数的函数1.0
//int AroundMineCount(char mine[ROWS][COLS], int x, int y)
//{
//	return mine[x - 1][y] + mine[x - 1][y - 1] +
//		mine[x][y - 1] + mine[x + 1][y - 1] +
//		mine[x + 1][y] + mine[x + 1][y + 1] +
//		mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
//}

//统计周围雷的个数的函数2.0
int AroundMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = x - 1;i <= x + 1;i++)
	{
		int j = 0;
		for (j = y - 1;j <= y + 1;j++)
		{
			count += mine[i][j] - '0';
		}
	}
	return count;
}

//已知该坐标不是雷,若周围没有雷,该坐标赋为空,再向外扩展(达到展开一片的效果),否则只显示该坐标周围雷的个数
void ExpandAround(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//限制在棋盘内展开,防止下标越界
	{
		int count = AroundMineCount(mine, x, y);//获取雷数
		if (count == 0)//周围没雷,递归展开
		{
			show[x][y] = ' ';//周围没雷,赋值为空格
			int i = 0;
			for (i = x - 1;i <= x + 1;i++)
			{
				int j = 0;
				for (j = y - 1;j <= y + 1;j++)
				{
					if (show[i][j] == '*')//只对位排查的坐标进行展开,防止死循环
					{
						ExpandAround(mine, show, i, j);
					}
				}
			}
		}
		else
		{
			show[x][y] = count + '0';//周围有雷,显示雷数
		}
	}
}

//定义标记雷的函数
void MarkMine(char show[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 && show[x][y] != '!')
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				system("cls");
				PrintBoard(show, ROW, COL);
				break;
			}
			else
			{
				printf("该坐标已经被排查过,无法标记,请重新输入\n");
			}
		}
		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '!')
		{
			printf("该坐标已经被标记过了,无法二次标记,请重新输入\n");
		}
		else
		{
			printf("该坐标越界,请重新输入\n");
		}
	}
}

//定义删除雷的标记的函数
void DelectMarkMine(char show[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 && show[x][y] == '!')
		{
			show[x][y] = '*';
			system("cls");
			PrintBoard(show, ROW, COL);
			break;
		}
		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '!')
		{
			printf("该坐标未被标记过,无法删除标记,请重新输入\n");
		}
		else
		{
			printf("该坐标越界,请重新输入\n");
		}
	}
}

//定义游戏胜利的函数
int IsWin(char show[ROWS][COLS], int row, int col)
{
	int count = 0;
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')//统计show中*的个数
			{
				count++;
			}
		}
	}
	if (count == MINE_NUMBER)//若剩余的*数目等于雷的数目,返回1,代表游戏胜利
	{
		return 1;
	}
	return 0;//否则返回0,代表游戏继续
}

//定义排查雷的函数1.0:不能实现展开一片的效果
//int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
//	int x = 0;
//	int y = 0;
//	int ret1 = 0;
//	while (1)
//	{
//		printf("请输入你要排查的坐标:>");
//		scanf("%d %d", &x, &y);
//		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')//判断输入的坐标是否合法且未被排查过
//		{
//			if (mine[x][y] == '1')//该坐标是雷
//			{
//				system("cls");//清空屏幕——头文件windows.h
//				printf("很遗憾,你被雷炸死了,游戏失败!\n");
//				printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//				PrintBoard(mine, ROW, COL);
//				ret1 = 1;
//				break;
//			}
//			else//该坐标不是雷
//			{
//				system("cls");//清空屏幕——头文件windows.h
//				int count = AroundMineCount(mine, x, y);
//				show[x][y] = count + '0';//该坐标要存放雷的个数
//				int ret2 = IsWin(show, row, col);
//				if (ret2 == 1)
//				{
//					printf("恭喜你!找到了所有的雷,游戏胜利!\n");
//					printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//					PrintBoard(mine, row, col);
//				}
//				else
//				{
//					PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
//				}
//				break;
//			}
//		}
//		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '*')//该坐标已经被排查过
//		{
//			printf("该坐标已经被排查过,请重新输入坐标\n");
//		}
//		else//越界
//		{
//			printf("输入的坐标错误,请重新输入\n");
//		}
//	}
//	return ret1;
//}

//定义排查雷的函数2.0:可以实现展开一片的效果
//int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
//	int x = 0;
//	int y = 0;
//	int ret1 = 0;
//	while (1)
//	{
//		printf("请输入你要排查的坐标:>");
//		scanf("%d %d", &x, &y);
//		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过
//		{
//			if (show[x][y] == '*' || show[x][y] == '!')
//			{
//				if (show[x][y] == '*')
//				{
//				again:
//					if (mine[x][y] == '1')//该坐标是雷
//					{
//						system("cls");//清空屏幕——头文件windows.h
//						printf("很遗憾,你被雷炸死了,游戏失败!\n");
//						printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//						PrintBoard(mine, ROW, COL);
//						ret1 = 1;
//						break;
//					}
//					else//该坐标不是雷
//					{
//						ExpandAround(mine, show, x, y);//直接展开
//						system("cls");//清空屏幕——头文件windows.h
//						int ret2 = IsWin(show, row, col);
//						if (ret2 == 1)
//						{
//							printf("恭喜你!找到了所有的雷,游戏胜利!\n");
//							printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//							PrintBoard(mine, row, col);
//						}
//						else
//						{
//							PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
//						}
//						break;
//					}
//				}
//				else
//				{
//					int select = 0;
//					printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");
//					while (1)
//					{
//						printf("请输入(0/1):>");
//						scanf("%d", &select);
//						if (select == 0)
//						{
//							return 0;
//						}
//						else if (select == 1)
//						{
//							goto again;
//						}
//						else
//						{
//							printf("输入错误,请重新输入\n");
//						}
//					}
//				}
//			}
//			else
//			{
//				printf("该坐标已经被排查过,请重新输入\n");
//			}
//		}
//		else//越界
//		{
//			printf("该坐标越界,请重新输入\n");
//		}
//	}
//	return ret1;
//}

//定义排查雷的函数3.0:可以实现展开一片的效果
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//第一次排查确保该点不是雷以及周围没有雷,达到网页版扫雷的效果
	//由于是随机排雷,不能保证该点以及周围有无雷,可以将雷,神不知鬼不觉地换掉
	static int FindMineCount = 1;
	if (FindMineCount == 1)
	{
		int x = 0;
		int y = 0;
		while (1)
		{
			printf("请输入你要排查的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{

				FirstIsSafe(mine, show, x, y);
				FindMineCount++;
				system("cls");
				//PrintBoard(mine, ROW, COL);
				PrintBoard(show, ROW, COL);
				return 0;
			}
			else
			{
				printf("输入的坐标越界,请重新输入\n");
			}
		}
	}
	//以后雷的位置就固定下来了
	else
	{
		int x = 0;
		int y = 0;
		int ret1 = 0;
		while (1)
		{
			printf("请输入你要排查的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过
			{
				if (show[x][y] == '*' || show[x][y] == '!')
				{
					if (show[x][y] == '*')
					{
					again:
						if (mine[x][y] == '1')//该坐标是雷
						{
							system("cls");//清空屏幕——头文件windows.h
							printf("很遗憾,你被雷炸死了,游戏失败!\n");
							printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
							PrintBoard(mine, ROW, COL);
							ret1 = 1;
							break;
						}
						else//该坐标不是雷
						{
							ExpandAround(mine, show, x, y);//直接展开
							system("cls");//清空屏幕——头文件windows.h
							int ret2 = IsWin(show, row, col);
							if (ret2 == 1)
							{
								printf("恭喜你!找到了所有的雷,游戏胜利!\n");
								printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
								PrintBoard(mine, row, col);
							}
							else
							{
								PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
							}
							break;
						}
					}
					else
					{
						int select = 0;
						printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");
						while (1)
						{
							printf("请输入(0/1):>");
							scanf("%d", &select);
							if (select == 0)
							{
								return 0;
							}
							else if (select == 1)
							{
								goto again;
							}
							else
							{
								printf("输入错误,请重新输入\n");
							}
						}
					}
				}
				else
				{
					printf("该坐标已经被排查过,请重新输入\n");
				}
			}
			else//越界
			{
				printf("该坐标越界,请重新输入\n");
			}
		}
		FindMineCount++;
		return ret1;
	}
}

//定义第一次排雷是安全的,不是雷并且可以达到展开一片的效果,增加了可玩性
void FirstIsSafe(char mine[ROWS][COLS], char show[ROWS][COLS],int x, int y)
{
	//若该点不是雷且周围无雷,直接展开
	if (mine[x][y] != '1' && AroundMineCount(mine, x, y) == 0)
	{
		ExpandAround(mine, show, x, y);
	}
	//否则分情况
	else
	{
		//先判断该点是不是雷
		if (mine[x][y] == '1')//该点为雷,将其移走
		{
			mine[x][y] = '0';
			while (1)
			{
				int x1 = rand() % ROW + 1;
				int y1 = rand() % COL + 1;
				if (mine[x1][y1] != '1' && x1 != x && y1 != y)
				{
					mine[x1][y1] = '1';
					break;
				}
			}
		}
		//以及将雷移走了,在判断周围是否有雷
		if (AroundMineCount(mine, x, y) != 0)//该点周围有雷,将它们移走
		{
			int count = AroundMineCount(mine, x, y);//计算周围雷的数量
			int i = 0;
			for (i = x - 1;i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					mine[i][j] = '0';//将周围的雷变为无雷的状态
				}
			}
			while (count)
			{
				int x1 = rand() % ROW + 1;
				int y1 = rand() % COL + 1;
				if ((x1 != x - 1 && y1 != y - 1)&&
					(x1 != x - 1 && y1 != y)&&
					(x1 != x - 1 && y1 != y + 1)&&
					(x1 != x && y1 != y - 1)&&
					(x1 != x && y1 != y)&&
					(x1 != x && y1 != y + 1)&&
					(x1 != x + 1 && y1 != y - 1)&&
					(x1 != x + 1 && y1 != y)&&
					(x1 != x + 1 && y1 != y + 1))//不能将雷移回来
				{
					if (mine[x1][y1] != '1')
					{
						mine[x1][y1] = '1';//如果该坐标不是雷,则布置雷
						count--;
					}
				}
			}
		}
		ExpandAround(mine, show, x, y);
	}
}
  • test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//菜单
void Menu()
{
	printf("-------------------------------------\n");
	printf("|              扫雷游戏!           |\n");
	printf("|        0.exit        1.play       |\n");
	printf("-------------------------------------\n");
}
void Game()
{
	//创建两个二维数组
	char mine[ROWS][COLS];//存放该点是不是雷    (0代表不是雷,1代表是雷)
	char show[ROWS][COLS];//存放该点周围有多少雷(数字代表周围有多少雷,*代表未知)

	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//打印棋盘
	//PrintBoard(mine, ROW, COL);
	//PrintBoard(show, ROW, COL);

	//布置雷
	SetMine(mine, ROW, COL);

	//PrintBoard(mine, ROW, COL);
	PrintBoard(show, ROW, COL);

	int select = 0;
	int ret1 = 0;
	int ret2 = 0;
	while (ret1 != 1 && ret2 != 1)
	{
		printf("1.排查雷  2.标记雷  3.删除雷的标记\n");
		printf("请选择:>");
		scanf("%d", &select);
		switch (select)
		{
		case 1:
			ret1 = FindMine(mine, show, ROW, COL);
			ret2 = IsWin(show, ROW, COL);
			break;
		case 2:
			MarkMine(show, ROW, COL);
			break;
		case 3:
			DelectMarkMine(show, ROW, COL);
			break;
		default:
			printf("输入的值错误,请重新输入\n");
			break;
		}
	}
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//设置随机种子
	do
	{
		Menu();
		printf("请输入(0/1):>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

四.截图

  • 最后附上其中一张截图。

请添加图片描述

  • 感兴趣的可以去玩一下哈,最后到这文章也就结束了,日后还会更新更多的小游戏,例如贪吃蛇等等,喜欢的话可以三连下下哦,以防找不到了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/592073.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用机器学习确定文本的编程语言

导入必要的库 norman Python 语句&#xff1a;import <span style"color:#000000"><span style"background-color:#fbedbb"><span style"color:#0000ff">import</span> pandas <span style"color:#0000ff&quo…

Postman的一些使用技巧

Postman 是一个流行的 API 开发工具&#xff0c;用于设计、开发、测试、发布和监控 API。在现代web开发中使用非常广泛。后端开发必备而且必会的工具。 目录 1.配置环境变量 2.动态变量 3.脚本 4.测试 5.模拟 6.监控 7.集合运行器 8.响应保存 9.请求历史 10.同步请求…

基于springboot+vue+Mysql的影城管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

brpc profiler

cpu profiler cpu profiler | bRPC MacOS的额外配置 在MacOS下&#xff0c;gperftools中的perl pprof脚本无法将函数地址转变成函数名&#xff0c;解决办法是&#xff1a; 安装standalone pprof&#xff0c;并把下载的pprof二进制文件路径写入环境变量GOOGLE_PPROF_BINARY_PA…

现代循环神经网络(GRU、LSTM)(Pytorch 14)

一 简介 前一章中我们介绍了循环神经网络的基础知识&#xff0c;这种网络 可以更好地处理序列数据。我们在文本数据上实现 了基于循环神经网络的语言模型&#xff0c;但是对于当今各种各样的序列学习问题&#xff0c;这些技术可能并不够用。 例如&#xff0c;循环神经网络在…

Java中接口的默认方法

为什么要使用默认方法 当我们把一个程序的接口写完后 用其他的类去实现&#xff0c;此时如果程序需要再添加一个抽象方法的时候我们只有两种选择 将抽象方法写在原本的接口中 但是这样写会导致其他所有改接口的实现类都需要实现这个抽象方法比较麻烦 写另一个接口 让需要的实…

23.哀家要长脑子了!

目录 1.290. 单词规律 - 力扣&#xff08;LeetCode&#xff09; 2.532. 数组中的 k-diff 数对 - 力扣&#xff08;LeetCode&#xff09; 3.205. 同构字符串 - 力扣&#xff08;LeetCode&#xff09; 4.138. 随机链表的复制 - 力扣&#xff08;LeetCode&#xff09; 5.599. 两…

Spring Boot与OpenCV:融合机器学习的智能图像与视频处理平台

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

VMware虚拟机中ubuntu使用记录(6)—— 如何标定单目相机的内参(张正友标定法)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、张正友相机标定法1. 工具的准备2. 标定的步骤(1) 启动相机(2) 启动标定程序(3) 标定过程的操作(5)可能的报错 3. 标定文件内容解析 前言 张正友相机标定法…

什么是PWM?

1.PWM也叫做脉冲宽度调制&#xff0c;它是一种模拟控制方式&#xff0c;根据相应 载荷 的变化来调制晶体管基级和MOS管栅极的偏置&#xff0c;来实现 晶体管 或 MOS管 导通时间的改变&#xff0c;从而实现开关稳压电源输出的改变。 这种方式能使电源的输出电压在工作条件变化时…

linux的基础入门(2)

环境变量 在Shell中&#xff0c;正确的赋值语法是没有空格的&#xff0c;即变量名数值。所以&#xff0c;正确的方式是&#xff1a; tmpshy 这样就将变量tmp赋值为"shy"了。 注意&#xff1a;并不是任何形式的变量名都是可用的&#xff0c;变量名只能是英文字母、…

Reac19 升级指南

Reactv19 已经发布 beta 版本&#xff0c;想要快速体验如何升级到 v19 版本尝鲜的朋友们可以查阅进行了解 前言 React 已于近日发布了 v19 的 beta 版本&#xff0c;同时为了帮助后续的 v19 升级&#xff0c;也同时发布了 v18.3.0的正式版&#xff0c; 与 v18.2 版本完全相同…

JavaEE初阶Day 15:文件IO(1)

目录 Day 15&#xff1a;文件IO&#xff08;1&#xff09;IO文件1. 路径2. 文件的分类3. 使用Java针对文件系统进行操作3.1 属性3.2 构造方法3.3 方法 Day 15&#xff1a;文件IO&#xff08;1&#xff09; IO I&#xff1a;Input输入 O&#xff1a;Output输出 输入输出规则…

【golang-ent】go-zero框架 整合 ent orm框架实现一对一 一对多 多种姿势查询方式

一、ent的 O2O 问题 官方文档如下: https://entgo.io/zh/docs/schema-edges#o2o-same-type 1、ent O2O问题 官方提供了三种 one2one的方式,可以看到他全部使用了 mysql的 foregionKey 的方式进行关联,虽然举例了单表和双表的不同使用方式,但是我们实际使用mysql中是不创建…

深度解析 Spring 源码:探寻Bean的生命周期

文章目录 一、 Bean生命周期概述二、Bean生命周期流程图三、Bean生命周期验证3.1 代码案例3.2 执行结果 四、Bean生命周期源码4.1 setBeanName()4.2 setBeanFactory()4.3 setApplicationContext()4.4 postProcessBeforeInitialization()4.5 afterPropertiesSet()4.6 postProces…

银行数字化转型导师坚鹏:银行数字化转型5大战略定位

银行数字化转型必须建立清晰的战略定位&#xff0c;可以从以下5个方面建立&#xff0c;使广大银行员工明白银行数字化转型的重要性。 1.国家战略落地的重要抓手 银行是中国金融体系的核心&#xff0c;银行是做好“科技金融、绿色金融、普惠金融、养老金融和数字金融‘五篇大文…

【c++】继承学习(二):探索 C++ 中派生类的默认机制与静态成员共享

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 目录 1.派生类的默认成员函数2.继承与友元3.继承与静态成员 朋友们大家好&#xff0c;本篇文章我们来学习继承的第二部分 1.派生类的默认成员函数 来看下面的类&#xff1a; class Person…

【自留】运行一个开源项目

运行一个开源项目 首先是运行起来 1. 拿到地址 拿到你想要的项目的地址 2. 克隆 打开编辑器 VSCode &#xff0c;创建一个放项目的文件夹&#xff0c;控制台输入以下代码克隆项目 git clone 克隆地址gitee克隆地址在这看&#xff1a; github上项目的话&#xff0c;在这…

【python的魅力】:教你如何用几行代码实现文本语音识别

文章目录 引言一、运行效果二、文本转换为语音2.1 使用pyttsx32.2 使用SAPI实现文本转换语音2.3 使用 SpeechLib实现文本转换语音 三、语音转换为文本3.1 使用 PocketSphinx实现语音转换文本 引言 语音识别技术&#xff0c;也被称为自动语音识别&#xff0c;目标是以电脑自动将…

SSM+Vue+小程序+基于微信小程序的高校学生事务管理系统

项目配合学校日常生活通知&#xff0c;考试等管理需要&#xff0c;开发学校事务管理系统&#xff0c;maven管理依赖&#xff0c;mybatis处理数据库交互 学校管理 学院管理 班级管理 年级管理 教师管理 通知公告管理 学生资料管理 待办事项管理 教务处通知管理 讲座通…