计算机软件技术实习项目三 基于A*算法的迷宫游戏开发

一,问题描述
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、效果图

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值