欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-07 14:01:49
...

扫雷部分:

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

#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#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 << "当前得分是:" <<this->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 << "开始循环效验未探索区域"<<endl;
		//如果栈不空的话,就弹栈然后循环验证周围是否有为0的坐标
		pop(s, b);//出栈,这个时候b[0]为xb[1]为y
		//探索上面的格子
		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 > 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性能测试

自动扫雷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性能测试

测试结果数据源:

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

最后是完整的代码:

#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#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 << "当前得分是:" <<this->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 << "开始循环效验未探索区域"<<endl;
		//如果栈不空的话,就弹栈然后循环验证周围是否有为0的坐标
		pop(s, b);//出栈,这个时候b[0]为xb[1]为y
		//探索上面的格子
		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 > 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;
	}
}

 

相关标签: c 扫雷 ai