游戏寻路算法A*的实现

来源:互联网 发布:软件开发简历专业技能 编辑:程序博客网 时间:2024/04/30 02:43

[测试平台] Celeron 2.4GHZ + 1G内存

[演示]

 

(1)    地图大小(size)假设地图都是正方形size <= 1000

(2)    障碍百分比(rate):用于设置障碍点数,障碍数=size*size*rate/100

(3)    测试软件的目标是用A*算法在地图中找到最短路径

(4)    图示中size = 1000的地图用了624ms,这是比较好的情况,如果在比较差的情况下size=1000时可能需要3s的时间;当一般情况size=300时,一般需要100ms左右的时间,应该还是属于高效的。

(5)    起点坐标(00),终点坐标(size-1,size-1

(6)    地图文件map.in,寻路输出文件map.out,最好用写字版打开.

(7)    本文的目标在于介绍A*的具体实现方法,不打算介绍A*的原理,也不会对h(n)进行过多的探讨,h(n)使用最简单的dx+dy形式。从算法效率角度来说针对总体效率的把握,而没有对程序本身进行过多无谓的优化,否则如果用汇编实现一些关键运算的话效率就可以提高N多。

 

[寻路结果示例] size=30,rate = 60,.’表示可以通行,‘*’表示障碍

→↘ . * . . . * * * . * * * * * . . . . * . . * . . . * . *

 . * . * . . * * * . . * * * . . . . * . * . . . . . * * *

 . * * * * * . * . . . . * * . * . * * * . * . . . * . * .

 * * . . * . * . * . * * * . . . * . . . * . . * . . * . *

 . . . * * * * . * . * . . . * * . * . . . * . . . . * * .

 . . . * * . . . . * * . * . * * . * * * . . * . . . * . .

 . . . . .→→→↘ * . . * * . * * . . . * * . . * . * . . *

 * * . . * . . * . . * . . * * * * . . * * * . . * * . * *

 . . * . * . * . * * . * . * * * * * . * . . . . . * * . .

 . . . * . * . . * * * * . * . . * * . * . . * . * . . * *

 . * * * * . . . * * . * * . . . . . * * * . * . . . . . .

 . * * * * * . . . . * * * * * * . . * * * * . . * . . * .

 * . . . * . . * . * * *↘ . . * * * * . . . * . * . . . * .

 . . * . . . * * * . * . *↙ * * * * * . . . * . . * . . * .

 . * * * * * . . . . * *↘ * * * . * * . . . * . . * . . * .

 * * . * . * * * . * * . .→→↘ * . * * . * . * * * . * . .

 . . . . * * . . . * * * . * * *→↘ * * . * . * * * * . . .

 * * . . . . * * * . . . * . * . . .↘ * . * . . . * * * . .

 . * . * . . . . . * * . . . * . * * .→↘ * * . * . . * * *

 . * * . . . . . . * * . * * * . * . . . *↓ * * . * . * . *

 . * . * * * . . . * . * . * . . * . . * *↘ . * . * . * * *

 . * * * . * . . * . . * . . * * * . . . . .↘ * . * * * . *

 . * * * * . * * . . . . . * . . . * . * * . .↘ * . . * * *

 * . . . * * * * . . . . . . . * . * . * * . * *↘ . * * * .

 . * . . . . . * . * * . * . . . * * . * * * * . *↘ . * . *

 * . * * * . * * . . . . . . . . * . . * . * . * . *↘ . * *

 . * . . . . . * . * . * * . * * . . * . * . * . . * .↘ . *

 * . . . * . . * . * * * . . * * . * . * . . . * . . * .↘ *

 . . . . . . . * * . * * . * * . * * . * . * . . * * * * *↓

 . . * . * . . . . . . . . . * . . . . . . * . . * * * . * .

 

一、A*算法实现过程中的存储结构

(1)    八个方向

int dir[8][2] = {{-1,0},{-1,1},{0,1},{1,1},{-1,-1},{0,-1},{1,-1},{1,0}} ;

char dir_ch[8][3] = { "","","","","","","","" } ;

这样存储8个方向的好处是,当知道AàB的方向为x时,就可以直接得到BàA的方向为(7-x)

 

  

(2)    地图储存结构MAP

typedef struct _MAP {

     int type ;             // 0:可行 1:障碍

     int status ;            // 0:未访问1:OPEN 2:CLOSE

     int dir_parent ;        // 指向前一结点dir ( 07)

     int g ;                // g(n),起点到当前结点的代价

     int h ;                // h(n),当前结点到终点的估价值

     int index ;            // heap中的结点索引号

} MAP ;

 

着重介绍2个参数:

<1>status:当前结点的状态,由于这个参数的存在可以节约CLOSE列表

<2>indexA*中需要对结点进行不断的更新,这个参数可以直接定位到当前结点在OPEN队列中的位置,不需要额外的查找。

 

(3)    OPEN列表的结点

typedef struct _NODE {

     int x, y ;    // 结点坐标

     int value ;   // f = g + h

} NODE, *PNODE ;

 

二、A*算法实现

bool  AStar_GetShortestPath ()
{
    
int lx = CUR_LEN, ly =
 CUR_LEN ;
    
int nStartX = 0, nStartY = 0
 ;
    
int nEndX = lx-1, nEndY = ly-1
 ;

    OPEN.BHAddNew ( nStartX, nStartY, 
3
 ) ;
    map[nStartX][nStartY].status 
= 1
 ;
    map[nStartX][nStartY].index 
= 0
 ;

    
while ( OPEN.BHGetNum() > 0
 )
    {
        NODE TopNode 
=
 OPEN.BHGetTop () ;
        
// 如果堆顶元素为终点时,直接返回true

        if ( TopNode.x == nEndX && TopNode.y == nEndY )
            
return true
 ;

        
// 修改结点状态,OPEN-->CLOSE

        map[TopNode.x][TopNode.y].status = 2 ;
        OPEN.BHDelTop () ;

        
for ( int i = 0; i < 8; i++
 )
        {
            
int a = TopNode.x + dir[i][0
] ;
            
int b = TopNode.y + dir[i][1
] ;
            
// 检测坐标是否有效,且该结点是否为障碍

            if ( IsValidPos(a,b) == false || map[a][b].type == 0 )
                
continue
 ;

            
// 计算当前消费g(n)

            int cur_cost = map[TopNode.x][TopNode.y].g + cost[i] ;

            
// 若该结点首次被访问

            if ( map[a][b].status == 0 )
            {
                map[a][b].status 
= 1
 ;
                map[a][b].dir_parent 
= 7 -
 i ;
                map[a][b].g 
=
 cur_cost ;
                map[a][b].h 
= ( nEndX - a ) + ( nEndY - b ) ;    // 只在首次访问时估价h(n)

                OPEN.BHAddNew ( a, b, map[a][b].g + map[a][b].h ) ;
            }
            
// 如果该结点需要更新

            else if ( cur_cost < map[a][b].g )
            {
                map[a][b].dir_parent 
= 7-
i ;
                map[a][b].g 
=
 cur_cost ;

                
// 如果该结点已经在OPEN,那么只需要BinaryHeap进行修改操作

                if ( map[a][b].status == 1 )
                {
                    OPEN.BHAdjustByIndex ( map[a][b].index, map[a][b].g 
+
 map[a][b].h ) ;
                }
                
// 如果该结点在CLOSE中,那么只需要BinaryHeap进行添加操作

                else if ( map[a][b].status == 2 )
                {
                    OPEN.BHAddNew ( a, b, map[a][b].g 
+
 map[a][b].h ) ;        
                }
                map[a][b].status 
= 1
 ;
            }
        } 
// for

    } // while 
    
    
return false
 ;
}

 

 

 

三、二插堆(Binary Heap)A*中的应用

BinaryHeap的来实现OPEN,有这么几个优点:

(1)    快速定位OPEN中最小元素,O(1)

(2)    能够实现对结点的快速插入/删除/修改,O(long(n))

BinaryHeap本身对结点的修改操作支持不好,因为除了已知堆顶元素最小/大之外,对其他元素一无所知,因而一般情况下非暴力搜索不可行。在地图存储结构MAP中有index项,就可以直接定位当前结点在OPEN中的位置。

 

BinaryHeap3种操作方式:插入/删除/修改   

    bool BHAddNew ( int x, int y, int value )
    {
        NODE node ( x, y, value ) ;
        
this->pHeap[this->count++= node ;
        
this->BHFilterUp ( this->count-1 ) ;
        
return true ;
    }

    
bool BHDelTop ()
    {
        
if ( this->count <= 0 )
            
return false ;

        
this->count-- ;
        
this->pHeap[0= this->pHeap[this->count] ;
        
this->BHFilterDown ( 0 ) ;
        
return true ;
    }

    
bool BHAdjustByIndex ( int index, int nNewValue )
    {
        
this->pHeap[index].value = nNewValue ;
        
if ( this->BHFilterDown ( index ) == index )
            
this->BHFilterUp ( index ) ;
        
return true ;
    }

 

BinaryHeap2种更新方式:向下/向上更新

int BHFilterDown ( int index )
    {
        NODE TempNode 
= this->
pHeap[index] ;
        
int cur =
 index ;
        
int son = 2 * cur + 1
 ;
        
while ( son < this->
count )
        {
            
if ( son+1 < this->count && this->pHeap[son].value > this->pHeap[son+1
].value )
                son
++
 ;

            
if ( TempNode.value < this->
pHeap[son].value )
                
break
 ;
            
else

            {
                
this->pHeap[cur] = this->pHeap[son] ;
                
this->BHUpdatePos ( this->pHeap[son].x, this->
pHeap[son].y, cur ) ;
                cur 
=
 son ;
                son 
= 2 * cur + 1
 ;
            }
        }
        
this->pHeap[cur] =
 TempNode ;
        
this->
BHUpdatePos ( TempNode.x, TempNode.y, son ) ;
        
return
 son ;
    }

    
int BHFilterUp ( int
 index )
    {
        NODE TempNode 
= this->
pHeap[index] ;
        
int cur =
 index ;
        
int parent = ( cur - 1 ) / 2
 ;
        
while ( cur != 0
 )
        {
            
if ( this->pHeap[parent].value <=
 TempNode.value )
                
break
 ;

            
this->pHeap[cur] = this->
pHeap[parent] ;
            
this->BHUpdatePos ( this->pHeap[parent].x, this->
pHeap[parent].y, cur ) ;
            cur 
=
 parent ;
            parent 
= ( cur - 1 ) / 2
 ;
        }
        
this->pHeap[cur] =
 TempNode ;
        
this->
BHUpdatePos ( TempNode.x, TempNode.y, cur ) ;
        
return
 cur ;
    }

 

 

node在堆中改变位置时需要修改MAP中所对应[node.x,node.y]中的index

void BHUpdatePos ( int x, int y, int pos )

     {

         map[x][y].index = pos ;

     }

 

原创粉丝点击