一,问题描述
1,问题描述
迷宫实验是取自心理学的一个古典实验。在该实验中,把一只老鼠从一个无顶大盒子的门放入,在盒中设置了许多墙,对行进方向形成了多处阻挡。盒子仅有一个出口,在出口处放置一块奶酪,吸引老鼠在迷宫中寻找道路以到达出口。对同一只老鼠重复进行上述实验,一直到老鼠从入口到出口,而不走错一步。老鼠经多次试验终于得到它学习走迷宫的路线。
2,设计功能要求
迷宫由m行n列的二维数组设置,0表示无障碍,1表示有障碍。设入口为(1,1),出口为(m,n),每次只能从一个无障碍单元移到周围四个方向上任一无障碍单元。编程实现对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
算法输入:代表迷宫入口的坐标
算法输出:穿过迷宫的结果。
算法要点:创建迷宫,试探法查找路。
二,设计思路
1,迷宫的创建
如何保存?
使用二维数组进行保存,0代表路,1代表墙,2代表起点,3代表终点
如何创建?
1,手动创建:手动输入二维数组进行创建
2,自动创建:通过使用自然分岔型1进行创建
2,如何寻路
BFS广度优先搜索
通过创建栈进行寻路
DFS深度优先搜索
通过创建队列进行寻路
3,如何展示及想要实现的效果
使用Qt创建可视化的迷宫界面
可直接在迷宫界面对迷宫进行修改
将迷宫的通路可视化显示在迷宫界面上,并且有动画效果
4.A*算法
A算法,A(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。
算法中的距离估算值与实际值越接近,最终搜索速度越快。选择路径中经过哪个方格的关键是下面这个等式:F = G + H。
其中,f是从初始状态经由状态n到目标状态的代价估计,g是在状态空间中从初始状态到状态n的实际代价,h是从状态n到目标状态的最佳路径的估计代价。(对于路径搜索问题,状态就是图中的节点,代价就是距离)
h的选取:保证找到最短路径(最优解的)条件,关键在于估价函数f的选取(或者说h的选取)。
以d(n)表达状态n到目标状态的距离,那么h(n)的选取大致有如下三种情况:
① 如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
② 如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
③如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
三、代码实现
1、定义主要函数
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPainter>
#include <QDebug>
#include <QStack>
#include <QMessagebox>
#include <QKeyEvent>
#include <QEvent>
#include <QTime>
#include <QProcess>
//方块大小
#define xsize 20
#define ysize 20
//行列
#define H 29
#define W 29
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent *event);
void keyPressEvent(QKeyEvent *event);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
//起点位置
int x=xsize*2,y=ysize*2;
//当前坐标
int row=1,col=1;
public:
QPainter painter;
QPen pen;
void initmaze();
void Astart(int r,int c);
}; #endif // WIDGET_H
2、随机生成迷宫,画迷宫,按键控制走向,A*算法求解迷宫。
#include "widget.h"
#include "ui_widget.h"
#include <iostream>
using namespace std;
Widget::Widget(QWidget *parent):QWidget(parent),ui(new Ui::Widget)
{
ui->setupUi(this);
//设置不可缩放,大小就是当前大小
this->setFixedSize(this->size());
qDebug()<<this->size();
}
Widget::~Widget()
{
delete ui;
}
//1,2代表墙,0代表类似树的节点。破墙赋4,访问过赋4
int MAP[H][W];
int isFind[H+2][W+2];
struct Point {
int row;
int col;
};
QStack<Point> load;
void Widget::initmaze()
{
Point curP = { 2,2 };//辅组地图上的起始点
QStack<Point> ms;
ms.push(curP);
int direction = 0;
qsrand(QTime::currentTime().msec());
while (1) {
//结束循环条件:遍历完了
int n = 0;
for (int i = 0; i < H+2; i++) {
for (int j = 0; j < W+2; j++) {
if (isFind[i][j] == 0)n++;
}
}
if (n == 0)break;
direction = qrand()%4;
//退栈条件是走到死胡同
//死胡同是上下左右都走过了
if (isFind[curP.row - 2][curP.col] == 1 && isFind[curP.row][curP.col - 2] == 1 &&
isFind[curP.row + 2][curP.col] == 1 && isFind[curP.row][curP.col + 2] == 1)
{
if(!ms.empty())ms.pop();
if(ms.empty()){
qDebug()<<"生成失败";
}
curP = ms.top();
}
if (direction == 0 && isFind[curP.row - 2][curP.col] == 0) {
MAP[curP.row - 1 - 1][curP.col - 1] = 0;//打破墙,原地图比isfind地图小1
isFind[curP.row - 2][curP.col] = 1;//记录走过
curP.row -= 2;
ms.push(curP);
}
else if (direction == 1 && isFind[curP.row][curP.col - 2] == 0) {
MAP[curP.row - 1][curP.col - 1 - 1] = 0;
isFind[curP.row][curP.col - 2] = 1;
curP.col -= 2;
ms.push(curP);
}
else if (direction == 2 && isFind[curP.row + 2][curP.col] == 0) {
MAP[curP.row + 1 - 1][curP.col - 1] = 0;
isFind[curP.row + 2][curP.col] = 1;
curP.row += 2;
ms.push(curP);
}
else if (direction == 3 && isFind[curP.row][curP.col + 2] == 0) {
MAP[curP.row - 1][curP.col + 1 - 1] = 0;
isFind[curP.row][curP.col + 2] = 1;
curP.col += 2;
ms.push(curP);
}
}
}
void Widget::paintEvent(QPaintEvent *event)
{
painter.begin(this);
//设置间距
pen.setWidth(xsize);
for(int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{
if(MAP[i][j]==1)
{
pen.setColor(Qt::black);
painter.setPen(pen);
painter.drawPoint((j+1)*xsize,(i+1)*ysize);
}
//类似节点
if(MAP[i][j]==0)
{
pen.setColor(Qt::white);
painter.setPen(pen);
painter.drawPoint((j+1)*xsize,(i+1)*ysize);
}
}
}
pen.setColor(Qt::red);
painter.setPen(pen);
painter.drawPoint((col+1)*xsize,(row+1)*ysize);
pen.setColor(Qt::green);
painter.setPen(pen);
painter.drawPoint((H-1)*xsize,(W-1)*ysize);
//画自动走的路径
while(!load.empty())
{
Point temp = load.top();
qDebug() << "row = " << temp.row << " col = " << temp.col << endl;
pen.setColor(Qt::yellow);
painter.setPen(pen);
painter.drawPoint((temp.col+1)*xsize,(temp.row+1)*ysize);
load.pop();
}
painter.end();
}
void Widget::keyPressEvent(QKeyEvent *event)
{
//点击按键会给direction赋值,但如果是向上走就不能往下 上下左右0123
switch (event->key())
{
case Qt::Key_W:
if(row*ysize>0&&MAP[row-1][col]==0)
{
row--;
}
break;
case Qt::Key_S:
if((row+2)*ysize<=ysize*H&&MAP[row+1][col]==0)
{
row++;
}
break;
case Qt::Key_A:
if(col*xsize>0&&MAP[row][col-1]==0)
{
col--;
}
break;
case Qt::Key_D://x=(col+1)*xsize
if((col+2)*xsize<=ysize*W&&MAP[row][col+1]==0)
{
col++;
}
break;
default:
break;
}
update();
//走到终点
if(row==H-2&&col==W-2)
{
//创建 QMessageBox 类对象
QMessageBox MyBox(QMessageBox::Question,"提示","你已走出迷宫,是否重新开始?",QMessageBox::Yes|QMessageBox::No);
//使 MyBox 对话框显示
int rec=MyBox.exec();
if (rec==QMessageBox::Yes) {
QProcess::startDetached(qApp->applicationFilePath(), QStringList());
}
else if (rec==QMessageBox::No) {
this->close();
}
}
}
void Widget::on_pushButton_clicked()
{
row=1;
col=1;
//初始化MAP迷宫
for (int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{
if(i%2==1&&j%2==1)
{
MAP[i][j]=0;
continue;
}
MAP[i][j]=1;
}
}
//初始化辅助数组
for (int i=0;i<H+2;i++)
{
for(int j=0;j<W+2;j++)
{
isFind[i][j]=1;
}
}
for (int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{
isFind[i+1][j+1]=MAP[i][j];
}
}
initmaze();
update();
}
void Widget::Astart(int r,int c)
{
while(!load.empty())load.pop();
//初始化辅助数组
int arr[H][W];
for (int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{
arr[i][j]=0;
}
}
Point beginPoint = { r,c };//自定义起点
Point endPoint = { H-2,W-2 };//自定义终点
load.push(beginPoint);
Point currentPoint = beginPoint;//当前走到的位置
arr[currentPoint.row][currentPoint.col] = 1;
while (1)//while循环一直走下去,一直走到终点
{
//下
if (MAP[currentPoint.row + 1][currentPoint.col] == 0 &&
arr[currentPoint.row + 1][currentPoint.col] == 0)//可以向下走的条件
{
arr[currentPoint.row + 1][currentPoint.col] = 1;
currentPoint.row = currentPoint.row + 1;
load.push(currentPoint);
}
//右
else if (MAP[currentPoint.row][currentPoint.col+1] == 0 &&
arr[currentPoint.row][currentPoint.col+1] == 0)//可以向右走的条件
{
arr[currentPoint.row][currentPoint.col + 1] = 1;
currentPoint.col = currentPoint.col + 1;
load.push(currentPoint);
}
//上
else if (MAP[currentPoint.row - 1][currentPoint.col] == 0 &&
arr[currentPoint.row - 1][currentPoint.col] == 0)//可以向上走的条件
{
arr[currentPoint.row - 1][currentPoint.col] = 1;
currentPoint.row = currentPoint.row - 1;
load.push(currentPoint);
}
//左
else if (MAP[currentPoint.row][currentPoint.col-1] == 0 &&
arr[currentPoint.row][currentPoint.col-1] == 0)//可以向左走的条件
{
arr[currentPoint.row][currentPoint.col - 1] = 1;
currentPoint.col = currentPoint.col - 1;
load.push(currentPoint);
}
else//死胡同,即上下左右都无法走
{
load.pop();
currentPoint = load.top();
}
if (currentPoint.row == endPoint.row && currentPoint.col == endPoint.col)
{
qDebug() << "找到终点!路径回退:" << endl;
break;
}
if (load.empty())
{
qDebug() << "找不到路!" << endl;
break;
}
}
/*while (!load.empty())//打印路径
{
Point temp = load.top();
qDebug() << "row = " << temp.row << " col = " << temp.col << endl;
load.pop();
}*/
}
void Widget::on_pushButton_2_clicked()
{
Astart(row,col);
update();
}
3、主函数实现
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
4、效果图