A*算法迷宫学习

一、实验环境

QT

C++

二、实验内容

1)迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;

2) 要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。

3) 要求迷宫游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统提示迷宫路径要求基于A*算法实现,输出玩家当前位置到迷宫出口的最优路径。设计交互友好的游戏图形界面。

三、代码实现

    迷宫游戏的设计分两个重点,一个是迷宫的生成,还有一个是自动寻路操作的实现。

迷宫生成

    迷宫的生成有很多方法:PRIM算法、深度优先算法······这里我主要使用了递推的算法。

/**************************************************
 * -1:边界 0:墙壁
 * 1:空单元(初始化后未判定的单元)
 * 2:待定墙壁(新建已定单元时建立)
 * 3:已定单元(确定迷宫树已延伸到的单元)
 * 4:打通墙壁(延伸操作)
 * 5.起点
 * 6.终点
 * 7.已经过路径
 * **************************************************/
void maze:: op(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;
        _2(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;
        _2(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;
        _2(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;
        _2(i, j - 1);
        start_x = i;
        start_y = j - 1;
    } else {
        map[i][j] = 0;
    }
}

    初始化迷宫地图后可以用该段代码实现打通墙壁生成路径。全部完成之后还要判断地图是否还有待定的方格(即数值为2的方格)。

A*算法

定义一个结构体Node,其中有五个整型参数和两个函数。

struct Node {
    int x1,y1,f,h,g;
        Node(){
            x1=y1=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){
            x1=x,y1=y,f=ft,h=ht,g=gt;
        }


};

    其中x1和y1是当前位置四周方格的坐标,f是中间节点到终点的代价,h是中间节点到终点的预估代价,g为二者之和。比较运算符 'operator <' 重载了'<'运算符,可以比较两个Node对象的'g'成员。如果'g'成员小于另一个Node对象的'g'成员,那么该Node对象就被视为小于另一个Node对象。

void mazeWidget::on_pushButton_clicked()
{
  std::priority_queue<Node> q;
  this->setFocus();
  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.x1+dir[i][0],dy=tmpdata.y1+dir[i][1];
          if(mpback[dx][dy]==0||mpback[dx][dy]==-1||mpback[dx][dy]==9)continue;
          pre[dx][dy]=qMakePair(tmpdata.x1,tmpdata.y1);
          if(mpback[dx][dy]==6){
              break;
          }
          mpback[dx][dy]=9;
          Node topush;
          topush.x1=dx,topush.y1=dy;
          topush.f=tmpdata.f+1;
          topush.h=heuristic(dx,dy);
          topush.g=topush.h+topush.f;
          q.push(topush);
      }
  }
  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*算法:

  1. 创建一个优先队列q,其中元素类型为NodeNode类型应该包含代表迷宫中的点的信息,以及可能还有一些额外的信息,例如该节点的父节点等。

  2. 将当前焦点设置为该控件。

  3. 清空一个名为pre的二维数组,这个数组可能用来保存迷宫的生成过程或者路径。

  4. 在一个二维数组map->map中,将当前位置(map->p_xmap->p_y)标记为5,这可能代表该位置为障碍物或者特殊标记。

  5. map->map的内容复制到mpback中,可能是为了保存一份迷宫的备份。

  6. 将起点位置(map->p_xmap->p_y)以及一些其他信息(例如代价等)封装为一个Node对象并加入到优先队列q中。

  7. 通过while循环和for循环,不断地从优先队列q中取出代价最小的节点(根据g值来比较,g值为[0, 1, 2, 3]),并对该节点的四个方向进行遍历。

    • )如果该方向是可行的(即不是障碍物或者已经访问过),就更新该位置的代价(pre值),并将其加入到优先队列中。
    • )如果该方向是终点(即代价为6),就跳出循环。
  8. 当所有的节点都被访问过之后,算法结束。

  9. 将起点位置标记为已访问(即将其代价设为5)。

  10. 重新绘制界面。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值