A-Star 寻路算法演示(PureBasic)
;A-Star panth find
;2003.2.5 from vb6
EnableExplicit
#wd=15 ;width
#Xc=20
#Yc=20
#obstruct = 0
#channel = 1
Structure AStarNode
pos.Point ;该节点的坐标
father.Point
G.i
H.i
style.i ;类型,是否可行走
EndStructure
Declare.i AStar()
Declare AddOpenList(*pos.AStarNode)
Declare DelOpenList(*pos.AStarNode)
Declare AddCloseList(*pos.AStarNode)
Declare Neighbor_Search(*minFP.AStarNode,offsetX.i, offsetY.i)
Declare.i CheckCloseNode(*node.AStarNode)
Declare.i CheckNode(*node.AStarNode)
Declare CreateAStarMap(Array maps.i(2),*startP.AStarNode, *endP.AStarNode)
Declare OnLeftClick()
Declare OnBtLeftClick()
Declare OnBtCPLeftClick()
Declare OnBtCMLeftClick()
Declare OnChkBLeftClick()
Declare ISstartOrEndPoint(px.i,py.i)
Declare DrawMap()
Global OpenNum.i ;开启列表中的总结点数-1
Global CloseNum.i ;关闭列表中的总结点数-1
Define ArrLength.l ;数组长度
Global minX.i,minY.i,maxX.i,maxY.i
;计算出来的地图尺寸
minX=0
minY=0
maxX=#Xc
maxY=#Yc
Global Dim MColor(4),PenColor,Choise.i,starts.i=0,ends.i=0
MColor(1)=$00A000 ;green
MColor(2)=$F00000 ;blue
MColor(3)=$0000F0 ;red
MColor(4)=$00CCFF ;yellow
PenColor=MColor(1)
Choise=1
ArrLength = (maxX - minX) * (maxY - minY) - 1
Global Dim OpenList.AStarNode(ArrLength) ;开启表
Global Dim CloseList.AStarNode(ArrLength) ;关闭表
Global Dim AStarMap.AStarNode(maxX,maxY) ;地图
Global Dim maps.i(maxX,maxY), HavePath.i=#False
Global AstartP.AStarNode ;起点
Global AendP.AStarNode ;终点
;参数:要寻路的二维地图,寻路起点,寻路终点
;返回值:1找到路径,路径存在AStarPath中 0未找到路径
Global Dim AStarPath.Point(ArrLength) ;路径
Global PathLength.i ;路径长度
Global Slant.i ;斜向 0 false ,1 true
Define k.i,Event
Define X.i,Y.i,XN.i,YN.i,oldSX,oldSY,oldEX,oldEY
If OpenWindow(0, 100, 100, 460, 400, "PureBasic - A-Star Path", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
FrameGadget(11, 335, 15,95, 150, "选项")
OptionGadget(12, 350, 40, 40, 20, "平地")
OptionGadget(13, 350, 65, 40, 20, "障碍")
OptionGadget(14, 350, 90, 40, 20, "开始")
OptionGadget(15, 350, 115,40, 20, "结束")
CheckBoxGadget(16,350,140,40,20,"斜线")
SetGadgetState(12, 1) ; set second option as active one
BindGadgetEvent(12, @OnLeftClick(),#PB_EventType_LeftClick) ; Bind left click
BindGadgetEvent(13, @OnLeftClick(),#PB_EventType_LeftClick) ; Bind left click
BindGadgetEvent(14, @OnLeftClick(),#PB_EventType_LeftClick) ; Bind left click
BindGadgetEvent(15, @OnLeftClick(),#PB_EventType_LeftClick) ; Bind left click
ButtonGadget(17,340,185,80,30,"Find Path")
ButtonGadget(18,340,235,80,30,"Clear Path")
ButtonGadget(19,340,285,80,30,"Clear Map")
BindGadgetEvent(17, @OnBtLeftClick(),#PB_EventType_LeftClick) ; Bind left click
BindGadgetEvent(18, @OnBtCPLeftClick(),#PB_EventType_LeftClick)
BindGadgetEvent(19, @OnBtCMLeftClick(),#PB_EventType_LeftClick)
CanvasGadget(1,400,40,15,115)
If StartDrawing(CanvasOutput(1))
Box(0,0,75,115,$EEEEEE);RGB(196,196,196))
Box(0, 0,15,15,MColor(1))
Box(0,25,15,15,MColor(2))
Box(0,50,15,15,MColor(3))
Box(0,75,15,15,MColor(4))
LineXY(0,105,15,115,$000000)
StopDrawing()
EndIf
CanvasGadget(0, 10, 10, 301, 301)
OnBtCMLeftClick()
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget And EventGadget() = 0
If EventType() = #PB_EventType_LeftButtonDown Or (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
If (x<300 And x>0) And (y<300 And y>0)
If StartDrawing(CanvasOutput(0))
XN=Int(x/#wd)
YN=Int(y/#wd)
Select Choise
Case 1 ;#Tongdao
ISstartOrEndPoint(XN,YN)
maps(XN,YN)=#channel
Case 2 ;#zhangai
ISstartOrEndPoint(XN,YN)
maps(XN,YN)=#obstruct
Case 3
ISstartOrEndPoint(XN,YN)
maps(XN,YN)=2
Box(oldSX*#wd+1,oldSY*#wd+1,13,13,MColor(1))
maps(oldSX,oldSY)=#channel
oldSX=XN
oldSY=YN
starts=1
AstartP\pos\x=XN
AstartP\pos\y=YN
Case 4
ISstartOrEndPoint(XN,YN)
maps(XN,YN)=3
Box(oldEX*#wd+1,oldEY*#wd+1,13,13,MColor(1))
maps(oldEX,oldEY)=#channel
oldEX=XN
oldEY=YN
ends=1
AendP\pos\x=XN
AendP\pos\y=YN
EndSelect
Box(Int(x/#wd)*#wd+1,Int(y/#wd)*#wd+1,13,13,PenColor)
StopDrawing()
EndIf
EndIf
EndIf
EndIf
Until Event = #PB_Event_CloseWindow ; If the user has pressed on the close button
EndIf
End
Procedure.i AStar()
Protected p.Point ;指针
Protected minFP.AStarNode ;最小F值的节点
Protected i.i ;找最小F值For循环的循环变量
Protected Result=0
;初始化
OpenNum = -1: CloseNum = -1
PathLength = 0
Protected t.i=1
CreateAStarMap(maps(),@AstartP,@AendP) ;根据游戏地图创建本次寻路的A星地图
AddOpenList(@AstartP) ;将起点加入开启表
Repeat
If OpenNum = -1
Result = 0
Break ;当开启列表为空时,退出循环(没有找到路径)
EndIf
;把开启列表中G H值最小的点找出来(有多个相同最小值的话,找出靠前的那个)
minFP = OpenList(0)
For i = 0 To OpenNum
If minFP\G + minFP\H > OpenList(i)\G + OpenList(i)\H ;找数组中最小数
minFP = OpenList(i)
EndIf
Next i
;把这个点从开启列表中删除,加入到关闭列表
DelOpenList(@minFP)
AddCloseList(@minFP)
;搜索该点的邻居
Neighbor_Search(@minFP,0,-1) ;上
Neighbor_Search(@minFP, 0, 1) ;下
Neighbor_Search(@minFP,-1, 0) ;左
Neighbor_Search(@minFP, 1, 0) ;右
;这里是八方寻路,用不上可以直接注释掉
If Slant = 1
Neighbor_Search(@minFP, -1, -1) ;上左
Neighbor_Search(@minFP, 1, -1) ;上右
Neighbor_Search(@minFP, -1, 1) ;下左
Neighbor_Search(@minFP, 1, 1) ;下右
EndIf
If CheckCloseNode(@AendP) = #True ;如果终点在关闭列表中,就说明找到了通路,用回溯的方法记录路径
Result = 1
;寻找回路
p = AendP\pos
Repeat
AStarPath(PathLength) = p
PathLength = PathLength + 1
p = AStarMap(p\x,p\y)\father ;指针移动
If p\X = AstartP\pos\x And p\Y = AstartP\pos\y
Break
EndIf
Until t=0
Break
EndIf
Until OpenNum=-1
ProcedureReturn Result
;Debug.Print AStarMap(0, 0).H: Debug.Print AStarMap(1, 1).H
EndProcedure
;根据游戏地图创建AStar的寻路地图
Procedure CreateAStarMap(Array maps.i(2),*startP.AStarNode, *endP.AStarNode)
Protected x.i, y.i
;ReDim AStarMap(maxX - minX, maxY - minY) '根据游戏地图确定寻路地图尺寸
;生成寻路地图
For X = minX To maxX
For Y = minY To maxY
If Maps(X, Y) = 0
AStarMap(X, Y)\style = #obstruct
AStarMap(X, Y)\G = 0 ;初始化成0,到需要的时候再重新计算
AStarMap(X, Y)\H = (Abs(X - *endP\pos\X) + Abs(Y - *endP\pos\Y)) * 10 ;对于相同的起点和终点,H为定值,我们需要在这里一次性计算好(曼哈顿距离)
AStarMap(X, Y)\pos\X = X
AStarMap(X, Y)\pos\Y = Y
ElseIf Maps(X, Y) >= 1
AStarMap(X, Y)\style = #channel
AStarMap(X, Y)\G = 0
AStarMap(X, Y)\H = (Abs(X - *endP\pos\X) + Abs(Y - *endP\pos\Y)) * 10
AStarMap(X, Y)\pos\X = X
AStarMap(X, Y)\pos\Y = Y
EndIf
Next Y
Next X
EndProcedure
;参数:需要添加进来的节点(添加在线性表的尾部)
Procedure AddOpenList(*pos.AStarNode)
;Debug OpenNum
OpenNum = OpenNum + 1 ;总节点数 1
;OpenList(OpenNum)=*pos;添加节点
OpenList(OpenNum)\father=*pos\father
OpenList(OpenNum)\G=*pos\G
OpenList(OpenNum)\H=*pos\H
OpenList(OpenNum)\pos=*pos\pos
OpenList(OpenNum)\style=*pos\style
EndProcedure
;参数:需要删除的节点(删除后,将线性表尾部节点补充到删除后的空缺位置,为了减小时间复杂度)
Procedure DelOpenList(*pos.AStarNode)
Protected t.AStarNode ;临时节点,用于做变量交换
Protected c.AStarNode ;临时节点,用于清空对象
Protected i.i
For i = 0 To OpenNum
If OpenList(i)\pos\X =*pos\pos\X And OpenList(i)\pos\Y =*pos\pos\Y ;找到要删除的节点(目标节点)
t = OpenList(OpenNum) ;t指向开启表中最后一个节点
OpenList(OpenNum) = c ;删除最后一个节点
OpenList(i) = t ;把最后一个节点覆盖到目标节点
OpenNum = OpenNum - 1 ;开启表长度-1
Break ;结束不必要的循环
EndIf
Next i
EndProcedure
;参数:需要添加进来的节点(添加在线性表的尾部)
Procedure.i AddCloseList(*pos.AStarNode)
CloseNum = CloseNum + 1 ;总节点数 1
;CloseList(CloseNum) =*pos ;添加节点
CloseList(CloseNum)\father=*pos\father
CloseList(CloseNum)\G=*pos\G
CloseList(CloseNum)\H=*pos\H
CloseList(CloseNum)\pos=*pos\pos
CloseList(CloseNum)\style=*pos\style
EndProcedure
;确认传入节点是否存在于开启表中
Procedure.i CheckNode(*node.AStarNode)
Protected i.i
Protected Result=#False
For i = 0 To OpenNum
If OpenList(i)\pos\X =*node\pos\X And OpenList(i)\pos\Y =*node\pos\Y ;找到了
Result = #True
Break
EndIf
Next i
If i>OpenNum
Result = #False
EndIf
ProcedureReturn Result
EndProcedure
;确认是否在关闭表里
Procedure CheckCloseNode(*node.AStarNode)
Protected i.i
Protected Result=#False
For i = 0 To CloseNum
If CloseList(i)\pos\X =*node\pos\X And CloseList(i)\pos\Y =*node\pos\Y ;找到了
Result =#True
Break
EndIf
Next i
If i>CloseNum
Result = #False
EndIf
ProcedureReturn Result
EndProcedure
;功能:
;更新开启表中的G值
Procedure UpdataG()
Protected i.i
For i = 0 To OpenNum
If OpenList(i)\G <> AStarMap(OpenList(i)\pos\X, OpenList(i)\pos\Y)\G
OpenList(i)\G = AStarMap(OpenList(i)\pos\X, OpenList(i)\pos\Y)\G
EndIf
Next i
EndProcedure
Procedure Neighbor_Search(*minFP.AStarNode,offsetX.i, offsetY.i)
Protected AStep.i
;越界检测
If *minFP\pos\X + offsetX >=maxX Or *minFP\pos\X + offsetX < 0 Or *minFP\pos\Y + offsetY >=maxY Or *minFP\pos\Y + offsetY < 0
Goto exit1
EndIf
If offsetX = 0 Or offsetY = 0 ;设置单位花费
AStep = 10
Else
AStep = 14
EndIf
;如果该邻居不是障碍并且不在关闭表中
If AStarMap(*minFP\pos\X + offsetX, *minFP\pos\Y + offsetY)\style <>#obstruct And CheckCloseNode(AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)) =#False
;AStarMap(minFP.pos.x offsetX, minFP.pos.y offsetY).G = minFP.G AStep '给G赋值
If CheckNode(AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)) = #True ;存在于开启表中
If *minFP\G + AStep < AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)\G ;如果走新路径更短就更换父节点
AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)\G =*minFP\G + AStep
AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)\father =*minFP\pos
UpdataG() ;更新Openlist中的G值
EndIf
Else ;不存在于开启表中
AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)\father =*minFP\pos ;设置该邻居的父节点为我们上面找到的最小节点(minFP)
AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)\G =*minFP\G + AStep ;计算该点(邻居)的G值
AddOpenList(@AStarMap(*minFP\pos\X + offsetX,*minFP\pos\Y + offsetY)) ;把该点加入开启表中
EndIf
EndIf
exit1:
EndProcedure
Procedure OnLeftClick()
If GetGadgetState(12)
Choise=1
PenColor=MColor(1)
EndIf
If GetGadgetState(13)
Choise=2
PenColor=MColor(2)
EndIf
If GetGadgetState(14)
Choise=3
PenColor=MColor(3)
EndIf
If GetGadgetState(15)
Choise=4
PenColor=MColor(4)
EndIf
EndProcedure
Procedure ISstartOrEndPoint(px.i,py.i)
If maps(px,py)=2
starts=0
EndIf
If maps(px,py)=3
ends=0
EndIf
EndProcedure
Procedure OnBtLeftClick()
Define i.i
If starts=0 Or ends=0
MessageRequester("Warnning","No Start or Ending point!",#PB_MessageRequester_Ok|#PB_MessageRequester_Warning)
Else
If GetGadgetState(16)=#PB_Checkbox_Checked
Slant=1
Else
Slant=0
EndIf
If AStar()=0
MessageRequester("Info","No Path find !",#PB_MessageRequester_Ok|#PB_MessageRequester_Info)
Else
If StartDrawing(CanvasOutput(0))
For i = 1 To PathLength - 1
Circle(AStarPath(i)\x*#wd+7, AStarPath(i)\y*#wd+7,5, $F000F0)
Next i
EndIf
StopDrawing()
HavePath=#True
EndIf
EndIf
EndProcedure
Procedure OnBtCPLeftClick()
Define i.i
If HavePath
If StartDrawing(CanvasOutput(0))
For i = 1 To PathLength - 1
Circle(AStarPath(i)\x*#wd+7, AStarPath(i)\y*#wd+7,5, MColor(1))
Next i
EndIf
StopDrawing()
HavePath=#False
EndIf
EndProcedure
Procedure OnBtCMLeftClick()
Define i.i,j.i
For i=0 To maxX
For j=0 To maxY
maps(i,j)=#channel
Next j
Next i
HavePath=#False
starts=0
ends=0
DrawMap()
EndProcedure
Procedure DrawMap()
Define k.i,Font1.i
LoadFont(0, "Arial" , 28, #PB_Font_Bold)
If StartDrawing(CanvasOutput(0))
Box(0,0,300,300,MColor(1))
For k=0 To 300 Step #wd
Line(0, k, 300, 1,RGB(0,0,0))
Line(k,0,1,300,RGB(0,0,0))
Next k
DrawingMode(#PB_2DDrawing_Transparent)
FrontColor(RGB(200,200,255)) ; print the text to white !
DrawingFont(FontID(0))
DrawText(70, 80, "A graphic")
DrawText(15,160,"of A-Star path !",RGB(220,120,160))
StopDrawing() ; This is absolutely needed when the drawing operations are finished !!! Never forget it !
EndIf
EndProcedure
正月十五闲来无事,改编自VB6版本的。

浙公网安备 33010602011771号