Great idea! We could learn the fundamentals of AStar
pathfinding by roleplaying Crazy Stalker/Helpless Victim! Ok,
my exact address is ....
heh heh.
Anyway, I just wanted to post and say:
WOOHOO, I DID IT!! I FIGURED OUT THE INFAMOUS A* PATHFINDING ALGORITHM!See? Here's my first program showing this:
[edit: Updated]
'First pathfinding program using A*
'Based from this tutorial: <a href="http://www.policyalmanac.org/games/aStarTutorial.htm" target="_blank">http://www.policyalmanac.org/games/aStarTutorial.htm</a>
'Import "drawThings.bmx"
SuperStrict
Graphics 1024, 768, ,60
Global mapX:Int = 11, mapY:Int = 8, heuristic:Int = 3, heuristicName:String = "Diagonal"
Type endsType
Field startX:Int, startY:Int, endX:Int, endY:Int
Method New()
startX = 2
startY = 2
endX = 10
endY = 2
EndMethod
EndType
Global ends:endsType = New endsType
Type nodeType
Field F:Int, G:Int, H:Int, open:Int=0, parentX:Int, parentY:Int
Method New()
open = 0
EndMethod
EndType
Global map:Int[mapX+1,mapY+1]
Global node:nodeType[mapX+1,mapY+1]
For Local y:Int = 0 To mapY
For Local x:Int = 0 To mapX
map[x,y] = 0
node[x,y] = New nodeType
Next
Next
For Local a:Int = 2 To 6
map[6, a]=255
Next
map[ends.startX, ends.startY] = 256
map[ends.endX, ends.endY] = 257
SetClsColor 50, 60, 50
Repeat
dokeys()
drawmap()
SetColor 255, 255, 255
DrawText "Left mouse - set start, right mouse - set goal, middle mouse - draw wall, up arrow - floor stick", 40, 740
DrawText "Heuristic: "+heuristicName+", (hit 1, 2, 3 to change)", 40, 754
Flip;Cls
Forever
Function drawmap()
For Local y:Int = 0 To mapY
For Local x:Int = 0 To mapX
'If map[x,y]
SetColor map[x,y]*5, map[x,y]*5, map[x,y]*5
Select map[x,y]
Case 255 'wall
SetColor 10, 30, 100
Case 256 'starting node
SetColor 20, 170, 30
Case 257 'goal node
SetColor 100, 30, 10
EndSelect
drawl(x*80, y*80, 79, 79, 1, FILL)
'EndIf
SetColor 75, 75, 75
drawl(x*80, y*80, 79,79)
Select node[x,y].open
Case 1
SetColor 170, 170, 170
DrawText "F"+node[x,y].F, x*80+21, y*80+3
DrawText "G"+node[x,y].G, x*80+1, y*80+63
DrawText "H"+node[x,y].H, x*80+41, y*80+63
SetColor 50, 150, 10
DrawEllipse x*80+40, y*80+40,3,3
SetColor 10, 200, 10
DrawL (x*80+40, y*80+40, (node[x,y].parentX-x)*20, (node[x,y].parentY-y)*20,1,LINE)
Case -1
SetColor 185, 25, 25
drawl(x*80+1, y*80+1, 77,77)
drawl(x*80+3, y*80+3, 73,73)
drawl(x*80+2, y*80+2, 76,76,2,corners)
DrawEllipse x*80+40, y*80+40,3,3
SetColor 10, 200, 10
DrawL (x*80+40, y*80+40, (node[x,y].parentX-x)*20, (node[x,y].parentY-y)*20,1,LINE)
EndSelect
Next
Next
EndFunction
Function dokeys()
If KeyHit(key_1) Then heuristic = 1; heuristicName = "Manhattan *10"
If KeyHit(key_2) Then heuristic = 2; heuristicName = "Manhattan"
If KeyHit(key_3) Then heuristic = 3; heuristicName = "Diagonal"
If KeyHit(key_up) Then
Local colour:Int = 0
For Local x:Int = 0 To mapX
For Local y:Int = mapY To 0 Step -1
If map[x,y] = 255 Then
colour = 0
Else
If map[x,y]<255 map[x,y] = colour
colour:+5
EndIf
Next
colour=0
Next
EndIf
If MouseDown(1) Then
map[ends.startX, ends.startY]=0
ends.startX = MouseX()/80
ends.startY = MouseY()/80
map[ends.startX, ends.startY]=256
EndIf
If MouseDown(2) Then
map[ends.endX, ends.endY]=0
ends.endX = MouseX()/80
ends.endY = MouseY()/80
map[ends.endX, ends.endY]=257
EndIf
If MouseHit(3) Then
If map[MouseX()/80, MouseY()/80]<>255 Then
map[MouseX()/80, MouseY()/80]=255
Else
map[MouseX()/80, MouseY()/80]=0
EndIf
For Local y:Int = 0 To mapY
For Local x:Int = 0 To mapX
node[x,y].open = 0
If map[x,y]<255 Then map[x,y] = 0
Next
Next
EndIf
If KeyHit(key_escape) End
If KeyHit(key_space) Or MouseHit(1) Or MouseHit(2) Then
For Local y:Int = 0 To mapY
For Local x:Int = 0 To mapX
node[x,y].open = 0
Next
Next
Local currentX:Int = ends.startX, currentY:Int = ends.startY
Local lowFX:Int, lowFY:Int
For Local a:Int = 0 To 80
createFGH(currentX, currentY)
findLowF(currentX, currentY)
If currentX = ends.endX And currentY = ends.endY Then Exit
Next
' SetColor 255, 255, 255
' DrawText lowFX+", "+lowFY, 10, 587
' Flip
' WaitKey
EndIf
EndFunction
Function createFGH(x:Int, y:Int)
'Local x:Int = ends.StartX, y:Int = ends.startY
Local xTarget:Int = ends.endX, yTarget:Int = ends.endY
node[x,y].open = -1
For Local yy:Int= -1 To 1
For Local xx:Int = -1 To 1
If x+xx < 0 Or x+xx > mapX Continue
If y+yy < 0 Or y+yy > mapY Continue
If node[x+xx, y+yy].open = -1 Or map[x+xx, y+yy] = 255 Continue 'If this node has been closed, skip To the Next one.
If node[x+xx, y+yy].open = 1 Then
'find the G score between current node and this here node
' Local G:Int = 14
' If xx = 0 Or yy = 0 Then G = 10
' If G < node[x+xx, y+yy].G Then
' node[x+xx, y+yy].parentX = x
' node[x+xx, y+yy].parentY = y
' EndIf
EndIf
If node[x+xx, y+yy].open = 0 Then '0 = not been touched yet
node[x+xx, y+yy].open = 1 'change it to 1, showing that we now consider it a possible path.
node[x+xx, y+yy].parentX = x
node[x+xx, y+yy].parentY = y
EndIf
Select heuristic
Case 1
node[x+xx, y+yy].H = (Abs(x+xx-ends.endX)+Abs(y+yy-ends.endY))*10
Case 2
node[x+xx, y+yy].H = (Abs(x+xx-ends.endX)+Abs(y+yy-ends.endY))
Case 3
Local ydist:Int = Abs(y+yy-ends.endY)
Local xdist:Int = Abs(x+xx-ends.endX)
If xDist> yDist Then
node[x+xx, y+yy].H = 14*yDist +10*(xDist-yDist)
Else
node[x+xx, y+yy].H = 14*xDist +10*(yDist-xDist)
EndIf
EndSelect
node[x+xx, y+yy].G = 14
If xx = 0 Or yy = 0 node[x+xx, y+yy].G = 10
node[x+xx, y+yy].F = node[x+xx, y+yy].G + node[x+xx, y+yy].H
Next
Next
EndFunction
Function findLowF:Int(lowFCoordinateX:Int Var, lowFCoordinateY:Int Var)
Local lowestF:Int = 999999
For Local y:Int = 0 To mapY
For Local x:Int = 0 To mapX
If node[x,y].open = 1 Then
If node[x,y].F < lowestF Then
lowestF = node[x,y].F
lowFCoordinateX = x
lowFCoordinateY = y
EndIf
EndIf
Next
Next
EndFunction
Const NORMAL:Int = 0
Const DOTTED:Int = 1
Function drawEllipse(x:Int,y:Int,xSize:Int,ySize:Int,StepSize:Int = 15, width:Int=1, style:Int = NORMAL)
Local oldlinewidth:Int = GetLineWidth(), dot:Int = 1
SetLineWidth width
Local a:Int = stepSize
Repeat
If dot DrawLine x+xSize*Sin(a), y+ySize*Cos(a),x+xSize*Sin(a-stepSize), y+ySize*Cos(a-stepSize)
a:+stepSize
If style dot = 1-dot
Until a> 360
SetLineWidth oldlinewidth
EndFunction
Const FILL:Int = 3
Const LINE:Int = 2
Const CORNERS:Int = 1
Const BOX:Int = 0
Function drawl(x1:Int,y1:Int,x2:Int,y2:Int,width:Int=1,style:Int = BOX)
Local oldlinewidth:Int = GetLineWidth()
SetLineWidth width
Select style
Case 3
DrawRect x1,y1,x2,y2
Case 2
x2:+x1
y2:+y1
DrawLine x1,y1,x2,y2
Case 1
x2:+x1
y2:+y1
DrawLine (x1,y1,x1+(x2-x1)*.1,y1)
DrawLine (x1,y1,x1,y1+(y2-y1)*.1)
DrawLine (x1,y2-(y2-y1)*.1,x1,y2)
DrawLine (x1,y2,x1+(x2-x1)*.1,y2)
DrawLine (x2,y1,x2-(x2-x1)*.1,y1)
DrawLine (x2,y1,x2,y1+(y2-y1)*.1)
DrawLine (x2,y2-(y2-y1)*.1,x2,y2)
DrawLine (x2,y2,x2-(x2-x1)*.1,y2)
Default
x2:+x1
y2:+y1
DrawLine (x1,y1,x2,y1)
DrawLine (x1,y1,x1,y2)
DrawLine (x1,y2,x2,y2)
DrawLine (x2,y1,x2,y2)
EndSelect
SetLineWidth oldlinewidth
End Function
As you can see, it doesn't find the most efficient route at
all. I'm not sure why. Not to mention the code is
horrendous (I'm using functions! Ah!)
But it works! And it works pretty fast, too! Now, I
rewrite and rewrite again until I have a usable module to
implement in my projects.
This is quite cool. I'm very excited.