c++扫雷以及自动扫雷ai性能测试

扫雷部分:

    matrix记录地雷和周边信息,用0表示什么都没有,5表示地雷,1,2,3,4,表示周围地雷的数量
    matrix_probe记录这个地方是否被探索过,0表示没有被探索过,1表示探索过,2表示插旗,认为这里有地雷

#include "stdafx.h"
#include 
#include 
#include 
#include 
#include 
#include 
#define MAXSIZE 100   //棋盘的最大尺寸
#define OK 1
#define ERROR 0


using namespace std;
//栈的数据结构,用于揭开迷雾
typedef struct stack {
	int coord[MAXSIZE][2];   //用于记录坐标数据
	int top,bottom;          //栈底和栈顶
}*st;
//初始化这个栈
void init_stack(st s) {
	for (int i = 0; i < MAXSIZE; i++) {
		for (int j = 0; j < 2; j++) {
			s->coord[i][j] = 0;
		}
	}
	s->top= -1;
	s->bottom = -1;
}
//判断这个栈是否为空,如果为空返回0,不为空返回1
int empty(st s) {
	if (s->top == s->bottom)
		//这是一个空栈
		return 0;
	else
		//这不是一个空栈
		return 1;
}
//入栈
int push(st s,int x,int y) {
	s->top++;
	if (s->top == MAXSIZE) {
		//栈满,不可以在添加元素了
		s->top--;
		cout << "栈已经满了,不可以再将数据入栈";
		return ERROR;
	}
	s->coord[s->top][0] = x;
	s->coord[s->top][1] = y;
	return OK;
}
//出栈
int pop(st s,int b[2]) {
	b[0] = s->coord[s->top][0]; 
	b[1] = s->coord[s->top][1];
	s->top--;
	return OK;
}



/*
	matrix记录地雷和周边信息,用0表示什么都没有,5表示地雷,1,2,3,4,表示周围地雷的数量
	matrix_probe记录这个地方是否被探索过,0表示没有被探索过,1表示探索过,2表示插旗,认为这里有地雷
*/



class MineCleaner {
	int matrix[MAXSIZE][MAXSIZE];    //棋盘
	int matrix_probe[MAXSIZE][MAXSIZE]; //记录是否被探索过
	int size;            //用户指定的棋盘大小
	int density;         //用户指定的地雷的密度
	int score;           //游戏得分,每走一步如果没有踩到雷的话就加一分
public:
	MineCleaner();//构造函数
	MineCleaner(int size, int density);
	~MineCleaner();//析构函数
	void print_blank();   //打印空格,就是每行最开始的空格
	void print_bottom();  //打印每一行的底部
	void print_col_info();//答应列数
	void print_main_info(int x,int y);   //打印棋盘内容,也就是主要的信息
	void print();   //打印棋盘
	void init_chessboard();  //初始化棋盘,也就是埋雷
	void add_score();
	int get_x();            //获取横坐标
	int get_y();			//获取纵坐标
	int get_size();
	int move();         //游戏主界面
	int move(int x, int y);   //重载的AI移动
	void game_over_print();    //游戏结束的界面
	void uncover(int x,int y);      //揭开没有信息的区域
	int if_win();       //判断是否胜利
	void win_print();          //胜利界面
	int get_matrix(int x,int y);
	int get_matrix_probe(int x, int y);
};
//构造函数
MineCleaner::MineCleaner() {
	cout << "请问是否指定棋盘大小,如果不指定大小的话,那么棋盘将会是默认大小10*10?[y/n]  ";
	char a;
	cin >> a;//输入字符,以回车结束
	if (a == 'y' || a == 'Y') {
		//如果回答是yes的话
		cout << "请输入棋盘的大小:";
		int size;
		cin >> size;
		this->size = size + 2;   //这样子棋盘就是从1开始到size-2结束的区域
	}
	else {
		this->size = 12;  //从1开始到10结束的区域
	}
	system("cls");
	cout << "请问是否指定棋盘的地雷密度,如果不指定的话那么将会是默认地雷密度30%?[y/n]  ";
	cin >> a;//输入字符,以回车结束
	if (a == 'y' || a == 'Y') {
		//如果指定地雷密度的话
		cout << "请输入0-100(不包括0和100)之间的地雷密度数,这将影响游戏的难度:";
		int density;
		cin >> density;
		while (density >= 100 || density <= 0) {
			cout << "输入的数据不正确,请重新输入:";
			cin >> density;
		}
		this->density = density;
	}
	else {
		this->density = 30;   //默认的地雷密度为30%
	}
	for (int i = 0; i < size; i++) {
		for (int j = 0; j < size; j++) {
			this->matrix[i][j] = 0;  //初始化棋盘
			this->matrix_probe[i][j] = 0;//一开始都是初始化没有被探索过
		}
	}
	for (int j = 0; j < this->size; j++) {
		//将边框变为可见,为ai做准备
		this->matrix_probe[0][j] = 1;
		this->matrix_probe[this->size - 1][j] = 1;
		this->matrix_probe[j][this->size - 1] = 1;
		this->matrix_probe[j][0] = 1;
	}
	system("cls");
	this->score = 0;//初始化分数
	init_chessboard();//初始化棋盘信息
}
MineCleaner::MineCleaner(int size, int density) {
	this->size = size + 2;
	this->density = density;
	for (int i = 0; i < this->size; i++) {
		for (int j = 0; j < this->size; j++) {
			this->matrix[i][j] = 0;  //初始化棋盘
			this->matrix_probe[i][j] = 0;//一开始都是初始化没有被探索过
		}
	}
	for (int j = 0; j < this->size; j++) {
		//将边框变为可见,为ai做准备
		this->matrix_probe[0][j] = 1;
		this->matrix_probe[this->size - 1][j] = 1;
		this->matrix_probe[j][this->size - 1] = 1;
		this->matrix_probe[j][0] = 1;
	}
	//system("cls");
	this->score = 0;//初始化分数
	init_chessboard();//初始化棋盘信息
}
MineCleaner::~MineCleaner(){}
void MineCleaner::add_score() {
	this->score++;
}
void MineCleaner::print_blank() {
	cout << "                      ";
}
void MineCleaner::print_bottom() {
	cout << endl;
	print_blank();
	cout << "    ";
	for (int i = 1; i < this->size - 1; i++) {
		cout << " ---";
	}
	cout << endl;
}
void MineCleaner::print_col_info() {
	print_blank();
	cout << "     ";
	for (int i = 1; i < this->size - 1; i++) {
		cout <<" "<< i << "  ";
	}
}
void MineCleaner::print_main_info(int x,int y) {
	//先查看matrix_probe数组,如果是0表示没有翻开,什么都不显示,如果是1就显示内容
	//如果是2就插旗子表示有可能有地雷
	if (this->matrix_probe[x][y] == 0) {
		//这表示没有翻开格子
		cout << "   |";
	}
	else if (this->matrix_probe[x][y] == 2) {
		//这表示插旗判断可能有地雷
		cout << " @ |";
	}
	else {
		//如果翻开了就显示这个格子本来的信息
		if (this->matrix[x][y] == 5) {
			cout << " * |";
		}
		else if (this->matrix[x][y] == 0) {
			cout << " 0 |";
		}
		else {
			cout << " " << this->matrix[x][y] << " |";
		}
	}
}
//打印棋盘,调试成功
void MineCleaner::print() {
	cout << endl;
	cout << endl;
	cout << endl;
	print_col_info();
	print_bottom();
	for (int i = 1; i < this->size - 1; i++) {
		//行信息,每一行开始的时候都要空行并且打印行标
		if (i < 10) {
			print_blank();
			cout << i << "   |";
		}
		else {
			print_blank();
			cout << i << "  |";
		}
		for (int j = 1; j < this->size - 1; j++) {
			//列信息
			print_main_info(i,j);
		}
		cout <<"  "<< i;//结束的时候打印行信息
		print_bottom();
	}
	//最后再打印列信息
	print_col_info();
	cout << endl;
	print_blank();
	cout << "当前得分是:" <score;
	cout << endl;
}
int MineCleaner::get_size() {
	return this->size;
}
//初始化棋盘信息
void MineCleaner::init_chessboard() {
	int num_of_total = (this->size-2)*(this->size-2);   //这是显示出来的棋盘的总数量
	float des = (float)this->density / 100;
	int x, y;
	int num_of_mine = des * num_of_total;  //这是地雷的总数
	//埋雷
	while (num_of_mine != 0) {
		//如果地雷数量不为0的话就要继续埋雷
		srand((unsigned)time(NULL));
		x = rand() % (this->size - 1) + 1;  //范围是从1-(size-1)
		y = rand() % (this->size - 1) + 1;
		while (this->matrix[x][y] == 5 || x == this->size-1 || y==this->size-1) {
			//如果这个地方已经有雷了,就重新找一个地方埋雷
			x = rand() % (this->size - 1) + 1;  //范围是从1-(size-1)
			y = rand() % (this->size - 1) + 1;
		}
		this->matrix[x][y] = 5;    //埋雷
		num_of_mine--;
	}
	//接下来要循环遍历整个棋盘的每个坐标,写下雷周围的数字
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix[i][j] != 5) {
				//如果这个坐标不是雷区才进行判断
				if (this->matrix[i - 1][j] == 5) this->matrix[i][j]++;
				if (this->matrix[i + 1][j] == 5) this->matrix[i][j]++;
				if (this->matrix[i][j - 1] == 5) this->matrix[i][j]++;
				if (this->matrix[i][j + 1] == 5) this->matrix[i][j]++;
			}
		}
	}
}
int MineCleaner::get_x() {
	print_blank();
	cout << "请输入要探索的横坐标(行):";
	int x;
	cin >> x;
	while (x < 0 || x > this->size - 1) {
		print_blank();
		cout << "输入的坐标超出允许的范围,请重新输入:";
		cin >> x;
	}
	return x;
}
int MineCleaner::get_y() {
	print_blank();
	cout << "请输入要探索的纵坐标(列):";
	int y;
	cin >> y;
	while (y < 0 || y > this->size - 1) {
		print_blank();
		cout << "输入的坐标超出允许的范围,请重新输入:";
		cin >> y;
	}
	return y;
}
//走棋
int MineCleaner::move() {
	//先接收要走棋的坐标位置
	//system("cls");
	print();
	int x, y;
	x = get_x();
	y = get_y();
	while (this->matrix_probe[x][y] == 1) {
		print_blank();
		cout << "该坐标已被探索过,请重新输入";
		x = get_x();
		y = get_y();
	}
	if (this->matrix[x][y] == 5) {
		game_over_print();
		return 0;
	}
	else if (this->matrix[x][y] == 0) {
		//如果这是个没有信息的区域,则要将一片连续的没有信息的区域都揭开
		uncover(x,y);
		if (if_win()) {
			return 0;
		}
		return 1;
	}
	else {
		this->matrix_probe[x][y] = 1; //表示已经探索过了
		add_score();
		return 1;
	}
}
//0是胜利,-1是失败
int MineCleaner::move(int x, int y) {
	system("cls");
	print();
	if (this->matrix[x][y] == 5) {
		game_over_print();
		return -1;
	}
	else if (this->matrix[x][y] == 0) {
		//如果这是个没有信息的区域,则要将一片连续的没有信息的区域都揭开
		uncover(x, y);
		this->matrix_probe[x][y] = 1;
		if (if_win()) {
			return 0;
		}
		return 1;
	}
	else {
		this->matrix_probe[x][y] = 1; //表示已经探索过了
		add_score();
		if (if_win()) {
			return 0;
		}
		return 1;
	}

}
void MineCleaner::uncover(int x,int y) {
	st s = (stack *)malloc(sizeof(stack));
	init_stack(s);
	int b[2] = { x,y };
	push(s, x,y);//先将当前坐标入栈
	//循环效验当前坐标的上下左右坐标
	while (empty(s)) {
		//cout << "开始循环效验未探索区域"<matrix_probe[b[0] - 1][b[1]] == 0 && this->matrix[b[0] - 1][b[1]] == 0) {
			//cout << "探索上面的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[0] - 1 > 0) {
				//如果不是边上的坐标的话
				this->matrix_probe[b[0] - 1][b[1]] = 1;//表示探索过了
				push(s, b[0] - 1, b[1]);
				add_score();
			}
		}
		//探索下面的格子
		if (this->matrix_probe[b[0] + 1][b[1]] == 0 && this->matrix[b[0] + 1][b[1]] == 0) {
			//cout << "探索下面的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[0] + 1 < this->size-1) {
				//如果不是边上的坐标的话
				this->matrix_probe[b[0] + 1][b[1]] = 1;//表示探索过了
				push(s, b[0] + 1, b[1]);
				add_score();
			}
		}
		//探索左边的坐标
		if (this->matrix_probe[b[0]][b[1] - 1] == 0 && this->matrix[b[0]][b[1] - 1] == 0) {
			//cout << "探索左边的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[1] - 1 > 0) {
				this->matrix_probe[b[0]][b[1] - 1] = 1;//表示探索过了
				push(s, b[0], b[1] - 1);
				add_score();
			}
		}
		//探索右边的坐标
		if (this->matrix_probe[b[0]][b[1] + 1] == 0 && this->matrix[b[0]][b[1] + 1] == 0) {
			//cout << "探索右边的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[1] + 1 < this->size-1) {
				this->matrix_probe[b[0]][b[1] + 1] = 1;//表示探索过了
				push(s, b[0], b[1] + 1);
				add_score();
			}
		}
	}
	//经过循环之后就把连续的没有探索过并且没有坐标的点都探索了
}
void MineCleaner::game_over_print() {
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			this->matrix_probe[i][j] = 1;//全部变成已经探索过了
		}
	}
	system("cls");
	print();
	print_blank();
	cout << "游戏结束,总得分为:" << this->score;
	cout << endl;
	print_blank();
}
//判断是否赢了,赢了返回1,没有赢返回0
int MineCleaner::if_win() {
	//如果所有没有探索过的格子都是地雷的话就赢了
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] == 0) {
				if (this->matrix[i][j] == 5) {
					return 1;
				}
			}
		}
	}
	return 0;
}
void MineCleaner::win_print() {
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			this->matrix_probe[i][j] = 1;//全部变成已经探索过了
		}
	}
	system("cls");
	print_blank();
	cout << "victory!";
	print();
}
int MineCleaner::get_matrix(int x, int y) {
	return this->matrix[x][y];
}
int MineCleaner::get_matrix_probe(int x,int y) {
	return this->matrix_probe[x][y];
}

扫雷的界面:

c++扫雷以及自动扫雷ai性能测试_第1张图片

自动扫雷ai:

概率计算算法描述:
    1.如果扫描到的格子已经是探索过的,就设置概率为0,并且假如周围有的格子没有被探索到,就用格子记录的数去除以还没有探索的格子数然后 加到那个格子是地雷的概率上
    2.如果扫描到的格子没有探索过,那么久跳过这个格子继续扫描别的格子

/*
	概率计算算法描述:
	1.如果扫描到的格子已经是探索过的,就设置概率为0,并且假如周围有的格子没有被探索到,就用格子记录的数去除以还没有探索的格子数
	加到那个格子是地雷的概率上
	2.如果扫描到的格子没有探索过,那么久跳过这个格子继续扫描别的格子
*/


class robot {
	int matrix[MAXSIZE][MAXSIZE];
	int matrix_probe[MAXSIZE][MAXSIZE];
	int matrix_probability[MAXSIZE][MAXSIZE];   //记录每个格子确认为地雷的概率
	int size;
	int go[2];    //这是要走的下一个格子
	int number;   //记录走棋的步数
public:
	robot();
	void set_size(int size);  //获取棋盘的大小
	void set_matrix(int matrix[][MAXSIZE], int matrix_probe[][MAXSIZE]);  //获取棋盘和没有探索过的位置
	void calculate();  //计算每个区域是地雷的可能性,用浮点数表示
	void move();       //扫描概率表,将概率最大的格子的坐标返回
	void first_go();   //第一步随机给出一个坐标点
	int get_y();       //获取列数
	int get_x();	   //获取行数
	void add_num();
	int get_num();
	void run();        //ai的主函数
	void print_pro();  //打印概率表
	void get_matrix();
	void get_matrix_probe();
};
robot::robot() {
	//初始化各个矩阵
	for (int i = 0; i < MAXSIZE; i++) {
		for (int j = 0; j < MAXSIZE; j++) {
			this->matrix[i][j] = 0;
			this->matrix_probe[i][j] = 0;
			this->matrix_probability[i][j] = 0;
		}
	}
	this->go[0] = 6; //一开始走(5,5)
	this->go[1] = 6;
	this->number = 0;
}
void robot::set_size(int size) {
	this->size = size;
}
void robot::set_matrix(int matrix[][MAXSIZE], int matrix_probe[][MAXSIZE]) {
	for (int i = 0; i < this->size; i++) {
		for (int j = 0; j < this->size; j++) {
			//获取棋盘信息和为探索区域的信息
			this->matrix[i][j] = matrix[i][j];
			this->matrix_probe[i][j] = matrix_probe[i][j];
		}
	}
}
void robot::calculate() {
	//先将所有区域的概率变成0
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
				this->matrix_probability[i][j] = 0;
		}
	}
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] != 0) {
				//如果是探索过的格子
				int num=0;//记录四周没有被探索过的格子的数量
				//扫描四周没有被探索过的格子
				if (this->matrix_probe[i - 1][j] == 0) num++;
				if (this->matrix_probe[i + 1][j] == 0) num++;
				if (this->matrix_probe[i][j - 1] == 0) num++;
				if (this->matrix_probe[i][j + 1] == 0) num++;
				if (num != 0) {
					int pro = this->matrix[i][j] * 100 / num;//这是四周没有探索过的格子要加上的的概率
					//cout << "num=" << num << " pro=" << pro << endl;
					if (this->matrix_probe[i - 1][j] == 0) this->matrix_probability[i - 1][j] += pro;
					if (this->matrix_probe[i + 1][j] == 0) this->matrix_probability[i + 1][j] += pro;
					if (this->matrix_probe[i][j - 1] == 0) this->matrix_probability[i][j - 1] += pro;
					if (this->matrix_probe[i][j + 1] == 0) this->matrix_probability[i][j + 1] += pro;
				}
			}
		}
	}
}
void robot::add_num() {
	this->number++;
}
int robot::get_num() {
	return this->number;
}
void robot::move() {
	//如果存在已经探索并且周围地雷为0的点,那么优先探索它周围的点
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] == 1 && this->matrix[i][j] == 0) {
				if (this->matrix_probe[i - 1][j] == 0) {
					this->go[0] = i - 1;
					this->go[1] = j;
					return;
				}
				if (this->matrix_probe[i + 1][j] == 0) {
					this->go[0] = i + 1;
					this->go[1] = j;
					return;
				}
				if (this->matrix_probe[i][j - 1] == 0) {
					this->go[0] = i;
					this->go[1] = j - 1;
					return;
				}
				if (this->matrix_probe[i][j + 1] == 0) {
					this->go[0] = i;
					this->go[1] = j + 1;
					return;
				}
			}
		}
	}
	int pro=1000000;       //记录最大的概率
	/*for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] == 0 && this->matrix_probability[i][j] != 0) {
				pro = this->matrix_probability[i][j];
				break;
			}
		}
	}*/
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probability[i][j] < pro && this->matrix_probe[i][j] == 0 && this->matrix_probability[i][j]!=0) {
				//只要概率比较小并且是没有探索过的就更换目标
				//cout << "进入更换坐标的函数" << endl;
				this->go[0] = i;
				this->go[1] = j;
				pro = this->matrix_probability[i][j];
			}
		}
	}
}
void robot::first_go() {
	/*srand((unsigned)time(NULL));
	int x, y;
	x = rand() % (this->size - 1) + 1;
	y = rand() % (this->size - 1) + 1;
	while (x+2 >= this->size - 1 || y+1 >= this->size - 1) {
		x = rand() % (this->size - 1) + 1;
		y = rand() % (this->size - 1) + 1;
	}
	this->go[0] = x;
	this->go[1] = y;*/
}
int robot::get_y() {
	return this->go[1];
}
int robot::get_x() {
	return this->go[0];
}
void robot::run() {
	calculate();
	move();
}
void robot::print_pro() {
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			cout << this->matrix_probability[i][j]<<" ";
		}
		cout << endl;
	}
}
void robot::get_matrix() {
	cout << "棋盘表:" << endl;
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			cout << this->matrix[i][j];
		}
		cout << endl;
	}
}
void robot::get_matrix_probe() {
	cout << "未探索表:" << endl;
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			cout << this->matrix_probe[i][j];
		}
		cout << endl;
	}
}

void get_matrix(MineCleaner m,int matrix[][MAXSIZE],int matrix_probe[][MAXSIZE]) {
	for (int i = 0; i < m.get_size(); i++) {
		for (int j = 0; j < m.get_size(); j++) {
			matrix[i][j] = m.get_matrix(i, j);
			matrix_probe[i][j] = m.get_matrix_probe(i, j);
		}
	}
}

最后的测试代码段:

int main()
{
	int num_of_victory=0;         //记录胜利的次数
	int victory[1000] = {0};    //记录胜利的盘数,胜利用1表示
	int number = 1000;
	int a[9] = {0};
	for (int j = 10; j < 100; j += 10) {
		num_of_victory = 0;
		for (int i = 0; i < number; i++) {
			MineCleaner m(10, j);
			robot r;
			int a;
			int matrix[MAXSIZE][MAXSIZE], matrix_probe[MAXSIZE][MAXSIZE];
			get_matrix(m, matrix, matrix_probe);
			r.set_matrix(matrix, matrix_probe);
			r.set_size(m.get_size());
			a = m.move(r.get_x(), r.get_y());
			while (a == 1) {
				r.add_num();
				//Sleep(500);
				//system("pause");
				get_matrix(m, matrix, matrix_probe);
				r.set_matrix(matrix, matrix_probe);
				cout << endl;
				m.print_blank();
				cout << "上一步的横纵坐标为:" << r.get_x() << " " << r.get_y() << endl;
				m.print_blank();
				cout << "当前步数为:" << r.get_num() << endl;
				r.run();
				//system("pause");
				a = m.move(r.get_x(), r.get_y());
			}
			//system("pause");
			if (a == 0) {
				num_of_victory++;
				victory[i]++;
			}
		}
		system("cls");
		cout << endl;
		cout << "胜利次数为:" << num_of_victory << endl;
		a[j / 10 - 1] = num_of_victory;//记录不同地雷密度的胜率
	}
	system("cls");
	for (int i = 0; i < 9 ; i++) {
		cout << "测试基数为1000,地雷密度为" << (i + 1) * 10 << "%的时候的胜利次数为:" << a[i] << endl;
	}
}

ai性能测试结果:

测试使用的是10*10的棋盘,除了地雷密度是0%和100%的情况,其他9种情况每一种都进行了1000次试验测试

c++扫雷以及自动扫雷ai性能测试_第2张图片

测试结果数据源:

c++扫雷以及自动扫雷ai性能测试_第3张图片

最后是完整的代码:

#include "stdafx.h"
#include 
#include 
#include 
#include 
#include 
#include 
#define MAXSIZE 100   //棋盘的最大尺寸
#define OK 1
#define ERROR 0


using namespace std;
//栈的数据结构,用于揭开迷雾
typedef struct stack {
	int coord[MAXSIZE][2];   //用于记录坐标数据
	int top,bottom;          //栈底和栈顶
}*st;
//初始化这个栈
void init_stack(st s) {
	for (int i = 0; i < MAXSIZE; i++) {
		for (int j = 0; j < 2; j++) {
			s->coord[i][j] = 0;
		}
	}
	s->top= -1;
	s->bottom = -1;
}
//判断这个栈是否为空,如果为空返回0,不为空返回1
int empty(st s) {
	if (s->top == s->bottom)
		//这是一个空栈
		return 0;
	else
		//这不是一个空栈
		return 1;
}
//入栈
int push(st s,int x,int y) {
	s->top++;
	if (s->top == MAXSIZE) {
		//栈满,不可以在添加元素了
		s->top--;
		cout << "栈已经满了,不可以再将数据入栈";
		return ERROR;
	}
	s->coord[s->top][0] = x;
	s->coord[s->top][1] = y;
	return OK;
}
//出栈
int pop(st s,int b[2]) {
	b[0] = s->coord[s->top][0]; 
	b[1] = s->coord[s->top][1];
	s->top--;
	return OK;
}



/*
	matrix记录地雷和周边信息,用0表示什么都没有,5表示地雷,1,2,3,4,表示周围地雷的数量
	matrix_probe记录这个地方是否被探索过,0表示没有被探索过,1表示探索过,2表示插旗,认为这里有地雷
*/



class MineCleaner {
	int matrix[MAXSIZE][MAXSIZE];    //棋盘
	int matrix_probe[MAXSIZE][MAXSIZE]; //记录是否被探索过
	int size;            //用户指定的棋盘大小
	int density;         //用户指定的地雷的密度
	int score;           //游戏得分,每走一步如果没有踩到雷的话就加一分
public:
	MineCleaner();//构造函数
	MineCleaner(int size, int density);
	~MineCleaner();//析构函数
	void print_blank();   //打印空格,就是每行最开始的空格
	void print_bottom();  //打印每一行的底部
	void print_col_info();//答应列数
	void print_main_info(int x,int y);   //打印棋盘内容,也就是主要的信息
	void print();   //打印棋盘
	void init_chessboard();  //初始化棋盘,也就是埋雷
	void add_score();
	int get_x();            //获取横坐标
	int get_y();			//获取纵坐标
	int get_size();
	int move();         //游戏主界面
	int move(int x, int y);   //重载的AI移动
	void game_over_print();    //游戏结束的界面
	void uncover(int x,int y);      //揭开没有信息的区域
	int if_win();       //判断是否胜利
	void win_print();          //胜利界面
	int get_matrix(int x,int y);
	int get_matrix_probe(int x, int y);
};
//构造函数
MineCleaner::MineCleaner() {
	cout << "请问是否指定棋盘大小,如果不指定大小的话,那么棋盘将会是默认大小10*10?[y/n]  ";
	char a;
	cin >> a;//输入字符,以回车结束
	if (a == 'y' || a == 'Y') {
		//如果回答是yes的话
		cout << "请输入棋盘的大小:";
		int size;
		cin >> size;
		this->size = size + 2;   //这样子棋盘就是从1开始到size-2结束的区域
	}
	else {
		this->size = 12;  //从1开始到10结束的区域
	}
	system("cls");
	cout << "请问是否指定棋盘的地雷密度,如果不指定的话那么将会是默认地雷密度30%?[y/n]  ";
	cin >> a;//输入字符,以回车结束
	if (a == 'y' || a == 'Y') {
		//如果指定地雷密度的话
		cout << "请输入0-100(不包括0和100)之间的地雷密度数,这将影响游戏的难度:";
		int density;
		cin >> density;
		while (density >= 100 || density <= 0) {
			cout << "输入的数据不正确,请重新输入:";
			cin >> density;
		}
		this->density = density;
	}
	else {
		this->density = 30;   //默认的地雷密度为30%
	}
	for (int i = 0; i < size; i++) {
		for (int j = 0; j < size; j++) {
			this->matrix[i][j] = 0;  //初始化棋盘
			this->matrix_probe[i][j] = 0;//一开始都是初始化没有被探索过
		}
	}
	for (int j = 0; j < this->size; j++) {
		//将边框变为可见,为ai做准备
		this->matrix_probe[0][j] = 1;
		this->matrix_probe[this->size - 1][j] = 1;
		this->matrix_probe[j][this->size - 1] = 1;
		this->matrix_probe[j][0] = 1;
	}
	system("cls");
	this->score = 0;//初始化分数
	init_chessboard();//初始化棋盘信息
}
MineCleaner::MineCleaner(int size, int density) {
	this->size = size + 2;
	this->density = density;
	for (int i = 0; i < this->size; i++) {
		for (int j = 0; j < this->size; j++) {
			this->matrix[i][j] = 0;  //初始化棋盘
			this->matrix_probe[i][j] = 0;//一开始都是初始化没有被探索过
		}
	}
	for (int j = 0; j < this->size; j++) {
		//将边框变为可见,为ai做准备
		this->matrix_probe[0][j] = 1;
		this->matrix_probe[this->size - 1][j] = 1;
		this->matrix_probe[j][this->size - 1] = 1;
		this->matrix_probe[j][0] = 1;
	}
	//system("cls");
	this->score = 0;//初始化分数
	init_chessboard();//初始化棋盘信息
}
MineCleaner::~MineCleaner(){}
void MineCleaner::add_score() {
	this->score++;
}
void MineCleaner::print_blank() {
	cout << "                      ";
}
void MineCleaner::print_bottom() {
	cout << endl;
	print_blank();
	cout << "    ";
	for (int i = 1; i < this->size - 1; i++) {
		cout << " ---";
	}
	cout << endl;
}
void MineCleaner::print_col_info() {
	print_blank();
	cout << "     ";
	for (int i = 1; i < this->size - 1; i++) {
		cout <<" "<< i << "  ";
	}
}
void MineCleaner::print_main_info(int x,int y) {
	//先查看matrix_probe数组,如果是0表示没有翻开,什么都不显示,如果是1就显示内容
	//如果是2就插旗子表示有可能有地雷
	if (this->matrix_probe[x][y] == 0) {
		//这表示没有翻开格子
		cout << "   |";
	}
	else if (this->matrix_probe[x][y] == 2) {
		//这表示插旗判断可能有地雷
		cout << " @ |";
	}
	else {
		//如果翻开了就显示这个格子本来的信息
		if (this->matrix[x][y] == 5) {
			cout << " * |";
		}
		else if (this->matrix[x][y] == 0) {
			cout << " 0 |";
		}
		else {
			cout << " " << this->matrix[x][y] << " |";
		}
	}
}
//打印棋盘,调试成功
void MineCleaner::print() {
	cout << endl;
	cout << endl;
	cout << endl;
	print_col_info();
	print_bottom();
	for (int i = 1; i < this->size - 1; i++) {
		//行信息,每一行开始的时候都要空行并且打印行标
		if (i < 10) {
			print_blank();
			cout << i << "   |";
		}
		else {
			print_blank();
			cout << i << "  |";
		}
		for (int j = 1; j < this->size - 1; j++) {
			//列信息
			print_main_info(i,j);
		}
		cout <<"  "<< i;//结束的时候打印行信息
		print_bottom();
	}
	//最后再打印列信息
	print_col_info();
	cout << endl;
	print_blank();
	cout << "当前得分是:" <score;
	cout << endl;
}
int MineCleaner::get_size() {
	return this->size;
}
//初始化棋盘信息
void MineCleaner::init_chessboard() {
	int num_of_total = (this->size-2)*(this->size-2);   //这是显示出来的棋盘的总数量
	float des = (float)this->density / 100;
	int x, y;
	int num_of_mine = des * num_of_total;  //这是地雷的总数
	//埋雷
	while (num_of_mine != 0) {
		//如果地雷数量不为0的话就要继续埋雷
		srand((unsigned)time(NULL));
		x = rand() % (this->size - 1) + 1;  //范围是从1-(size-1)
		y = rand() % (this->size - 1) + 1;
		while (this->matrix[x][y] == 5 || x == this->size-1 || y==this->size-1) {
			//如果这个地方已经有雷了,就重新找一个地方埋雷
			x = rand() % (this->size - 1) + 1;  //范围是从1-(size-1)
			y = rand() % (this->size - 1) + 1;
		}
		this->matrix[x][y] = 5;    //埋雷
		num_of_mine--;
	}
	//接下来要循环遍历整个棋盘的每个坐标,写下雷周围的数字
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix[i][j] != 5) {
				//如果这个坐标不是雷区才进行判断
				if (this->matrix[i - 1][j] == 5) this->matrix[i][j]++;
				if (this->matrix[i + 1][j] == 5) this->matrix[i][j]++;
				if (this->matrix[i][j - 1] == 5) this->matrix[i][j]++;
				if (this->matrix[i][j + 1] == 5) this->matrix[i][j]++;
			}
		}
	}
}
int MineCleaner::get_x() {
	print_blank();
	cout << "请输入要探索的横坐标(行):";
	int x;
	cin >> x;
	while (x < 0 || x > this->size - 1) {
		print_blank();
		cout << "输入的坐标超出允许的范围,请重新输入:";
		cin >> x;
	}
	return x;
}
int MineCleaner::get_y() {
	print_blank();
	cout << "请输入要探索的纵坐标(列):";
	int y;
	cin >> y;
	while (y < 0 || y > this->size - 1) {
		print_blank();
		cout << "输入的坐标超出允许的范围,请重新输入:";
		cin >> y;
	}
	return y;
}
//走棋
int MineCleaner::move() {
	//先接收要走棋的坐标位置
	//system("cls");
	print();
	int x, y;
	x = get_x();
	y = get_y();
	while (this->matrix_probe[x][y] == 1) {
		print_blank();
		cout << "该坐标已被探索过,请重新输入";
		x = get_x();
		y = get_y();
	}
	if (this->matrix[x][y] == 5) {
		game_over_print();
		return 0;
	}
	else if (this->matrix[x][y] == 0) {
		//如果这是个没有信息的区域,则要将一片连续的没有信息的区域都揭开
		uncover(x,y);
		if (if_win()) {
			return 0;
		}
		return 1;
	}
	else {
		this->matrix_probe[x][y] = 1; //表示已经探索过了
		add_score();
		return 1;
	}
}
//0是胜利,-1是失败
int MineCleaner::move(int x, int y) {
	system("cls");
	print();
	if (this->matrix[x][y] == 5) {
		game_over_print();
		return -1;
	}
	else if (this->matrix[x][y] == 0) {
		//如果这是个没有信息的区域,则要将一片连续的没有信息的区域都揭开
		uncover(x, y);
		this->matrix_probe[x][y] = 1;
		if (if_win()) {
			return 0;
		}
		return 1;
	}
	else {
		this->matrix_probe[x][y] = 1; //表示已经探索过了
		add_score();
		if (if_win()) {
			return 0;
		}
		return 1;
	}

}
void MineCleaner::uncover(int x,int y) {
	st s = (stack *)malloc(sizeof(stack));
	init_stack(s);
	int b[2] = { x,y };
	push(s, x,y);//先将当前坐标入栈
	//循环效验当前坐标的上下左右坐标
	while (empty(s)) {
		//cout << "开始循环效验未探索区域"<matrix_probe[b[0] - 1][b[1]] == 0 && this->matrix[b[0] - 1][b[1]] == 0) {
			//cout << "探索上面的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[0] - 1 > 0) {
				//如果不是边上的坐标的话
				this->matrix_probe[b[0] - 1][b[1]] = 1;//表示探索过了
				push(s, b[0] - 1, b[1]);
				add_score();
			}
		}
		//探索下面的格子
		if (this->matrix_probe[b[0] + 1][b[1]] == 0 && this->matrix[b[0] + 1][b[1]] == 0) {
			//cout << "探索下面的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[0] + 1 < this->size-1) {
				//如果不是边上的坐标的话
				this->matrix_probe[b[0] + 1][b[1]] = 1;//表示探索过了
				push(s, b[0] + 1, b[1]);
				add_score();
			}
		}
		//探索左边的坐标
		if (this->matrix_probe[b[0]][b[1] - 1] == 0 && this->matrix[b[0]][b[1] - 1] == 0) {
			//cout << "探索左边的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[1] - 1 > 0) {
				this->matrix_probe[b[0]][b[1] - 1] = 1;//表示探索过了
				push(s, b[0], b[1] - 1);
				add_score();
			}
		}
		//探索右边的坐标
		if (this->matrix_probe[b[0]][b[1] + 1] == 0 && this->matrix[b[0]][b[1] + 1] == 0) {
			//cout << "探索右边的格子,这个格子也是0" << endl;
			//如果没有探索并且是没有信息的坐标的话
			if (b[1] + 1 < this->size-1) {
				this->matrix_probe[b[0]][b[1] + 1] = 1;//表示探索过了
				push(s, b[0], b[1] + 1);
				add_score();
			}
		}
	}
	//经过循环之后就把连续的没有探索过并且没有坐标的点都探索了
}
void MineCleaner::game_over_print() {
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			this->matrix_probe[i][j] = 1;//全部变成已经探索过了
		}
	}
	system("cls");
	print();
	print_blank();
	cout << "游戏结束,总得分为:" << this->score;
	cout << endl;
	print_blank();
}
//判断是否赢了,赢了返回1,没有赢返回0
int MineCleaner::if_win() {
	//如果所有没有探索过的格子都是地雷的话就赢了
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] == 0) {
				if (this->matrix[i][j] == 5) {
					return 1;
				}
			}
		}
	}
	return 0;
}
void MineCleaner::win_print() {
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			this->matrix_probe[i][j] = 1;//全部变成已经探索过了
		}
	}
	system("cls");
	print_blank();
	cout << "victory!";
	print();
}
int MineCleaner::get_matrix(int x, int y) {
	return this->matrix[x][y];
}
int MineCleaner::get_matrix_probe(int x,int y) {
	return this->matrix_probe[x][y];
}

/*
	概率计算算法描述:
	1.如果扫描到的格子已经是探索过的,就设置概率为0,并且假如周围有的格子没有被探索到,就用格子记录的数去除以还没有探索的格子数
	加到那个格子是地雷的概率上
	2.如果扫描到的格子没有探索过,那么久跳过这个格子继续扫描别的格子
*/


class robot {
	int matrix[MAXSIZE][MAXSIZE];
	int matrix_probe[MAXSIZE][MAXSIZE];
	int matrix_probability[MAXSIZE][MAXSIZE];   //记录每个格子确认为地雷的概率
	int size;
	int go[2];    //这是要走的下一个格子
	int number;   //记录走棋的步数
public:
	robot();
	void set_size(int size);  //获取棋盘的大小
	void set_matrix(int matrix[][MAXSIZE], int matrix_probe[][MAXSIZE]);  //获取棋盘和没有探索过的位置
	void calculate();  //计算每个区域是地雷的可能性,用浮点数表示
	void move();       //扫描概率表,将概率最大的格子的坐标返回
	void first_go();   //第一步随机给出一个坐标点
	int get_y();       //获取列数
	int get_x();	   //获取行数
	void add_num();
	int get_num();
	void run();        //ai的主函数
	void print_pro();  //打印概率表
	void get_matrix();
	void get_matrix_probe();
};
robot::robot() {
	//初始化各个矩阵
	for (int i = 0; i < MAXSIZE; i++) {
		for (int j = 0; j < MAXSIZE; j++) {
			this->matrix[i][j] = 0;
			this->matrix_probe[i][j] = 0;
			this->matrix_probability[i][j] = 0;
		}
	}
	this->go[0] = 6; //一开始走(5,5)
	this->go[1] = 6;
	this->number = 0;
}
void robot::set_size(int size) {
	this->size = size;
}
void robot::set_matrix(int matrix[][MAXSIZE], int matrix_probe[][MAXSIZE]) {
	for (int i = 0; i < this->size; i++) {
		for (int j = 0; j < this->size; j++) {
			//获取棋盘信息和为探索区域的信息
			this->matrix[i][j] = matrix[i][j];
			this->matrix_probe[i][j] = matrix_probe[i][j];
		}
	}
}
void robot::calculate() {
	//先将所有区域的概率变成0
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
				this->matrix_probability[i][j] = 0;
		}
	}
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] != 0) {
				//如果是探索过的格子
				int num=0;//记录四周没有被探索过的格子的数量
				//扫描四周没有被探索过的格子
				if (this->matrix_probe[i - 1][j] == 0) num++;
				if (this->matrix_probe[i + 1][j] == 0) num++;
				if (this->matrix_probe[i][j - 1] == 0) num++;
				if (this->matrix_probe[i][j + 1] == 0) num++;
				if (num != 0) {
					int pro = this->matrix[i][j] * 100 / num;//这是四周没有探索过的格子要加上的的概率
					//cout << "num=" << num << " pro=" << pro << endl;
					if (this->matrix_probe[i - 1][j] == 0) this->matrix_probability[i - 1][j] += pro;
					if (this->matrix_probe[i + 1][j] == 0) this->matrix_probability[i + 1][j] += pro;
					if (this->matrix_probe[i][j - 1] == 0) this->matrix_probability[i][j - 1] += pro;
					if (this->matrix_probe[i][j + 1] == 0) this->matrix_probability[i][j + 1] += pro;
				}
			}
		}
	}
}
void robot::add_num() {
	this->number++;
}
int robot::get_num() {
	return this->number;
}
void robot::move() {
	//如果存在已经探索并且周围地雷为0的点,那么优先探索它周围的点
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] == 1 && this->matrix[i][j] == 0) {
				if (this->matrix_probe[i - 1][j] == 0) {
					this->go[0] = i - 1;
					this->go[1] = j;
					return;
				}
				if (this->matrix_probe[i + 1][j] == 0) {
					this->go[0] = i + 1;
					this->go[1] = j;
					return;
				}
				if (this->matrix_probe[i][j - 1] == 0) {
					this->go[0] = i;
					this->go[1] = j - 1;
					return;
				}
				if (this->matrix_probe[i][j + 1] == 0) {
					this->go[0] = i;
					this->go[1] = j + 1;
					return;
				}
			}
		}
	}
	int pro=1000000;       //记录最大的概率
	/*for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probe[i][j] == 0 && this->matrix_probability[i][j] != 0) {
				pro = this->matrix_probability[i][j];
				break;
			}
		}
	}*/
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			if (this->matrix_probability[i][j] < pro && this->matrix_probe[i][j] == 0 && this->matrix_probability[i][j]!=0) {
				//只要概率比较小并且是没有探索过的就更换目标
				//cout << "进入更换坐标的函数" << endl;
				this->go[0] = i;
				this->go[1] = j;
				pro = this->matrix_probability[i][j];
			}
		}
	}
}
void robot::first_go() {
	/*srand((unsigned)time(NULL));
	int x, y;
	x = rand() % (this->size - 1) + 1;
	y = rand() % (this->size - 1) + 1;
	while (x+2 >= this->size - 1 || y+1 >= this->size - 1) {
		x = rand() % (this->size - 1) + 1;
		y = rand() % (this->size - 1) + 1;
	}
	this->go[0] = x;
	this->go[1] = y;*/
}
int robot::get_y() {
	return this->go[1];
}
int robot::get_x() {
	return this->go[0];
}
void robot::run() {
	calculate();
	move();
}
void robot::print_pro() {
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			cout << this->matrix_probability[i][j]<<" ";
		}
		cout << endl;
	}
}
void robot::get_matrix() {
	cout << "棋盘表:" << endl;
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			cout << this->matrix[i][j];
		}
		cout << endl;
	}
}
void robot::get_matrix_probe() {
	cout << "未探索表:" << endl;
	for (int i = 1; i < this->size - 1; i++) {
		for (int j = 1; j < this->size - 1; j++) {
			cout << this->matrix_probe[i][j];
		}
		cout << endl;
	}
}

void get_matrix(MineCleaner m,int matrix[][MAXSIZE],int matrix_probe[][MAXSIZE]) {
	for (int i = 0; i < m.get_size(); i++) {
		for (int j = 0; j < m.get_size(); j++) {
			matrix[i][j] = m.get_matrix(i, j);
			matrix_probe[i][j] = m.get_matrix_probe(i, j);
		}
	}
}


int main()
{
	int num_of_victory=0;         //记录胜利的次数
	int victory[1000] = {0};    //记录胜利的盘数,胜利用1表示
	int number = 1000;
	int a[9] = {0};
	for (int j = 10; j < 100; j += 10) {
		num_of_victory = 0;
		for (int i = 0; i < number; i++) {
			MineCleaner m(10, j);
			robot r;
			int a;
			int matrix[MAXSIZE][MAXSIZE], matrix_probe[MAXSIZE][MAXSIZE];
			get_matrix(m, matrix, matrix_probe);
			r.set_matrix(matrix, matrix_probe);
			r.set_size(m.get_size());
			a = m.move(r.get_x(), r.get_y());
			while (a == 1) {
				r.add_num();
				//Sleep(500);
				//system("pause");
				get_matrix(m, matrix, matrix_probe);
				r.set_matrix(matrix, matrix_probe);
				cout << endl;
				m.print_blank();
				cout << "上一步的横纵坐标为:" << r.get_x() << " " << r.get_y() << endl;
				m.print_blank();
				cout << "当前步数为:" << r.get_num() << endl;
				r.run();
				//system("pause");
				a = m.move(r.get_x(), r.get_y());
			}
			//system("pause");
			if (a == 0) {
				num_of_victory++;
				victory[i]++;
			}
		}
		system("cls");
		cout << endl;
		cout << "胜利次数为:" << num_of_victory << endl;
		a[j / 10 - 1] = num_of_victory;//记录不同地雷密度的胜率
	}
	system("cls");
	for (int i = 0; i < 9 ; i++) {
		cout << "测试基数为1000,地雷密度为" << (i + 1) * 10 << "%的时候的胜利次数为:" << a[i] << endl;
	}
}

 

你可能感兴趣的:(项目,小游戏)