计算机软件实习 项目三:A*算法迷宫自动寻路

1.A*算法

A*算法

A*算法通过下面这个函数来计算每个节点的优先级。

其中:

  • f(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  • g(n) 是节点n距离起点的代价。
  • h(n)是节点n距离终点的预计代价,这也就是A*算法的启发函数。关于启发函数我们在下面详细讲解。

A*算法在运算过程中,每次从优先队列中选取f(n)值最小(优先级最高)的节点作为下一个待遍历的节点。

2.迷宫的生成算法

首先,将地图分割成若干相互隔离的小格,将起点设置成扩展状态,然后从起点开始,向外进行扩展,同时使用随机函数选择一个可行的墙面进行打通,这样就构成了迷宫的地图

算法实现:

A*算法

这里f代表从起点开始到当前位置走过的步数,h表示当前节点到终点的曼哈顿距离,这样,总估价就是g=f+h,通过C++ stl中的优先队列进行维护;

bool flag=false;
QPair<int,int> pre[200][200];
int mpback[200][200];
struct node{
    int xnow,ynow,f,h,g;
    node(){
        xnow=ynow=f=h=g=0;
    }
    bool operator < (const node& tmp)const{
        return g<tmp.g;
    }
    node(int x,int y,int ft,int ht,int gt){
        xnow=x,ynow=y,f=ft,h=ht,g=gt;
    }
};
void mazeWidget::on_Astar_btn_clicked() {
    std::priority_queue<node> q;
    this->setFocus();
    if(flag)return;
    memset(pre,0,sizeof(pre));
    map->map[map->p_x][map->p_y]=5;
    memcpy(mpback,map->map,sizeof(map->map));
    q.push(node(map->p_x,map->p_y,0,0,0));
    while(q.size()){
        node tmpdata=q.top();
        q.pop();
        for(int i=0;i<4;i++){
            int dx=tmpdata.xnow+dir[i][0],dy=tmpdata.ynow+dir[i][1];
            if(mpback[dx][dy]==0||mpback[dx][dy]==-1||mpback[dx][dy]==9)continue;
            pre[dx][dy]=qMakePair(tmpdata.xnow,tmpdata.ynow);
            if(mpback[dx][dy]==6){
                flag=true;
                break;
            }
            mpback[dx][dy]=9;
            node topush;
            topush.xnow=dx,topush.ynow=dy;
            topush.f=tmpdata.f+1;
            topush.h=manha(dx,dy);
            topush.g=topush.h+topush.f;
            q.push(topush);
        }
        if(flag) break;
    }
    int tmpx=map->start_x,tmpy=map->start_y;
    while(map->map[tmpx][tmpy]!=5){
        map->map[tmpx][tmpy]=5;
        int a=pre[tmpx][tmpy].first,b=pre[tmpx][tmpy].second;
        tmpx=a,tmpy=b;
    }
    map->map[map->start_x][map->start_y]=6;
    repaint();
    return;
}

运行结果:

在起点进行自动寻路

在移动过程中进行A*寻路

可以设置难度:

完整源代码:

maze.h

#ifndef MAZE_H
#define MAZE_H
#include <iostream>
/**************************************************
 * -1:边界 0:墙壁
 * 1:空单元(初始化后未判定的单元)
 * 2:待定墙壁(新建已定单元时建立)
 * 3:已定单元(确定迷宫树已延伸到的单元)
 * 4:打通墙壁(延伸操作)
 * 5.起点
 * 6.终点
 * 7.已经过路径
 * **************************************************/

class maze {
private:
    int para; 
    void base();
    void expandfunc(int i, int j);
    void start() ;
    bool judge();
    void judgement();
    void operation(int i, int j);
    void randomopt();
public:
    maze(int in_level);
    ~maze();
    //获取地图
    int start_x, start_y;   
    int getlevel() ;
    int map[200][200]; 
    int** getmap();
    int getside();
    void makemap();
    int p_x, p_y; 
    void rebuildmap();
    int* operator[](int index);
};
#endif // MAZE_H

maze.cpp

#include "maze.h"
void maze::base() {
    for (int i = 0; i < para * 2 + 1; i++) {
        for (int j = 0; j < para * 2 + 1; j++) {
            if (i == 0 || j == 0 || i == para * 2 + 1 - 1 ||
                j == para * 2 + 1 - 1) {
                map[i][j] = -1;
            } else if (i % 2 != 0 && j % 2 != 0) {
                map[i][j] = 1;
            } else {
                map[i][j] = 0;
            }
        }
    }
}

void maze:: expandfunc(int i, int j) {
    if (map[i - 1][j] == 0) {
        map[i - 1][j] = 2;
    }
    if (map[i + 1][j] == 0) {
        map[i + 1][j] = 2;
    }
    if (map[i][j - 1] == 0) {
        map[i][j - 1] = 2;
    }
    if (map[i][j + 1] == 0) {
        map[i][j + 1] = 2;
    }
}

void maze:: start() {
    map[start_x][start_y] = 5;
    expandfunc(start_x, start_y);
}

bool maze:: judge() {
    bool flag = 0;
    for (int i = 0; i < para * 2 + 1; i++) {
        for (int j = 0; j < para * 2 + 1; j++) {
            if (map[i][j] == 2) {
                flag = 1;
                return flag;
            }
        }
    }
    return flag;
}

void maze:: operation(int i, int j) {
    if ((map[i - 1][j] == 3 || map[i - 1][j] == 5) && map[i + 1][j] == 1) {
        map[i][j] = 4;
        map[i + 1][j] = 3;
        expandfunc(i + 1, j);
        start_x = i + 1;
        start_y = j;
    } else if ((map[i][j - 1] == 3 || map[i][j - 1] == 5) &&
               map[i][j + 1] == 1) {
        map[i][j] = 4;
        map[i][j + 1] = 3;
        expandfunc(i, j + 1);
        start_x = i;
        start_y = j + 1;
    } else if ((map[i + 1][j] == 3 || map[i + 1][j] == 5) &&
               map[i - 1][j] == 1) {
        map[i][j] = 4;
        map[i - 1][j] = 3;
        expandfunc(i - 1, j);
        start_x = i - 1;
        start_y = j;
    } else if ((map[i][j + 1] == 3 || map[i][j + 1] == 5) &&
               map[i][j - 1] == 1) {
        map[i][j] = 4;
        map[i][j - 1] = 3;
        expandfunc(i, j - 1);
        start_x = i;
        start_y = j - 1;
    } else {
        map[i][j] = 0;
    }
}
void maze::judgement(){
    if (!judge()) {
        map[start_x][start_y] = 6;
    }
    return;
}
void maze:: randomopt() {
    int t = 0;
    for (int i = 0; i < para * 2 + 1; i++) {
        for (int j = 0; j < para * 2 + 1; j++) {
            if (map[i][j] == 2) {
                t++;
            }
        }
    }
    int k = rand() % t + 1;
    t = 0;
    for (int i = 0; i < para * 2 + 1; i++) {
        for (int j = 0; j < para * 2 + 1; j++) {
            if (map[i][j] == 2) {
                t++;
                if (t == k) {
                    operation(i, j);
                    judgement();
                    return;
                }
            }
        }
    }

}
maze:: maze(int in_level):para(in_level) {
    start_x = 1, start_y = 1;
}
maze::~maze() {
}
int maze:: getlevel() {
    return maze::para;
}
int maze::getside() {
    return para * 2 + 1;
}
void maze:: makemap() {
    p_x = start_x;
    p_y = start_y;
    base();
    start();
    int a = 0;
    while (judge()) {
        a++;
        randomopt();
    }
}
int* maze::operator[](int index) {
    return map[index];
}
void maze::rebuildmap() {
    start_x = 1;
    start_y = 1;
    makemap();
}

mazewidget.h

#ifndef MAZEWIDGET_H
#define MAZEWIDGET_H

#include <QWidget>
#include <QMessageBox>   
#include <QPainter>        
#include <QDebug>           
#include <QKeyEvent>      
#include <QTimer>          
#include <QInputDialog>     
#include "maze.h"           

QT_BEGIN_NAMESPACE

namespace Ui {
class mazeWidget;
}
QT_END_NAMESPACE
class mazeWidget : public QWidget {
    Q_OBJECT
public:
    mazeWidget(QWidget* parent = nullptr);
    int manha(int x,int y);
    void print();
    ~mazeWidget();
protected:
    void paintEvent(QPaintEvent*);        
    void keyPressEvent(QKeyEvent*);       
private slots:
    void on_start_btn_clicked();    
    void on_stop_ptn_clicked();    
    void on_end_btn_clicked();      
    void on_setting_btn_clicked();  
    void on_Astar_btn_clicked();   
    void time_update();             
private:
    Ui::mazeWidget* ui;         
    maze* map;                  

    bool painting_switch;      
    bool timing_switch;         
    bool keybord_switch;       
    bool stop_switch;          

    int grade;                  
    int time;                   

    QTimer* timer;
};
#endif // MAZEWIDGET_H

 mazewidget.cpp

#include "mazewidget.h"
#include "ui_mazewidget.h"
#include <QVector>
#include <QThread>
#include <QPair>
#include <bits/stdc++.h>
#include <iostream>
int dir[4][2]={
    {0,-1},
    {0,1},
    {-1,0},
    {1,0}
};
bool flag=false;
QPair<int,int> pre[200][200];
int mpback[200][200];
struct node{
    int xnow,ynow,f,h,g;
    node(){
        xnow=ynow=f=h=g=0;
    }
    bool operator < (const node& tmp)const{
        return g>tmp.g;
    }
    node(int x,int y,int ft,int ht,int gt){
        xnow=x,ynow=y,f=ft,h=ht,g=gt;
    }
};
mazeWidget::mazeWidget(QWidget* parent)
    : QWidget(parent)
    , ui(new Ui::mazeWidget), map(new maze(20)), painting_switch(false), timing_switch(false)
    , keybord_switch(false), grade(0), time(0) {
    ui->setupUi(this);
    ui->progressBar->setVisible(false);
    ui->end_btn->setEnabled(false);
    ui->stop_ptn->setEnabled(false);
    ui->grade_value->setText("  ");
    ui->time_value->setText("  ");
    map->makemap();
    timer=new QTimer(this);
    connect(timer, &QTimer::timeout, this, &mazeWidget::time_update);
}
mazeWidget::~mazeWidget() {
    delete ui;
    delete map;
    delete timer;
}
void mazeWidget::paintEvent(QPaintEvent*) {
    if(!painting_switch) return;
    QPainter painter(this);     //画笔对象
    //绘图逻辑:
    int perblock = (std::min(ui->frame->width(), ui->frame->height()) - 20) / (map->getside());
    int start_x = ui->frame->x() + (ui->frame->width() - (ui->frame->x() + (map->getside()) * perblock)) / 2;
    int strat_y = ui->frame->y() + (ui->frame->height() - (ui->frame->y() + (map->getside()) * perblock)) / 2;
    for(int i = 0; i < map->getlevel() * 2 + 1; i++) {
        for(int j = 0; j < map->getlevel() * 2 + 1; j++) {
            if(i == map->p_x && j == map->p_y) {
                painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::red));
            } else if(map->map[i][j] == 7) {
                painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::yellow));
            } else if(map->map[i][j] == 3 || map->map[i][j] == 4) {
                painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::gray));
            } else if(map->map[i][j] == 5) {
                painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::blue));
            } else if(map->map[i][j] == 6) {
                painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::green));
            } else {
                painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::black));
            }
        }
    }
}
void mazeWidget::keyPressEvent(QKeyEvent* event) {
    if(!keybord_switch) return;
    int x = map->p_x;
    int y = map->p_y;
    map->map[x][y]=7;
    if(event->key() == Qt::Key_Up || event->key() == Qt::Key_W) {
        if((*map)[x][y - 1] == 3 || (*map)[x][y - 1] == 4 || (*map)[x][y - 1] == 5 || (*map)[x][y - 1] == 6 || (*map)[x][y - 1] == 7) {
            map->p_y--;
        }
    } else if(event->key() == Qt::Key_Down || event->key() == Qt::Key_S) {
        if((*map)[x][y + 1] == 3 || (*map)[x][y + 1] == 4 || (*map)[x][y + 1] == 5 || (*map)[x][y + 1] == 6 || (*map)[x][y + 1] == 7) {
            map->p_y++;
        }
    } else if(event->key() == Qt::Key_Left || event->key() == Qt::Key_A) {
        if((*map)[x - 1][y] == 3 || (*map)[x - 1][y] == 4 || (*map)[x - 1][y] == 5 || (*map)[x - 1][y] == 6 || (*map)[x - 1][y] == 7) {
            map->p_x--;
        }
    } else if(event->key() == Qt::Key_Right || event->key() == Qt::Key_D) {
        if((*map)[x + 1][y] == 3 || (*map)[x + 1][y] == 4 || (*map)[x + 1][y] == 5 || (*map)[x + 1][y] == 6 || (*map)[x + 1][y] == 7) {
            map->p_x++;
        }
    }
    if((*map)[map->p_x][map->p_y] != 5 && (*map)[map->p_x][map->p_y] != 6)(*map)[map->p_x][map->p_y] = 7;
    repaint();
    if((*map)[map->p_x][map->p_y] == 6) {
        map->makemap();
        repaint();
        flag=false;
        grade += pow(map->getlevel(), 2);
        ui->grade_value->setText(QString::number(grade));
    }
    map->map[map->p_x][map->p_y]=5;
}
void mazeWidget::time_update() {
    if(time != 0) {
        time--;
        ui->time_value->setText(QString::number(time));
        ui->progressBar->setValue(time / 2);
    } else {
        timer->stop();
        ui->progressBar->setVisible(false);
        keybord_switch = false;
        painting_switch = false;
        timing_switch = false;
        repaint();
        ui->start_btn->setEnabled(true);
        ui->time_value->setText(" ");
        ui->grade_value->setText(" ");
        ui->stop_ptn->setEnabled(false);
        ui->end_btn->setEnabled(false);
        ui->setting_btn->setEnabled(true);
        QMessageBox outgrade(QMessageBox::NoIcon, "恭喜", "您得分:" + QString::number(grade), QMessageBox::Ok);
        outgrade.exec();
        grade = 0;
    }
}
void mazeWidget::on_start_btn_clicked() {
    painting_switch = true;
    timing_switch = true;
    keybord_switch = true;
    flag=false;
    time = 200;
    this->setFocus();
    timer->start(1000);
    ui->progressBar->setVisible(true);
    ui->progressBar->setValue(100);
    ui->Astar_btn->setEnabled(true);
    repaint();
    ui->time_value->setText(QString::number(time));
    ui->grade_value->setText(QString::number(grade));
    ui->start_btn->setEnabled(false);
    ui->stop_ptn->setEnabled(true);
    ui->end_btn->setEnabled(true);
    ui->setting_btn->setEnabled(false);

}
void mazeWidget::on_stop_ptn_clicked() {
    this->setFocus();
    if(stop_switch) {
        timing_switch = false;
        keybord_switch = false;
        timer->stop();
        ui->stop_ptn->setText("继续");
        stop_switch = false;
    } else {
        timing_switch = true;
        keybord_switch = true;
        timer->start();
        ui->stop_ptn->setText("暂停");
        stop_switch = true;
    }
}
void mazeWidget::on_end_btn_clicked() {
    timing_switch = false;
    painting_switch = false;
    keybord_switch = false;
    stop_switch = false;
    timer->stop();
    this->setFocus();
    time = 0;
    grade = 0;
    ui->progressBar->setVisible(false);
    ui->grade_value->setText(" ");
    ui->time_value->setText(" ");
    ui->stop_ptn->setText("暂停");
    ui->stop_ptn->setEnabled(false);
    ui->end_btn->setEnabled(false);
    ui->start_btn->setEnabled(true);
    ui->setting_btn->setEnabled(true);
    ui->Astar_btn->setEnabled(false);
    map->rebuildmap();
    repaint();
}

void mazeWidget::on_setting_btn_clicked() {
    QStringList difficultys;
    this->setFocus();
    difficultys << tr("简单") << tr("普通") << tr("中等") << tr("困难");
    QString difficulty = QInputDialog::getItem(this, tr("选择难度"),
                         tr("请选择一个条目"), difficultys, 0, false);
    if(difficulty == tr("简单")) {
        delete map;
        map = new maze(5);
        map->makemap();
    } else if(difficulty == tr("普通")) {
        delete map;
        map = new maze(10);
        map->makemap();
    } else if(difficulty == tr("中等")) {
        delete map;
        map = new maze(20);
        map->makemap();
    } else if(difficulty == tr("困难")) {
        delete map;
        map = new maze(40);
        map->makemap();
    }
}
int mazeWidget::manha(int x,int y){
    return abs(x-map->start_x)+abs(y-map->start_y);
}
void mazeWidget::on_Astar_btn_clicked() {
    std::priority_queue<node> q;
    this->setFocus();
    if(flag)return;
    int len=map->getlevel();
    for(int i=0;i<=2*len;i++){
        for(int j=0;j<=2*len;j++){
            if(map->map[i][j]==7)
                map->map[i][j]=4;
        }
    }
    memset(pre,0,sizeof(pre));
    map->map[map->p_x][map->p_y]=5;
    memcpy(mpback,map->map,sizeof(map->map));
    q.push(node(map->p_x,map->p_y,0,0,0));
    while(q.size()){
        node tmpdata=q.top();
        q.pop();
        for(int i=0;i<4;i++){
            int dx=tmpdata.xnow+dir[i][0],dy=tmpdata.ynow+dir[i][1];
            if(mpback[dx][dy]==0||mpback[dx][dy]==-1||mpback[dx][dy]==9)continue;
            pre[dx][dy]=qMakePair(tmpdata.xnow,tmpdata.ynow);
            if(mpback[dx][dy]==6){
                flag=true;
                break;
            }
            mpback[dx][dy]=9;
            node topush;
            topush.xnow=dx,topush.ynow=dy;
            topush.f=tmpdata.f+1;
            topush.h=manha(dx,dy);
            topush.g=topush.h+topush.f;
            q.push(topush);
        }
        if(flag) break;
    }
    int tmpx=map->start_x,tmpy=map->start_y;
    while(map->map[tmpx][tmpy]!=5){
        map->map[tmpx][tmpy]=5;
        int a=pre[tmpx][tmpy].first,b=pre[tmpx][tmpy].second;
        tmpx=a,tmpy=b;
    }
    map->map[map->start_x][map->start_y]=6;
    repaint();
    return;
}

main.cpp

#include "mazewidget.h"     
#include "maze.h"         
#include <QApplication>
#include <QStyleFactory>    

int main(int argc, char* argv[]) {
    srand(time(NULL));                              
    QApplication a(argc, argv);
    a.setStyle(QStyleFactory::create("Fusion"));    
    mazeWidget w;
    w.show();
    return a.exec();
}

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值