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];
}
扫雷的界面:
自动扫雷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次试验测试
测试结果数据源:
最后是完整的代码:
#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;
}
}
上一篇: 苹果AI秀——Core ML强势来袭
下一篇: 利用人工智能进行网页设计的10种方法
推荐阅读