mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
git-svn-id: http://tgstation13.googlecode.com/svn/trunk@3671 316c924e-a436-60f5-8080-3fe189b3f50e
186 lines
4.8 KiB
Plaintext
186 lines
4.8 KiB
Plaintext
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
|
|
|
/*
|
|
A Star pathfinding algorithm
|
|
Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of
|
|
windows along the route into account.
|
|
Use:
|
|
your_list = AStar(start location, end location, adjacent turf proc, distance proc)
|
|
For the adjacent turf proc i wrote:
|
|
/turf/proc/AdjacentTurfs
|
|
And for the distance one i wrote:
|
|
/turf/proc/Distance
|
|
So an example use might be:
|
|
|
|
src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance)
|
|
|
|
Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use.
|
|
|
|
src.path_list = reverselist(src.pathlist)
|
|
|
|
Then to start on the path, all you need to do it:
|
|
Step_to(src, src.path_list[1])
|
|
src.path_list -= src.path_list[1] or equivilent to remove that node from the list.
|
|
|
|
Optional extras to add on (in order):
|
|
MaxNodes: The maximum number of nodes the returned path can be (0 = infinite)
|
|
Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite)
|
|
Mintargetdist: Minimum distance to the target before path returns, could be used to get
|
|
near a target, but not right to it - for an AI mob with a gun, for example.
|
|
Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum
|
|
length to avoid portals or something i guess?? Not that they're counted right now but w/e.
|
|
*/
|
|
|
|
// Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null
|
|
// Used for checking if route exists through a door which can be opened
|
|
|
|
// Also added 'exclude' turf to avoid travelling over; defaults to null
|
|
|
|
|
|
PriorityQueue
|
|
var/L[]
|
|
var/cmp
|
|
New(compare)
|
|
L = new()
|
|
cmp = compare
|
|
proc
|
|
IsEmpty()
|
|
return !L.len
|
|
Enqueue(d)
|
|
var/i
|
|
var/j
|
|
L.Add(d)
|
|
i = L.len
|
|
j = i>>1
|
|
while(i > 1 && call(cmp)(L[j],L[i]) > 0)
|
|
L.Swap(i,j)
|
|
i = j
|
|
j >>= 1
|
|
|
|
Dequeue()
|
|
if(!L.len) return 0
|
|
. = L[1]
|
|
Remove(1)
|
|
|
|
Remove(i)
|
|
if(i > L.len) return 0
|
|
L.Swap(i,L.len)
|
|
L.Cut(L.len)
|
|
if(i < L.len)
|
|
_Fix(i)
|
|
_Fix(i)
|
|
var/child = i + i
|
|
var/item = L[i]
|
|
while(child <= L.len)
|
|
if(child + 1 <= L.len && call(cmp)(L[child],L[child + 1]) > 0)
|
|
child++
|
|
if(call(cmp)(item,L[child]) > 0)
|
|
L[i] = L[child]
|
|
i = child
|
|
else
|
|
break
|
|
child = i + i
|
|
L[i] = item
|
|
List()
|
|
var/ret[] = new()
|
|
var/copy = L.Copy()
|
|
while(!IsEmpty())
|
|
ret.Add(Dequeue())
|
|
L = copy
|
|
return ret
|
|
RemoveItem(i)
|
|
var/ind = L.Find(i)
|
|
if(ind)
|
|
Remove(ind)
|
|
PathNode
|
|
var/datum/source
|
|
var/PathNode/prevNode
|
|
var/f
|
|
var/g
|
|
var/h
|
|
var/nt // Nodes traversed
|
|
New(s,p,pg,ph,pnt)
|
|
source = s
|
|
prevNode = p
|
|
g = pg
|
|
h = ph
|
|
f = g + h
|
|
source.bestF = f
|
|
nt = pnt
|
|
|
|
datum
|
|
var/bestF
|
|
proc
|
|
PathWeightCompare(PathNode/a, PathNode/b)
|
|
return a.f - b.f
|
|
|
|
AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null)
|
|
|
|
// world << "A*: [start] [end] [adjacent] [dist] [maxnodes] [maxnodedepth] [mintargetdist], [minnodedist] [id]"
|
|
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare)
|
|
var/closed[] = new()
|
|
var/path[]
|
|
start = get_turf(start)
|
|
if(!start) return 0
|
|
|
|
open.Enqueue(new /PathNode(start,null,0,call(start,dist)(end)))
|
|
|
|
while(!open.IsEmpty() && !path)
|
|
{
|
|
var/PathNode/cur = open.Dequeue()
|
|
closed.Add(cur.source)
|
|
|
|
var/closeenough
|
|
if(mintargetdist)
|
|
closeenough = call(cur.source,dist)(end) <= mintargetdist
|
|
|
|
if(cur.source == end || closeenough)
|
|
path = new()
|
|
path.Add(cur.source)
|
|
while(cur.prevNode)
|
|
cur = cur.prevNode
|
|
path.Add(cur.source)
|
|
break
|
|
|
|
var/L[] = call(cur.source,adjacent)(id)
|
|
if(minnodedist && maxnodedepth)
|
|
if(call(cur.source,minnodedist)(end) + cur.nt >= maxnodedepth)
|
|
continue
|
|
else if(maxnodedepth)
|
|
if(cur.nt >= maxnodedepth)
|
|
continue
|
|
|
|
for(var/datum/d in L)
|
|
if(d == exclude)
|
|
continue
|
|
var/ng = cur.g + call(cur.source,dist)(d)
|
|
if(d.bestF)
|
|
if(ng + call(d,dist)(end) < d.bestF)
|
|
for(var/i = 1; i <= open.L.len; i++)
|
|
var/PathNode/n = open.L[i]
|
|
if(n.source == d)
|
|
open.Remove(i)
|
|
break
|
|
else
|
|
continue
|
|
|
|
open.Enqueue(new /PathNode(d,cur,ng,call(d,dist)(end),cur.nt+1))
|
|
if(maxnodes && open.L.len > maxnodes)
|
|
open.L.Cut(open.L.len)
|
|
}
|
|
|
|
var/PathNode/temp
|
|
while(!open.IsEmpty())
|
|
temp = open.Dequeue()
|
|
temp.source.bestF = 0
|
|
while(closed.len)
|
|
temp = closed[closed.len]
|
|
temp.bestF = 0
|
|
closed.Cut(closed.len)
|
|
|
|
if(path)
|
|
for(var/i = 1; i <= path.len/2; i++)
|
|
path.Swap(i,path.len-i+1)
|
|
|
|
return path
|