Merge pull request #7360 from SkyMarshal/astar-fix

Fix/Cleanup for A* implementation
This commit is contained in:
Zuhayr
2014-12-10 16:29:55 +10:30

View File

@@ -38,148 +38,144 @@ length to avoid portals or something i guess?? Not that they're counted right no
PriorityQueue PriorityQueue
var/L[] var/list/queue
var/cmp var/proc/comparison_function
New(compare) New(compare)
L = new() queue = list()
cmp = compare comparison_function = 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() proc/IsEmpty()
if(!L.len) return 0 return !queue.len
. = L[1]
Remove(1)
Remove(i) proc/Enqueue(var/data)
if(i > L.len) return 0 queue.Add(data)
L.Swap(i,L.len) var/index = queue.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 //From what I can tell, this automagically sorts the added data into the correct location.
var/bestF while(index > 2 && call(comparison_function)(queue[index / 2], queue[index]) > 0)
proc queue.Swap(index, index / 2)
PathWeightCompare(PathNode/a, PathNode/b) index /= 2
return a.f - b.f
AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null) proc/Dequeue()
if(!queue.len)
return 0
return Remove(1)
// world << "A*: [start] [end] [adjacent] [dist] [maxnodes] [maxnodedepth] [mintargetdist], [minnodedist] [id]" proc/Remove(var/index)
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) if(index > queue.len)
var/closed[] = new() return 0
var/path[]
start = get_turf(start)
if(!start) return 0
open.Enqueue(new /PathNode(start,null,0,call(start,dist)(end))) var/thing = queue[index]
queue.Swap(index, queue.len)
queue.Cut(queue.len)
if(index < queue.len)
FixQueue(index)
return thing
while(!open.IsEmpty() && !path) proc/FixQueue(var/index)
{ var/child = 2 * index
var/PathNode/cur = open.Dequeue() var/item = queue[index]
closed.Add(cur.source)
var/closeenough while(child <= queue.len)
if(mintargetdist) if(child < queue.len && call(comparison_function)(queue[child], queue[child + 1]) > 0)
closeenough = call(cur.source,dist)(end) <= mintargetdist child++
if(call(comparison_function)(item, queue[child]) > 0)
if(cur.source == end || closeenough) queue[index] = queue[child]
path = new() index = child
path.Add(cur.source) else
while(cur.prevNode)
cur = cur.prevNode
path.Add(cur.source)
break break
child = 2 * index
queue[index] = item
var/L[] = call(cur.source,adjacent)(id) proc/List()
if(minnodedist && maxnodedepth) return queue.Copy()
if(call(cur.source,minnodedist)(end) + cur.nt >= maxnodedepth)
continue
else if(maxnodedepth)
if(cur.nt >= maxnodedepth)
continue
for(var/datum/d in L) proc/Length()
if(d == exclude) return queue.len
continue
var/ng = cur.g + call(cur.source,dist)(d) proc/RemoveItem(data)
if(d.bestF) var/index = queue.Find(data)
if(ng + call(d,dist)(end) < d.bestF) if(index)
for(var/i = 1; i <= open.L.len; i++) return Remove(index)
var/PathNode/n = open.L[i]
if(n.source == d) PathNode
open.Remove(i) var/datum/position
break var/PathNode/previous_node
var/best_estimated_cost
var/estimated_cost
var/known_cost
var/cost
var/nodes_traversed
New(_position, _previous_node, _known_cost, _cost, _nodes_traversed)
position = _position
previous_node = _previous_node
known_cost = _known_cost
cost = _cost
estimated_cost = cost + known_cost
best_estimated_cost = estimated_cost
nodes_traversed = _nodes_traversed
proc/PathWeightCompare(PathNode/a, PathNode/b)
return a.estimated_cost - b.estimated_cost
proc/AStar(var/start, var/end, var/proc/adjacent, var/proc/dist, var/max_nodes, var/max_node_depth = 30, var/min_target_dist = 0, var/min_node_dist, var/id, var/datum/exclude)
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare)
var/list/closed = list()
var/list/path
var/list/path_node_by_position = list()
start = get_turf(start)
if(!start)
return 0
open.Enqueue(new /PathNode(start, null, 0, call(start, dist)(end), 0))
while(!open.IsEmpty() && !path)
var/PathNode/current = open.Dequeue()
closed.Add(current.position)
if(current.position == end || call(current.position, dist)(end) <= min_target_dist)
path = new /list(current.nodes_traversed + 1)
path[path.len] = current.position
var/index = path.len - 1
while(current.previous_node)
current = current.previous_node
path[index--] = current.position
break
if(min_node_dist && max_node_depth)
if(call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth)
continue
if(max_node_depth)
if(current.nodes_traversed >= max_node_depth)
continue
for(var/datum/datum in call(current.position, adjacent)(id))
if(datum == exclude)
continue
var/best_estimated_cost = current.estimated_cost + call(current.position, dist)(datum)
//handle removal of sub-par positions
if(datum in path_node_by_position)
var/PathNode/target = path_node_by_position[datum]
if(target.best_estimated_cost)
if(best_estimated_cost + call(datum, dist)(end) < target.best_estimated_cost)
open.RemoveItem(target)
else else
continue continue
open.Enqueue(new /PathNode(d,cur,ng,call(d,dist)(end),cur.nt+1)) var/PathNode/next_node = new (datum, current, best_estimated_cost, call(datum, dist)(end), current.nodes_traversed + 1)
if(maxnodes && open.L.len > maxnodes) path_node_by_position[datum] = next_node
open.L.Cut(open.L.len) open.Enqueue(next_node)
}
var/PathNode/temp if(max_nodes && open.Length() > max_nodes)
while(!open.IsEmpty()) open.Remove(open.Length())
temp = open.Dequeue()
temp.source.bestF = 0
while(closed.len)
temp = closed[closed.len]
temp.bestF = 0
closed.Cut(closed.len)
if(path) return path
for(var/i = 1; i <= path.len/2; i++)
path.Swap(i,path.len-i+1)
return path