IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 鹏哥C语言——扫雷2021-08-16 -> 正文阅读

[C++知识库]鹏哥C语言——扫雷2021-08-16

#1 源文件

//头文件 supportingGame.h
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <stdlib.h>
//一场游戏。需要:1.雷区(int[][]);2.地雷数目(int);3.死亡与否(int)
struct oneGame {//一个问题就是这个二维数组其实是不定的,在创建结构体时又不能进行动态内存分配。
	char* mineField = NULL;//退而求其次,不能创建动态分配的二维数组,就创建一个指针。用这个指针指向并且操作二维数组。
	int mines = 0;
	int deadOrNot = 1;//1表示没死,0表示挂了
	char* workDone = NULL;//用于表示已经完成的工作,大小和mineField一致。0代表没有完成,1代表完成。

};

int menu();//欢迎界面和模式选择。返回选择的模式。
//1:初级模式(9*9,10);2.中级模式(16*16,40);3.高级模式(16*30,99)4.自定义模式
//自定义不在menu中进行,而在game()中实现

int game(int);//读入游戏模式,返回是否愿意进行下一个游戏
void generateMineField(int row, int col, int mines, oneGame* thisGame);//读入行列和雷的数目,用于初始化oneGame对象和生成雷区。
//由下列几项功能组成:1.生成雷区的二维数组,提供给oneGame对象;给oneGame.mines赋值(初始化oneGame对象)
//2.随机分配地雷埋藏的位置(x,y)。并且在雷区这个二维数组的相应位置赋值为'#'。generateMine();
//3.根据地雷埋藏的位置,生成数字,用于提示。generateNumber();基本原理是数字检测旁边8个方块的地雷数目。
void generateMine(int row0, int col0, int mines, oneGame* thisGame);//在指定数组生成雷
void generateNumber(int row0, int col0, int mines, oneGame* thisGame);
//int displayMineField();
//展示一个简单的扫雷界面。行列标注(不然数起来麻烦),剩余雷的数目,死亡与否,花费时间,雷区组成。
void displayMineField(oneGame*, int row, int col);//每一次扫雷打印一份界面
//扫一次雷。(对workDone数组中的一个元素加以改变)。并且判断有无死亡或是游戏胜利(游戏结束)。
void sweepFieldOnce(oneGame* thisGame, int row, int col);

//源文件supportingGame.cpp
#include "supportingGame.h"

int menu() {
	char mode = 0;
	printf("*******************************\n");
	printf("****Welcome to MineSweeper!****\n");
	printf("*******************************\n");
	printf("**Please choose the game mode**\n");
	printf("#1.primary######\n");
	printf("#2.middle#######\n");
	printf("#3.advanced#####\n");
	printf("#4.user-defined#\n");
	printf("Your choice>:	");
	//这个容错机制(%d)只能对数字容错,改为%c之后能对字符容错
	while (1) {
		scanf("%c", &mode);//这时往往还有字符在缓冲区中,比如\n,会引发一些意想不到的问题。
		/*scanf("%*[^\n]%*c");*///这段代码的意思有待探究,但是使用getchar就可以解决缓冲取的问题
		while (getchar()!='\n') {

		}
		switch (mode) {
		case '1': printf("primary mode starts!\n"); return 1;
		case '2': printf("middle mode starts!\n"); return 2;
		case '3': printf("advanced mode starts!\n"); return 3;
		case '4': printf("define yourself!\n"); return 4;
		default: 
			printf("Wrong input! Again>:	");
			break;
		}

	}
	return -1;
}
int game(int mode) {
	oneGame thisGame;
	int row = 0;
	int col = 0;
	int mines = 0;
	//游戏生成阶段,生成地雷和数字。
	switch (mode) {
	case 1: 
		row = 9;
		col = 9;
		mines = 10; break;
	case 2:
		row = 16;
		col = 16;
		mines = 40;
		break;
	case 3:
		row = 16;
		col = 32;
		mines = 99;
		break;
	case 4:
		printf("self-defined\n");
		break;
	default: 
		printf("Wrong input!\n");
		break;
	}
	
	
	generateMineField(row,col, mines, &thisGame);
	
	
	while (thisGame.deadOrNot) {
		displayMineField(&thisGame, row, col);
		sweepFieldOnce(&thisGame, row, col);
		system("cls");
	}
	displayMineField(&thisGame, row, col);


	//最后要free一下
	free(thisGame.mineField);
	free(thisGame.workDone);
	return 0;
}
void generateMineField(int row0, int col0, int mines, oneGame* thisGame) {
	thisGame->mines = mines;
	thisGame->mineField = (char*) malloc(row0*col0);//到游戏结束时应该有一个free函数
	thisGame->workDone = (char*)malloc(row0*col0);
	for (int i = 0; i < row0 * col0; ++i) {//先把这里的内存的内容都赋为0
		*(thisGame->mineField+i)= '0';
		*(thisGame->workDone + i) = '0';
	}
	generateMine(row0, col0, mines, thisGame);
	generateNumber(row0, col0, mines, thisGame);
	return;
}
void generateMine(int row0, int col0, int mines, oneGame* thisGame) {
	srand((unsigned long)time(NULL));
	for (int i = mines; i > 0; --i) {
		int x = rand() % row0;
		int y = rand() % col0;
		//如果(x,y)还没有放置地雷
		if (*(thisGame->mineField + y + x * col0)=='#') {
			++i;
			continue;
		}
		else *(thisGame->mineField + y + x * col0) = '#';
	}

	return;
}
void generateNumber(int row0, int col0, int mines, oneGame* thisGame) {
	char* arr = thisGame->mineField;
	int count = 0;
	//首先遍历四周,这些比较特殊
		//四角
	{	//左上角
		if (*(arr) == '#') {
			;//不做事
		}
		else {
			if (*(arr + 1) == '#') {
				count++;
			}
			if (*(arr + col0) == '#') {
				count++;
			}
			if (*(arr + col0 + 1) == '#') {
				count++;
			}
		}
		if (*(arr) != '#') {
			*(arr) = count + 48;
		}
		count = 0;
		//右上角
		if (*(arr + col0 - 1) == '#') {
			;//不做事
		}
		else {
			if (*(arr + col0 - 2) == '#') {
				count++;
			}
			if (*(arr + col0 - 1 + col0) == '#') {
				count++;
			}
			if (*(arr + col0 - 2 + col0) == '#') {
				count++;
			}
		}
		if (*(arr + col0 - 1) != '#') {
			*(arr + col0 - 1) = count + 48;
		}
		count = 0;
		//左下角
		if (*(arr + (row0 - 1) * col0) == '#') {
			;//不做事
		}
		else {
			if (*(arr + (row0 - 1) * col0 + 1) == '#') {
				count++;
			}
			if (*(arr + (row0 - 1) * col0 - col0) == '#') {
				count++;
			}
			if (*(arr + (row0 - 1) * col0 - col0 + 1) == '#') {
				count++;
			}
		}
		if (*(arr + (row0 - 1) * col0) != '#') {
			*(arr + (row0 - 1) * col0) = count + 48;
		}
		count = 0;
		//右下角
		if (*(arr + col0 - 1 + (row0 - 1) * col0) == '#') {
			;//不做事
		}
		else {
			if (*(arr + col0 - 1 + (row0 - 1) * col0 - 1) == '#') {
				count++;
			}
			if (*(arr + col0 - 1 + (row0 - 1) * col0 - col0) == '#') {
				count++;
			}
			if (*(arr + col0 - 1 + (row0 - 1) * col0 - col0 - 1) == '#') {
				count++;
			}
		}
		if (*(arr + col0 - 1 + (row0 - 1) * col0) != '#') {
			*(arr + col0 - 1 + (row0 - 1) * col0) = count + 48;
		}
		count = 0;
	}
	//东南西北
	count = 0;
	{	//北
		for (int i = 1; i <= col0 - 2; ++i) {
			if (*(arr + i) == '#')
				continue;
			if (*(arr + i) != '#') {
				if (*(arr + i - 1) == '#') {
					++count;
				}
				if (*(arr + i + 1) == '#') {
					++count;
				}
				if (*(arr + i + col0) == '#') {
					++count;
				}
				if (*(arr + i + col0 - 1) == '#') {
					++count;
				}
				if (*(arr + i + col0 + 1) == '#') {
					++count;
				}
				*(arr + i) = count + 48;
			}
			count = 0;
		}
		count = 0;
		//南
		for (int i = 1; i <= col0 - 2; ++i) {
			if (*(arr + i + (row0 - 1) * col0) == '#')
				continue;
			if (*(arr + i + (row0 - 1) * col0) != '#') {
				if (*(arr + i + (row0 - 1) * col0 + 1) == '#') {
					++count;
				}
				if (*(arr + i + (row0 - 1) * col0 - 1) == '#') {
					++count;
				}
				if (*(arr + i + (row0 - 1) * col0 - col0) == '#') {
					++count;
				}
				if (*(arr + i + (row0 - 1) * col0 - col0 - 1) == '#') {
					++count;
				}
				if (*(arr + i + (row0 - 1) * col0 - col0 + 1) == '#') {
					++count;
				}
				*(arr + i + (row0 - 1) * col0) = count + 48;
			}
			count = 0;
		}
		count = 0;
		//西
		for (int j = 1; j <= row0 - 2; ++j) {
			if (*(arr + j * col0) == '#')
				continue;
			if (*(arr + j * col0) != '#') {
				if (*(arr + j * col0 + 1) == '#') {
					++count;
				}
				if (*(arr + j * col0 - col0) == '#') {
					++count;
				}
				if (*(arr + j * col0 - col0 + 1) == '#') {
					++count;
				}
				if (*(arr + j * col0 + col0) == '#') {
					++count;
				}
				if (*(arr + j * col0 + col0 + 1) == '#') {
					++count;
				}
				*(arr + j * col0) = count + 48;
			}
			count = 0;
		}
		count = 0;
		//东
		for (int j = 1; j <= row0 - 2; ++j) {
			if (*(arr + j * col0 + row0 - 1) == '#')
				continue;
			if (*(arr + j * col0 + row0 - 1) != '#') {
				if (*(arr + j * col0 + row0 - 1 - 1) == '#') {
					++count;
				}
				if (*(arr + j * col0 + row0 - 1 + col0) == '#') {
					++count;
				}
				if (*(arr + j * col0 + row0 - 1 + col0 - 1) == '#') {
					++count;
				}
				if (*(arr + j * col0 + row0 - 1 - col0) == '#') {
					++count;
				}
				if (*(arr + j * col0 + row0 - 1 - col0 - 1) == '#') {
					++count;
				}
				*(arr + j * col0 + row0 - 1) = count + 48;
			}
			count = 0;
		}
		count = 0;
	}
	//遍历中间
	count = 0;
	
	{
		count = 0;
		for (int i = 1; i <= row0 - 2; ++i) {
			for (int j = 1; j <= col0 - 2; ++j) {
				if (*(arr + j + i * col0) == '#')
					continue;
					if (*(arr + j + i * col0) != '#') {
					if (*(arr + j + i * col0 + 1) == '#')
						++count;
					if (*(arr + j + i * col0 - 1) == '#')
						count++;
					if (*(arr + j + i * col0 - col0) == '#')
						count++;
					if (*(arr + j + i * col0 - col0 - 1) == '#')
						count++;
					if (*(arr + j + i * col0 - col0 + 1) == '#')
						count++;
					if (*(arr + j + i * col0 + col0) == '#')
						count++;
					if (*(arr + j + i * col0 + col0 - 1) == '#')
						count++;
					if (*(arr + j + i * col0 + col0 + 1) == '#')
						count++;
					*(arr + j + i * col0) = count + 48;
				}
				count = 0;
			}

		}
	}
	
}

void displayMineField(oneGame* thisGame, int row, int col) {
	//打印坐标和雷区,
		//将二维数组workDone和thisGame.mineField相乘得到可以打印的雷区
	for (int i = -1; i < row;++i) {
		printf("%2d ",i+1);
	
	}
	printf("\n");
	for (int i = 0; i < row;++i) {
		printf("%2d ", i+1);
	
		for (int j = 0; j < col;++j) {
			if (*(thisGame->workDone + j + i * col) == '0') {
				printf("  ");
			}
			else {
				printf("%2c ", *(thisGame->mineField + j + i * col));
			}

		}
		printf("\n");

	}


	//打印死亡与否
	if (thisGame->deadOrNot == 1) {
		;
	}
	else {
		printf("You are dead!\n");
		return;
	}
	//打印剩余地雷数量
	if (thisGame->mines == 0) {
		printf("Mission completed!\n");
		thisGame->deadOrNot = 0;
	}
	else {
		printf("%d mines left!\n", thisGame->mines);
	}
}


void sweepFieldOnce(oneGame* thisGame, int row, int col) {
	int x = 0;
	int y = 0;
	int choice = 0;
	printf("\n");
	printf("------------------------------------------------------------\n");
	scanf("%d %d %d", &x, &y, &choice);//x,y为选定坐标,而choice为:1.将选中处标记为雷;0.点击选中坐标。
	//判断游戏是否结束(死亡,胜利。先判断死亡,后修改thisGame,最后判断有无胜利。)
	int x0 = x - 1;
	int y0 = y - 1;
	if (choice == 1) {
		if (*(thisGame->mineField + y0 + x0 * col) != '#') {
			thisGame->deadOrNot = 0;
		}
		else {
			thisGame->mines--;
			*(thisGame->workDone + y0 + x0 * col) = '1';
		}
	}
	if (choice == 0) {
		if (*(thisGame->mineField + y0 + x0 * col) == '#') {
			thisGame->deadOrNot = 0;
		}
		else {
			*(thisGame->workDone + y0 + x0 * col) = '1'; 
		}
	}
		displayMineField(thisGame, row, col);
		return;
	
}

//主函数
#include "supportingGame.h"

int main() {
	int mode = menu();
	game(mode);


	return 0;
}

#2 经验或教训

这次的扫雷程序吸取了之前制作三子棋程序的教训,加强了编程之前的准备工作(建立程序的结构并且分析出了基本的对象),而且在代码的可扩展性方面下了功夫。总的来讲,这次的扫雷编写得很有逻辑感,算是成功的。但是还是有几点缺憾的地方:

1.编程之前的分析工作还是没有做足,内心还是有抗拒的情绪,这导致没有考虑清楚如何打印已经扫过的范围。具体表现为后续对oneGame结构体的修改,增加了workDone这一个二维数组用来记录已经扫过的范围——而正是这个数组的引入带来了内存泄漏的bug(return -1073740940)。这个bug后来解决了(把int[][]改为了char[][])。所以下一次要以耐心和理智做好编程前的分析工作,以达更高的编程效率。

具体的方法为:TTD思想。首先考虑清楚代码的实际应用场景和可能出现的问题,进而分析出程序的基本结构(控制流),需要进行的动作(函数)和所需基本的数据(数据结构,往往是结构体或是类,目前是这么理解)。可以反复地分析,用以发现更多的问题。分析出函数和结构体(或是数据结构)之后,就可以写头文件了,相应的文档一定要写好,用以解释结构体的作用(存放什么数据),和函数的参数的意义,函数的作用(完成什么事情)。也可以写函数的实现原理。? ? ? ? ? ? ? ? ? ? ? ?这之前是要给出程序的基本结构(main.cpp)。? ?最后才是具体实现(源文件.cpp)。? ? ? ? ??

2.另外是scanf缓冲区的问题,使用了getchar和while循环解决。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:11:24  更:2021-08-17 15:12:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 7:02:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码