前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >[c语言日寄]高度注释扫雷程序~免费源码

[c语言日寄]高度注释扫雷程序~免费源码

作者头像
siy2333
发布2025-02-05 12:45:57
发布2025-02-05 12:45:57
4300
代码可运行
举报
文章被收录于专栏:来自csdn的博客来自csdn的博客
运行总次数:0
代码可运行

2024.12.14 全流程实现了一个真正意义上的简单小游戏。此时学习编程4个月。

使用语言:c语言

实现平台:Visual Studio 2022

特别说明:由于是使用VS实现,采用了scanf_s函数,其余IDE需要将全部都scanf_s函数转换为scanf即可正常运行。

附免费源代码~在文末QwQ

实现功能:
  1. 简单的开始界面,并提供了数个选项以供选择(开始游戏,退出游戏,教程,开发者模式)

起始UI

  1. 开始游戏:
    1. 可以自行选择生成的地雷数
    1. 随机生成地雷,按实际情况输出提示数字
    2. 生成简洁的游玩界面:棋盘~
    游玩界面
    游玩界面

    游玩界面

    1. 可以挖掘地块,标记地雷,以及辅助疑问符(挖掘空格会同时打开相邻的空格)
    1. 找出全部地雷则胜利,输出操作次数
    1. 如果失败则会有一段简单的动画,输出操作次数,以及剩余没找到的地雷位置与个数。
  2. 教程:战术教学玩法
  1. 开发者模式:对局中展示棋盘下隐藏的数据,并且不会清空上一步骤的屏幕。
主要模块:

1. 主函数(main):    - 初始化游戏,设置随机种子。    - 创建一个11x11的数组来代表游戏棋盘,并初始化。

2. 开始界面(begin_s):    - 提供用户选择的菜单,包括开始游戏、退出、教程和开发者模式。    - 如果用户选择开始游戏,会让用户输入地雷的数量,并初始化棋盘,生成地雷和提示。

3. 游玩界面(play):    - 显示当前游戏棋盘的状态。    - 接收用户的输入,包括操作类型(挖掘、标记地雷或标疑问符)和坐标。    - 根据用户的操作,调用`operate`函数来执行相应的游戏逻辑。

4. 失败和胜利界面:    - `lose`函数显示失败时的动画和棋盘状态。    - `failed_interface`函数显示失败时的结算界面。    - `victory_interface`函数显示胜利时的结算界面。

5. 后端逻辑:    - `MineGeneration`函数在棋盘上随机生成地雷。    - `Prompt`函数生成地雷周围的提示数字。    - `pure`函数将空白地块标记为隐藏状态。    - `printf_play`函数用于打印当前棋盘的状态。    - `operate`函数处理用户的游玩操作,包括挖掘地块、标记地雷和标疑问符。    - `move`函数用于在控制台中移动光标位置。    - `Victory_judgement`函数用于判断用户是否赢得了游戏。    - `pure_look`函数用于递归地揭示空白地块。

6. 测试函数(printf_arr):    - 用于打印数组的状态,方便调试。

流程图
地雷提示字生成
数组生成

别问我为什么这张画风不一样,因为换软件了awa

用户输入模块
游玩输出模块
代码~复制即玩~~

ps:由于是使用VS实现,采用了scanf_s函数,其余IDE需要将全部都scanf_s函数转换为scanf即可正常运行。

代码语言:javascript
代码运行次数:0
复制
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
#include<Windows.h>

int gamemode = 0;

void MineGeneration(int* arr, int Set_MineNum);
void printf_arr(int* arr);
void Prompt(int* arr);
int begin_s(int* arr, int* num_operation);
int play(int* arr);
void printf_play(int* arr);
void pure(int* arr);
int operate(int play_operate, int play_x, int play_y, int* arr);
void move(int position);
void lose(int* arr);
void failed_interface(int num_operations, int left_mines, int Set_MineNum);
int Victory_judgement(int* arr, int* left_mine);
void victory_interface(int num_operations, int Set_MineNum);
void pure_look(int* arr, int pure_x, int pure_y);

int main(void){

	srand((unsigned int)time(NULL));	//随机种子
	int arr[11][11];
	memset(arr, 0, 11 * 11 * sizeof(int));	//数组初始化

	//操作次数
	int num_operations = 0;
	int* num_operation = &num_operations;
	//剩余雷数
	int left_mines = 0;
	int* left_mine = &left_mines;

	int game_state = 0;	//游戏状态

	int Set_MineNum = begin_s(arr, num_operation);	//----开始界面

	if (Set_MineNum == -2) { return 0; }

	do {
		if (num_operations >= 300)
		{
			printf("超过300次操作啦,自动结束游戏哦");
		}
		if (Victory_judgement(arr, left_mine) == 1)
		{
			game_state = 1;
			break;
		}
		num_operations++;

	} while (!(game_state = play(arr)));

	switch (game_state)
	{
	case -1:
		failed_interface(num_operations, left_mines, Set_MineNum);
		break;
	case 1:
		system("cls");
		printf_play(arr);
		victory_interface(num_operations, Set_MineNum);
		break;
	default:
		printf("游戏状态错误");
		printf("game_state = %d", game_state);
		break;
	}
	return 0;
}
1.前端

///1.1 初始界面
//1.1.1初始界面生成函数
int begin_s(int* arr,int* num_operation) {
	//循环保护
	static int Loop_detector = 0;
begin_again:
	if(Loop_detector >= 50){
		system("cls");
		printf("程序循环了50次,触发保护");
		return;
	}
	Loop_detector++;
	int begin_num = 0;
	printf("\n################################\n");
	printf("·                              \n");
	printf("· >.欢迎来到扫雷游戏            \n");
	printf("· 1.开始游戏                    \n");
	printf("· 2.退出游戏                    \n");
	printf("· 3.教程                        \n");
	printf("· 4.开/关 开发者模式             \n");
	printf("· >.请在键盘输入对应的数字序号进行操作 \n");
	printf("·                              \n");
	printf("################################\n");
	scanf_s("%d", &begin_num);
	if (!(begin_num == 1 || begin_num == 2 || begin_num == 3 || begin_num == 4)) {
		system("cls");
		printf("出错了:好像输入了无法理解的操作呢……请输入表中给出的操作哦");
		goto begin_again;
	}
	switch (begin_num)
	{
	case 1:	
		int Set_MineNum = 0;
		printf("请输入生成的地雷数(1-81):");
		scanf_s("%d",&Set_MineNum);
		//安全性判断
		if (Set_MineNum > 81 || Set_MineNum < 1)
		{
			system("cls");
			printf("出错了:好像输入了无法理解的操作呢……请输入表中给出的操作哦");
			Set_MineNum = 0;
			goto begin_again;
		}

		memset(arr, 0, 11 * 11 * sizeof(int));	//数组初始化
		//printf_arr(arr);	
		//printf("\n——————————————————\n");//----初始化正确性检查点;

		/*Set_MineNum = 11;*/
		MineGeneration(arr, Set_MineNum);
		//printf_arr(arr);	
		//printf("\n——————————————————\n");	//----地雷生成正确性检查点

		Prompt(arr);
		//printf_arr(arr);
		//printf("\n——————————————————\n");	//----地雷提示生成正确性检查点

		pure(arr);
		//printf_arr(arr);
		//printf("\n——————————————————\n");	//----空白地块生成检查点

		*num_operation = 0;	//初始化操作次数

		return Set_MineNum;
	case 2: return -2;
	case 3:
		system("cls");
		printf("欢迎游玩扫雷游戏!\n");
		Sleep(2000);
		printf("你接下来会看到一个9*9的棋盘\n");
		Sleep(2000);

		memset(arr, 0, 11 * 11 * sizeof(int));	//数组初始化
		int test = 0, test1 = 0, test2 = 0, test3 = 0;
		*(arr + 11*1+3) = 1;
		Prompt(arr);
		pure(arr);
		printf_play(arr);
		printf("对,就是上面这样");
		printf("(输入1继续)\n");
		while (test != 1) {
			scanf_s("%d", &test);
		}
		printf("每一个白色方块下面都有可能藏着一颗地雷\n");
		Sleep(3000);
		printf("你的任务就是找出所有的地雷\n");
		Sleep(2000);
		printf("不过别担心,我们会有提示\n");
		Sleep(2000);
		printf("输入 0 1 1试试,数字之间用空格隔开:\n");
		while (test1 != 0 || test2 != 1 || test3 != 1) {
			scanf_s("%d%d%d", &test1, &test2, &test3);
		}
		system("cls");
		operate(0, 1, 1, arr);
		printf_play(arr);
		printf("太棒了,你成功的掀开了一个区域,幸运的是里面没有地雷。\n");Sleep(3000);
		printf("让我们解析一下你输入的内容。\n");Sleep(3000);
		printf("第一个数字是操作符,操作符号有3种,“0’的作用是掀开这个区域\n");Sleep(4000);
		printf("除此之外还有‘1’,用于标记地雷所在地\n");Sleep(3000);
		printf("最后一个操作符号是‘2’,用于辅助你判断哪个方块之下有地雷");Sleep(1000);
		printf("(输入1继续)\n");
		test = 0;
		while (test != 1) {
			scanf_s("%d", &test);
		}
		printf("后面两位数字的作用你可能已经猜到了,没错,是坐标!\n");Sleep(3000);
		printf("你刚刚输入了‘1’‘1’,意味着你对着点(1,1)进行了“掀开”操作\n");Sleep(4000);
		printf("不过为什么你只针对(1,1)进行操作,却同时掀开那么多的点?\n");Sleep(4000);
		printf("那是因为:没有地雷且没有提示的连续方块,被看作一个区域\n");Sleep(4000);
		printf("这样就可以减少你手指耐久度的损耗了,不是吗(笑)");Sleep(3000);
		printf("(输入1继续)\n");
		test = 0;
		while (test != 1) {
			scanf_s("%d", &test);
		}
		system("cls");
		printf_play(arr);
		printf("好,接下来我们继续完成这盘对局\n");Sleep(3000);
		printf("输入0 2 2试试\n");Sleep(3000);
		while (test1 != 0 || test2 != 2 || test3 != 2) {
			scanf_s("%d%d%d", &test1, &test2, &test3);
		}
		system("cls");
		operate(0, 2, 2, arr);
		printf_play(arr);

		printf("如你所见,这里有一个数字‘1’\n");Sleep(4000);
		printf("它代表:以它为中心,周围3*3的区域里,一共有1个地雷\n");Sleep(4000);
		printf("没错,这就是“提示”\n");Sleep(4000);
		printf("这一类数字包含了1-8,他们的意义同理\n");Sleep(4000);
		printf("玩到这里,你应该知道地雷在哪里了吧!\n");Sleep(4000);
		printf("没错,在(3,1)!\n");Sleep(4000);
		printf("现在,使用操作符号‘1’,对(3,1)进行标记吧!\n");Sleep(2000);

		while (test1 != 1 || test2 != 3 || test3 != 1) {
			scanf_s("%d%d%d", &test1, &test2, &test3);
		}
		system("cls");
		operate(1, 3, 1, arr);
		printf_play(arr);
		printf("太棒了,你找到了所有地雷,通过了这个特别简单的教学关卡\n");Sleep(5000);
		printf("选择开始游戏,你可以自定义难度,挑战更高难度的关卡\n");Sleep(5000);
		printf("输入1开始你的扫雷之旅吧!\n");
		test = 0;
		while (test != 1) {
			scanf_s("%d", &test);
		}
		system("cls");
		goto begin_again;
	case 4:
		if (gamemode == 0) { 
			gamemode = 1; 
			system("cls");
			printf("已开启开发者模式\n");
			goto begin_again;
		}
		else { 
			gamemode = 0; 
			system("cls");
			printf("已关闭开发者模式\n");
			goto begin_again;
		}

	}
}

///1.2 游玩界面
//1.2.1 游玩面板
int play(int* arr){
	int play_state = 0;//游玩状态控制

	//测试点
	if (gamemode == 1) {
		printf("测试台:\n");
		printf_arr(arr);
		printf("\n——————————————————\n");
	}
	else {
		system("cls");
	}

	printf_play(arr);
	printf("\n--------");
	printf("操作符:0.挖掘地块\t1.地雷标记/取消标记\t2.标疑问符/去疑问符");
	printf("--------\n\n");
	int play_operate = 0, play_x = 0, play_y = 0;
	scanf_s("%d%d%d", &play_operate, &play_x, &play_y);
	play_state = operate(play_operate, play_x, play_y, arr);
	if (play_state == -1) {
		lose(arr);
		return -1;
	}
	return 0;
}

///1.3 结算界面

//1.3.1 失败动画

void lose(int* arr) {
	//移动光标
	printf("\n");
	move(26);
	//列提示生成
	printf("  行\t1\t2\t3\t4\t5\t6\t7\t8\t9\t  \n列\n\n");
	for (int onei = 1; onei < 10; onei++)
	{
		//9*9棋盘生成
		printf("%d\t", onei);		//输出提示列坐标
		for (int onej = 1; onej < 10; onej++)
		{
			switch (*(arr + onei * 11 + onej) / 100) {
			case 1:
				//旗帜生成
				if (*(arr + onei * 11 + onej) % 100 == 1) {
					printf("(*)\t");
				}
				else { printf("X\t"); }
				continue;
			case 2:
				//问号生成
				printf("?\t");
				continue;
			case 0:
				break;
			default:
				system("cls");
				printf("严重错误:数字错误!!!!");
				break;
			}
			switch (*(arr + onei * 11 + onej) % 100)
			{
				//提示符(隐藏)生成
			case 6:
			case 7:
			case 8:
			case 9:
			case 10:
			case 11:
			case 12:
			case 13:
				//空白块(隐藏)生成
			case 17:
				printf("■\t");
				break;
				//地雷爆炸!!
			case 1:
				printf("*");
				Sleep(100);
				printf("\b■");
				Sleep(200);
				printf("\b*\t");
				Sleep(100);
				
				break;
				//数字提示(显示)生成
			case 21:
			case 22:
			case 23:
			case 24:
			case 25:
			case 26:
			case 27:
			case 28:
				printf("%d\t", *(arr + onei * 11 + onej) - 20);
				break;
				//空白块(揭开)生成
			case 2:
				printf("\t");
				break;
				//错误提示生成
			default:
				system("cls");
				printf("严重错误:数字错误!!!!");
				break;
			}
		}
		printf("\n\n");	//换行
	}
	return;
}

//1.3.2 失败结算界面

void failed_interface(int num_operations, int left_mines, int Set_MineNum) {
	printf("很遗憾,你输了。");
	printf("\t您此局一共操作了%d次, 一共%d个雷, 剩余%d个雷没找出,再接再厉!", num_operations, Set_MineNum , left_mines);
	printf("\n                                                                                                            ");
	printf("\n                                                                                                            ");
	printf("\n                                                                                                            ");
	printf("\n                                                                                                            ");
}


//1.3.3 胜利结算
void victory_interface(int num_operations, int Set_MineNum) {
	printf("恭喜你,你赢了!");
	printf("\t您此局一共操作了%d次, 找出了全部一共%d个雷!", num_operations, Set_MineNum);
	printf("\n                                                                                                            ");
	printf("\n                                                                                                            ");
	printf("\n                                                                                                            ");
	printf("\n                                                                                                            ");
}


2.后端

///2.1数组模块

//2.1.1地雷生成函数
void MineGeneration(int* arr, int Set_MineNum) {
	int MineNum = 0, Arr_i = 1, Arr_j = 0, ins_mine = 0;
	while (MineNum < Set_MineNum) {
		ins_mine = (rand() % 10 + 1);
		if (ins_mine <= 8)
		{
			Arr_j = (rand() % 9) + 1;
			//重复性检查1
			if (*(arr+Arr_i*11+Arr_j)) {
				Arr_j = (Arr_j + 1) % 9 + 1;
				for (int ii = 1; ii <= 9; ii++)
				{
					if (*(arr + Arr_i * 11 + Arr_j)) {
						Arr_j = (Arr_j + 1) % 9 + 1;
					}
					else {
						*(arr + Arr_i * 11 + Arr_j) = 1;
						MineNum++;
						break;
					}
				}
				Arr_i = (Arr_i + 1) % 9 + 1;
			}
			else {
				*(arr + Arr_i * 11 + Arr_j) = 1;
				MineNum++;
				Arr_i = (Arr_i + 1) % 9 + 1;
			}
		}
	}
	return;
}

//2.1.2地雷提示字生成

void Prompt(int* arr) {
	//定义局部变量
	static int mine_num = 0;
	//遍历1-9行
	for (int onei = 1; onei < 10; onei++)
	{
		//遍历1-9列
		for (int onej = 1; onej < 10; onej++)
		{
			//判定是否含雷
			if (!*(arr + onei * 11 + onej)) {
				//初始化雷数
				mine_num = 0;
				///进入雷数收集
				//遍历列
				for (int twoi = -1; twoi < 2; twoi++)
				{
					//遍历行
					for (int twoj = -1; twoj < 2; twoj++)
					{
						//判定是否有雷
						if (*(arr+11*(onei+twoi)+(onej+twoj)) == 1)
						{
							//雷数+1
							mine_num++;
						}
					}
				}
				//赋值阶段
				if (mine_num != 0)
				{
					*(arr + onei * 11 + onej) = 5 + mine_num;
				}
			}
		}
	}

}

//2.1.3 空白地块生成
void pure(int* arr) {
	for (int onei = 1; onei < 10; onei++)
	{
		for (int onej = 1; onej < 10; onej++)
		{
			if ((*(arr + onei * 11 + onej)) == 0)
			{
				*(arr + onei * 11 + onej) = 17;
			}
		}
	}
}

///2.2用户游玩模块

//2.2.1 游玩输出
void printf_play(int* arr) {
	//列提示生成
	printf("  行\t1\t2\t3\t4\t5\t6\t7\t8\t9\t  \n列\n\n");
	for (int onei = 1; onei < 10; onei++)
	{
		//9*9棋盘生成
		printf("%d\t", onei);		//输出提示列坐标
		for (int onej = 1; onej < 10; onej++)
		{
			switch (*(arr + onei * 11 + onej) / 100) {
			case 1:
				//旗帜生成
				printf("X\t");
				continue;
			case 2:
				//问号生成
				printf("?\t");
				continue;
			case 0:
				break;
			default:
				system("cls");
				printf("严重错误:数字错误!!!!");
				break;
			}
			switch (*(arr + onei * 11 + onej) % 100)
			{
				//提示符(隐藏)生成
			case 6:
			case 7:
			case 8:
			case 9:
			case 10:
			case 11:
			case 12:
			case 13:
				//空白块(隐藏)生成
			case 17:
				//地雷隐藏块生成
			case 1:
				printf("■\t");
				break;
				//数字提示(显示)生成
			case 21:
			case 22:
			case 23:
			case 24:
			case 25:
			case 26:
			case 27:
			case 28:
				printf("%d\t", *(arr + onei * 11 + onej) - 20);
				break;
				//空白块(揭开)生成
			case 2:
				printf("\t");
				break;
				//错误提示生成
			default:
				system("cls");
				printf("严重错误:数字错误!!!!");
				break;
			}
		}
		printf("\n\n");	//换行
	}

}

//2.2.2 游玩操作

int operate(int play_operate, int play_x, int play_y, int* arr) {
	//操作符:0.挖掘地块 1.插旗/拔旗 2.标疑问符/去疑问符
	switch (play_operate) {
	case 0:
		switch (*(arr + play_y * 11 + play_x) % 100) {
			//含地雷方块掀开
		case 3:
		case 4:
		case 1:
			//接结束函数:end();
			return -1;
			//地雷提示数字掀开
		case 6:
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 12:
		case 13:
			*(arr + play_y * 11 + play_x) += 15;
			break;
		//空白块
		case 17:
			pure_look(arr, play_x, play_y);
		}
		break;
	case 1:
		//插旗
		if (*(arr + play_y * 11 + play_x) >= 100 && *(arr + play_y * 11 + play_x) < 200) {
			*(arr + play_y * 11 + play_x) -= 100;
		}
		//拔旗
		else {
			*(arr + play_y * 11 + play_x) += 100;
		}
		break;
	case 2:
		//标问号
		if (*(arr + play_y * 11 + play_x) >= 200 && *(arr + play_y * 11 + play_x) < 300) {
			*(arr + play_y * 11 + play_x) -= 200;
		}
		//去问号
		else {
			*(arr + play_y * 11 + play_x) += 200;
		}
		break;
	default:
		system("cls");
		printf("出错了:操作符错误!");
		printf("play_operate = %d", play_operate);
		break;
	}
	return 0;
}

//2.2.3 基于windowsAPI的光标移动
void move(int position) {
	// 获取当前光标位置
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(hConsole, &csbi);

	// 计算上一行的位置
	COORD newPos = { csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y - position };

	// 移动光标到上一行
	SetConsoleCursorPosition(hConsole, newPos);
}

//2.2.4 胜利判定

int Victory_judgement(int* arr, int* left_mine) {
	*left_mine = 0;
	for (int onei = 1; onei < 10 ; onei++)
	{
		for (int onej = 1; onej < 10; onej++)
		{
			if (((*(arr + onei * 11 + onej)) % 100 == 1) && !((*(arr + onei * 11 + onej)) / 100 == 1)) {
				(*left_mine)++;
			}
		}
	}
	if(*left_mine == 0){return 1;}
	return; 
}

//2.2.5 空白块显示函数

void pure_look(int* arr,int pure_x,int pure_y){

	for (int onei = -1; onei <= 1; onei++)
	{
		for (int onej = -1; onej <= 1; onej++)
		{
			if ((*((arr + pure_y * 11 + pure_x) + onej * 11 + onei) % 100) == 17) {
				*((arr + pure_y * 11 + pure_x) + onej * 11 + onei) = 2;
				pure_look(arr, pure_x + onei, pure_y + onej);
			}
		}
	}
}


3.测试

void printf_arr(int* arr) {
	for (int i = 0; i < 11; i++)
	{
		for (int j = 0; j < 11; j++)
		{
			if (*(arr + i * 11 + j) <= 13 && *(arr + i * 11 + j) >= 6) {
				printf("N:%d\t", *(arr + i * 11 + j) - 5);
				continue;
			}
			if (*(arr + i * 11 + j) <= 28 && *(arr + i * 11 + j) >= 21) {
				printf("N:%d\t", *(arr + i * 11 + j) - 20);
				continue;
			}
			else{ printf("%d\t", *(arr + i * 11 + j));continue;
			}
		}
		printf("\n");
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 附免费源代码~在文末QwQ
    • 实现功能:
    • 主要模块:
    • 流程图
      • 地雷提示字生成
      • 数组生成
      • 用户输入模块
      • 游玩输出模块
    • 代码~复制即玩~~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档