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

目录

项目需求

项目准备

项目框架

关键算法分析

prim算法生成迷宫

 A*算法自动寻址

 详细设计

开始界面设计

迷宫的生成

A*算法寻址

项目总结


项目需求

  1. 迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
  2. 要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘 方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示;
  3. 设计交互友好的游戏图形界面。

项目准备

  1. 仔细思考分析项目需求,生成项目的总体框架;
  2. 解决如何生成迷宫、起点终点的设置、自动寻址等问题,学习相关算法;
  3. 构造合适的界面UI;

项目框架

关键算法分析

prim算法生成迷宫

通过如下方法将迷宫生成问题和求最小生成树建立联系:

              把迷宫看做这个样子,左边是用类似数组表示的迷宫,把墙简化后比较容易看

 

  1. 这是迷宫初始化的样子,所有墙壁都是封死的,路径格处于独立状态。如果把迷宫看作带权图G,则点集V即4为简化图中显示的9个路径格,边集E则是任意相邻两路径格间连线的集合,可以看作所有路径权值都相等。
  2. 设迷宫入口为左上角的路径格,出口为右下角的路径格,则生成一个迷宫的实质就是找G的一个最小生成树T,T包含所有路径格,不成环,且其中任意两个路径格连通。(事实上当不必连通所有格,只要连通了起点和终点,也可以算迷宫生成完毕,只是这样的迷宫不完整)
  3. 把起点加入空集A,则森林Ga中包含9棵根节点状态的树,接下来要不断在E中找安全边,使Ga中的根节点状态的树元素都加入到集合A中,直到Ga中只剩A一个元素(或者Ga中某树包含起点和终点)为止。

算法思想

设N=(VE)是连通网,TE是N上最小生成树中边的集合。初始令U={U0},(U0∈V),TE={ }。在所有u∈U,v∈V-U的边(u,v)∈E中,找一条代价最小的边(u0,v0)将(u0,v0)并入集合TE同时v0并入U0重复上述操作直至U=V为止,则T=(V.TE)为N的最小生成树。

 A*算法自动寻址

1. 把起点加入 open list 。

2. 重复如下过程:

        1)遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。

        2)把这个节点移到 close list 。

        3) 对当前方格的 8 个相邻方格的每一个方格:

                        ◆如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。

                        ◆如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。

                        ◆如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。如果你的 open list 是按 F 值排序的话,改变后你可能需要重新排序。

        4)停止,当你

                ◆把终点加入到了 open list 中,此时路径已经找到了;

                ◆查找终点失败,并且 open list 是空的,此时没有路径。

3.保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。

 详细设计

 开始界面设计

 加入了弹跳动画和延时进入下一场景的功能


    //开始按钮
    MyPushButton * startBtn = new MyPushButton(":img/img/playbutton.png");
    startBtn->setParent(this);
    startBtn->setStyleSheet("border:0px");
    startBtn->move(this->width() * 0.5 - startBtn->width() * 0.5 , this->height() * 0.35);

    connect(startBtn,&MyPushButton::clicked,[=](){
        qDebug() << "点击了开始";
        //弹起的特效
        startBtn->zoom1();
        startBtn->zoom2();
        //延时进入到选择场景
        QTimer::singleShot(200,this,[=](){
            //自身隐藏
            this->hide();
            //显示选择场景
             mymaze.show();
            });
        });
void MyPushButton:: zoom1(){

    //创建动态对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");

    //设置动画时间间隔
    animation->setDuration(200);

    //起始位置
    animation -> setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));

    //结束位置
    animation -> setEndValue(QRect(this->x(),this->y()+10,this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();

}

void MyPushButton:: zoom2(){
    //创建动态对象
    QPropertyAnimation * animation = new QPropertyAnimation(this,"geometry");

    //设置动画时间间隔
    animation->setDuration(200);

    //起始位置
    animation -> setStartValue(QRect(this->x(),this->y()+10,this->width(),this->height()));

    //结束位置
    animation -> setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));

    //设置弹跳曲线
    animation->setEasingCurve(QEasingCurve::OutBounce);

    //执行动画
    animation->start();

}

迷宫的生成

void play_maze::on_creat_maze_clicked()//创建迷宫
{
    if(flag_click){
        return;
    }
    if(flag_get_row){
        maze_row=pre_maze_row;
    }
    else{
        maze_row=10;
        //qDebug()<<"hhhh";
    }
    if(flag_get_col){
        maze_col=pre_maze_col;
    }
    else{
        maze_col=10;
    }
    //qDebug()<<maze_row<<" "<<maze_col<<endl;
    flag_success=false;

    //获取行列值
    control_X=1;
    control_Y=1;
    target_X=maze_row-3;
    target_Y=maze_col-3;
    //qDebug()<<target_X<<" "<<target_Y<<endl;
    //    maze=new point *[maze_row];
    //    for(int i=0;i<maze_row;i++){
    //        maze[i]=new point[maze_col];
    //    }

    for(int i=0;i<maze_row;i++){
        for(int j=0;j<maze_col;j++){
            maze[i][j].i=i;
            maze[i][j].j=j;
            maze[i][j].state=0;
        }
    }
    int max;
    if(maze_row>=maze_col)
        max=maze_row;
    else
        max=maze_col;

    maze_cell_size=1080/max;

    startTimer(5*650/max);
    //qDebug()<<"get row and col"<<endl;
    build_maze_stack.clear();

    int i=3,j=3;
    maze[i][j].state=1;

    point temp;
    temp.i=i;
    temp.j=j;
    temp.state=1;
    bool up=false,down=false,left=false,right=false;

    srand((unsigned)time(NULL));

    while(true){                                    //利用prim算法生成迷宫
        temp.i=i;
        temp.j=j;

        int randnum=rand()%4;

        switch (randnum) {
        case 0: if(!up&&i>2&&maze[i-2][j].state==0){
                build_maze_stack.push(temp);
                maze[i-2][j].state=1;
                maze[i-1][j].state=1;
                i=i-2;
                if(maze[i-1][j].state==0)
                    up=false;
                else
                    up=true;
                if(maze[i+1][j].state==0)
                    down=false;
                else
                    down=true;
                if(maze[i][j-1].state==0)
                    left=false;
                else
                    left=true;
                if(maze[i][j+1].state==0)
                    right=false;
                else
                    right=true;
            }
            else{
                up=true;
            }
            break;
        case 1: if(!down&&i<maze_row-3&&maze[i+2][j].state==0)
            {
                build_maze_stack.push(temp);
                maze[i+2][j].state=1;
                maze[i+1][j].state=1;
                i=i+2;
                if(maze[i-1][j].state==0)
                    up=false;
                else
                    up=true;
                if(maze[i+1][j].state==0)
                    down=false;
                else
                    down=true;
                if(maze[i][j-1].state==0)
                    left=false;
                else
                    left=true;
                if(maze[i][j+1].state==0)
                    right=false;
                else
                    right=true;
            }
            else{
                down=true;
            }
            break;
        case 2: if(!left&&j>2&&maze[i][j-2].state==0)
            {
                build_maze_stack.push(temp);
                maze[i][j-2].state=1;
                maze[i][j-1].state=1;
                j=j-2;
                if(maze[i-1][j].state==0)
                    up=false;
                else
                    up=true;
                if(maze[i+1][j].state==0)
                    down=false;
                else
                    down=true;
                if(maze[i][j-1].state==0)
                    left=false;
                else
                    left=true;
                if(maze[i][j+1].state==0)
                    right=false;
                else
                    right=true;
            }
            else{
                left=true;
            }
            break;
        case 3: if(!right&&j<maze_col-3&&maze[i][j+2].state==0)
            {
                build_maze_stack.push(temp);
                maze[i][j+2].state=1;
                maze[i][j+1].state=1;
                j=j+2;
                if(maze[i-1][j].state==0)
                    up=false;
                else
                    up=true;
                if(maze[i+1][j].state==0)
                    down=false;
                else
                    down=true;
                if(maze[i][j-1].state==0)
                    left=false;
                else
                    left=true;
                if(maze[i][j+1].state==0)
                    right=false;
                else
                    right=true;
            }
            else{
                right=true;
            }
            break;
        }
        if(up&&down&&left&&right){
            if(!build_maze_stack.isEmpty()){
                i=build_maze_stack.top().i;
                j=build_maze_stack.top().j;
                build_maze_stack.pop();
                if(maze[i-1][j].state==0)
                    up=false;
                else
                    up=true;
                if(maze[i+1][j].state==0)
                    down=false;
                else
                    down=true;
                if(maze[i][j-1].state==0)
                    left=false;
                else
                    left=true;
                if(maze[i][j+1].state==0)
                    right=false;
                else
                    right=true;
            }
            else{
                maze[1][1].state=2;
                maze[maze_row-3][maze_col-3].state=3;
                creat_maze=true;
                for(int i=0; i<maze_row; i++)//这一段是防止生成迷宫后依旧显示路线
                    for(int j=0; j<maze_col; j++){
                        path[i][j].state=0;//在这里的状态表示父节点,1,2,3,4分别表示从上下左右发展过来
                        path[i][j].i=i;
                        path[i][j].j=j;
                    }
                //qDebug()<<"creat maze successfully!"<<endl;
                return;
            }
        }
    }
}

A*算法寻址

void play_maze::on_A_search_clicked()//A*算法
{
    if(flag_click||flag_success){
        return;
    }
    if(!creat_maze){
        return;
    }
    A_search.clear();
    qDebug()<<"begin find path";
    ftime.start();
    for(int i=0; i<maze_row; i++)
        for(int j=0; j<maze_col; j++){
            path[i][j].state=0;//在这里的状态表示父节点,1,2,3,4分别表示从上下左右发展过来
            path[i][j].i=i;
            path[i][j].j=j;
        }
    //用于记录该点是否搜索的矩阵
    for(int i=0; i<maze_row; i++)
        for(int j=0; j<maze_col; j++){
            if(maze[i][j].state==0)
                graph[i][j]=1;
            else
                graph[i][j]=0;          //初始化未被搜索
        }
    QString message;
    int searchnum=0;//搜索节点次数
    point top;
    top.i=control_X;
    top.j=control_Y;
    top.state=abs(control_X-target_X)+abs(control_Y-target_Y);
    A_search.push_back(top);//这里的状态什么也不表示
    graph[top.i][top.j]=0;

    while(!A_search.isEmpty()){
        qSort(A_search.begin(),A_search.end(),cmp);
        top=A_search.front();
        //qDebug()<<top.i<<" "<<top.j<<" "<<top.state<<endl;
        A_search.pop_front();
        if(graph[top.i][top.j]==0){
            searchnum+=1;
            if(maze[top.i][top.j].state==3){
                QString s=QString::number(ftime.elapsed(),10);
                message="花费时间:"+s+"ms";
                QMessageBox::information(NULL, " ", message, QMessageBox::Yes, QMessageBox::Yes);
                break;
            }
            //将此点标记为访问过
            //并将真实路径代价计算进去,用graph存储
            switch (path[top.i][top.j].state) {
            case 1:
                graph[top.i][top.j]=graph[top.i-1][top.j]+1;
                break;
            case 2:
                graph[top.i][top.j]=graph[top.i+1][top.j]+1;
                break;
            case 3:
                graph[top.i][top.j]=graph[top.i][top.j-1]+1;
                break;
            case 4:
                graph[top.i][top.j]=graph[top.i][top.j+1]+1;
                break;
            default:
                graph[top.i][top.j]=1;
                break;
            }
            //将未访问的子节点放入开节点表
            if((graph[top.i+1][top.j]==0)&&(maze[top.i+1][top.j].state!=0)){
                A_search.push_back(point(top.i+1,top.j,(graph[top.i][top.j]+1+abs(top.i+1-target_X)+abs(top.j-target_Y))));
                path[top.i+1][top.j].state=1;

            }
            if((graph[top.i-1][top.j]==0)&&(maze[top.i-1][top.j].state!=0)){
                A_search.push_back(point(top.i-1,top.j,(graph[top.i][top.j]+1+abs(top.i-1-target_X)+abs(top.j-target_Y))));
                path[top.i-1][top.j].state=2;

            }
            if((graph[top.i][top.j+1]==0)&&(maze[top.i][top.j+1].state!=0)){
                A_search.push_back(point(top.i,top.j+1,(graph[top.i][top.j]+1+abs(top.i-target_X)+abs(top.j+1-target_Y))));
                path[top.i][top.j+1].state=3;

            }
            if((graph[top.i][top.j-1]==0)&&(maze[top.i][top.j-1].state!=0)){
                A_search.push_back(point(top.i,top.j-1,(graph[top.i][top.j]+1+abs(top.i-target_X)+abs(top.j-1-target_Y))));
                path[top.i][top.j-1].state=4;

            }
        }
    }
    qDebug()<<"find the path!"<<endl;
}

项目总结

 在本实验项目中,关键在于迷宫的生成和迷宫的自动寻址,两者均可以有多种算法实现,但都具有一定难度,老师讲解后对prim算法和A*算法有了理解,再加之上网查阅算法的实现,勉强得到了应用,我也知道自己水平仍需提高,此迷宫也可以有很多更有趣的元素,但都因为自己编程不会,无法应用出来;本此项目的开始界面我应用了一定的动画,也发现自己可能更喜欢前端开发,以后会向这方面靠拢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值