request for code: simple A*

BlitzMax Forums/BlitzMax Programming/request for code: simple A*

CS_TBL(Posted 2007) [#1]
I'd like to request some A* methods/functions that:
1: don't come with 54 terabyte of comments between the code that make it unreadable, and for me, unusable. See, I don't want to know how the code works, I only want to know how the algo should be used.
2: are completely local, no globals, global arrays, wasted consts etc.
3: are clear about what the input and output formats are.
4: come *only* as one type with methods/functions, I don't want to see any examples that mess the whole thing up, also I don't want any external game-depended stuff in the core pathfinding algo. Only its own startX/Y and endX/Y fields. In fact, who's talking games anyway?

Actually what I'd like to see is a type that looks as follows:

Type TPathfinder

	Field status:Int ' 0: no possible path, 1:path found
	Field startX:Int, startY:Int, endX:Int, endY:Int

	Field diagonals:Int=1 ' true: use diagonal routes in pathfinding
	
	Field mapwidth:Int
	Field mapheight:Int
	
	Field map:TBank ' representing a 2d map where 0=walkable and 1=wall
	
	Field route:Int[]
	
	Method Setmapwidth(width:Int)
		' all kinda bounds checks here
		' ..
		' ..
		If mapwidth<1 RuntimeError "mapwidth<1"
		mapwidth=width
		Mapdimensions
	End Method
	
	Method Setstart(x:Int,y:Int)
		Assert x>=0 And x<mapwidth "start X out ot range"
		Assert y>=0 And y<mapheight "start Y out ot range"
		startx=x; starty=y
	End Method

	Method Setend(x:Int,y:Int)
		Assert x>=0 And x<mapwidth "end X out ot range"
		Assert y>=0 And y<mapheight "end Y out ot range"
		endx=x; endy=y
	End Method
	
	Method Mapdimensions()
		mapheight=BankSize(map)/mapwidth
		If mapheight<1 RuntimeError "mapheight<1"	
	End Method
	
	Method Setmap(bank:TBank=Null)
		Assert bank<>Null "bank=null"
		Assert BankSize(bank)>=1 "no dimension in bank"
		map=bank
		Mapdimensions
	End Method
	
	Method Findpath()
	
	
	' resulting path in a 'resliced' route[].
	' as [x0, y0, x1, y1, x2, y2, x3, y3, ..etc. .. xn,yn]
	'     ^ start                                   ^ end
	'
	End Method
	
	
End Type

' to use:
' make some bank
' make a pathfind instance
' add bank to instance
' set width and start/emd coords
' find path
' read out path from array
' yay \o/


If it's easy to adjust the A* code in the archives to this format then I'd be grateful to the one who does so. :-)


Curtastic(Posted 2007) [#2]
I think the problem is that most A* here is blitzbasic code. I'll be happy to code one in blitzmax.
Why is the map a bank and not a 2d array though?


CS_TBL(Posted 2007) [#3]
Well, dunno. Perhaps because a bank is a bank, and not an int? :P

If I add a bank to this type instance then map references to this bank, so I won't loose any more mem. Ints/bytes/floats etc. are copied to a new var instance afaik, so I figure the same counts for arrays of those basic vars.

If you do, please try to fill in those methods/functions in my framework.. ^_^ For I think that's a clean/open way to do all this.


Jesse(Posted 2007) [#4]
http://www.blitzmax.com/Community/posts.php?topic=62388#697498


CS_TBL(Posted 2007) [#5]
Too much noise in it.

It's what I meant with 'game depending', in that code there's this Tunit stuff everywhere. Unit? What unit? sprite:timage? What image? Speed? Who needs speed? Who says pathfinding is exclusively for games? Tnode, oh really.. so that costs yet another (global) type, and do I actually care, for only 2 int fields? Tilesize? What tiles? As far as I'm concerned, a tile is a visual property, not a functional property. Etc. etc., that whole thing is almost a game already, it's far from neutral and sterile.

The minimum required for pathfinding is a start location and an end location, and a 2d map with walkable/nonwalkable states. I suggest keeping these kind of solutions as minimal as possible, in order to be as flexible and modular as possible.


Dreamora(Posted 2007) [#6]
Check the german blitzbasic forum. There is a working and very good A* ressource in the codearchive of the blitzmax categorie (there is one for regular blitz as well if you do not like the BM one)


CS_TBL(Posted 2007) [#7]
Curtastic: so .. you're up to do it (in the way I proposed)? :P


Curtastic(Posted 2007) [#8]
ya!!!111:):):):

I think I can do it this weekend


Curtastic(Posted 2007) [#9]




Example of how to use:



CS_TBL(Posted 2007) [#10]
I want your babies!


Curtastic(Posted 2007) [#11]
You're welcome, let me know if any improvements are needed, for the code archives or anything.

And my seed will conquer the world!


CS_TBL(Posted 2007) [#12]
^_^
Why the 'abstract' btw?


Curtastic(Posted 2007) [#13]
Well I don't see the need to make new instances, since all you need is the route. I could easily change it though.

Right now if you want to keep 2 separate paths you have to do:
local myroute[]
local blahroute[]

tpathfinder.findpath(x1,y1,x2,y2)
myroute=tpathfinder.route

tpathfinder.findpath(blahx,blahy,targetx,targety)
blahroute=tpathfinder.route



Curtastic(Posted 2007) [#14]
Oops there was a big memory leak because of cyclic refrences, I just edited and fixed it.


TartanTangerine (was Indiepath)(Posted 2007) [#15]
Here's some code I borrowed from somewhere and ported, I've restricted to 4 directions but it's easy to change back.




Diordna(Posted 2007) [#16]
Awesome Curtastic, thanks! Couple of things:
-You misspelled "diagonal."
-If I put a value >1 into the map, will it count it as being twice as slow to walk over? If not, what part of the code should I change to make it do this? This is essential for games with varying terrian. (I think it also means that Map[] should be changed to something with decimals, not an int, so I can say, for example, 1.5.)


Curtastic(Posted 2007) [#17]
-You misspelled "diagonal."

wow. How can I even be smart enough to code this? lol.
I seriously didnt know how to spell that. fixed.

-If I put a value >1 into the map, will it count it as being twice as slow to walk over? If not, what part of the code should I change to make it do this? This is essential for games with varying terrian. (I think it also means that Map[] should be changed to something with decimals, not an int, so I can say, for example, 1.5.)


I could add support for that. Theoretically all you have to do is something like this:
'cost is slightly more for diagnols
If Dir < 4 Then
	NewP.Cost = P.Cost + CostOfTile
Else
	NewP.Cost = P.Cost + CostOfTile*Root2
EndIf



CS_TBL(Posted 2007) [#18]
I think it needs something like being able to walk in diagonals while not cutting corners when there's a non-walkable thing to cross. Another option for the diagonals field perhaps?
0 no diagonals
1 diagonals + cutting
2 diagonals, no cutting

I keep seeing Warcraft in front of me where figures are walking in all 8 directions but aren't cutting corners at buildings/trees etc.


CS_TBL(Posted 2007) [#19]
Actually I think we should expand this pathfinder with all kinda extra functionality, without loosing its neutrality of course.

Apart from the walk/wall and the said ramping there could also be floors (used in cobination with ramping), meaning that from floor a you can't directly move to floor b, only via a ramp.

Also, there could be walkable tiles with priorities, high priority is to choose over low priority.

What about legal random errors in a path to simulate human errors? (otherwise you get straight marching of units). These errors could be simulated by analyzing the found path: for each step in the route check the surrounding tiles, if they are walkable then the route could add this to the route.

What about another route[] to store enviromental properties for each step? Like how many rings of directly walkable tiles are around the position of the step?

etc. all these things keep the thing neutral but could greatly enhance the functionality!


Damien Sturdy(Posted 2007) [#20]
Wow, nice bit of code! :)


Curtastic(Posted 2007) [#21]
I think it needs something like being able to walk in diagonals while not cutting corners when there's a non-walkable thing to cross. Another option for the diagonals field perhaps?
0 no diagonals
1 diagonals + cutting
2 diagonals, no cutting


Ya I'll add that. I just didn't know if anyone wanted it.

I keep seeing Warcraft in front of me where figures are walking in all 8 directions but aren't cutting corners at buildings/trees etc.


Actually in warcraft (and most games I've seen) you can cut corners. Notice the footman is cutting a corner between farms. Most games have corners rounded, so there is room to cut inbetween.



What about legal random errors in a path to simulate human errors? (otherwise you get straight marching of units). These errors could be simulated by analyzing the found path: for each step in the route check the surrounding tiles, if they are walkable then the route could add this to the route.


Done!



What about another route[] to store enviromental properties for each step? Like how many rings of directly walkable tiles are around the position of the step?


Well once the route is found you can always do that, it shouldn't be be the pathfinder's job.

Now lets say the object is larger than one tile, the pathfinder will need to find a path that the large object can fit through. That's something that I can add.



Also, there could be walkable tiles with priorities, high priority is to choose over low priority.


Ya I'll do that except the tiles will store how long it takes to move into it. So a high number is slower and 0 is the fastest. [edit: done]


Wow, nice bit of code! :)

thanks!


CS_TBL(Posted 2007) [#22]
^_^ babies, more babies! ^_^

But one baby needs a new diper: try to move along the edges of the map with this randomness (place a wall next to it orso) and you'll get an assert exception (coords out of bounds).

[edit] hm.. or maybe not.. oO


CS_TBL(Posted 2007) [#23]
the example could use this, under the:

mx = MouseX() / TS
my = MouseY() / TS

	mx=Max(mx,0); mx=Min(mx,19)
	my=Max(my,0); my=Min(my,19)



Curtastic(Posted 2007) [#24]
ok, I added the no cuting corners option. Any more ideas?


GW(Posted 2007) [#25]
nice!
perhaps the option to specify a variable width for moving agents as an argument, So the pathfinder could exclude paths that are to narrow for anything with a width greater than 1 cell. For example, a width=3 would represent a unit that occupies a 3x3 area.


CS_TBL(Posted 2007) [#26]
well, those floors I mentioned perhaps, with ramps 'n stuff (like in Starcraft). Tho I wouldn't know how to include that in one single 2d array.