mirror of
https://github.com/fulpstation/fulpstation.git
synced 2025-12-09 16:09:15 +00:00
[READY]Astar improvement (#34713)
Improvements to A* via using bitflags to find directions and caching already checked directions so we don't recheck them.
This commit is contained in:
@@ -75,6 +75,7 @@
|
||||
#define INIT_ORDER_LIGHTING -20
|
||||
#define INIT_ORDER_SHUTTLE -21
|
||||
#define INIT_ORDER_SQUEAK -40
|
||||
#define INIT_ORDER_PATH -50
|
||||
#define INIT_ORDER_PERSISTENCE -100
|
||||
|
||||
// Subsystem fire priority, from lowest to highest priority
|
||||
|
||||
@@ -23,10 +23,14 @@ Actual Adjacent procs :
|
||||
/turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos
|
||||
|
||||
*/
|
||||
|
||||
#define PF_TIEBREAKER 0.005
|
||||
//tiebreker weight.To help to choose between equal paths
|
||||
//////////////////////
|
||||
//datum/PathNode object
|
||||
//////////////////////
|
||||
#define MASK_ODD 85
|
||||
#define MASK_EVEN 170
|
||||
|
||||
|
||||
//A* nodes variables
|
||||
/datum/PathNode
|
||||
@@ -36,13 +40,22 @@ Actual Adjacent procs :
|
||||
var/g //A* movement cost variable
|
||||
var/h //A* heuristic variable
|
||||
var/nt //count the number of Nodes traversed
|
||||
var/bf //bitflag for dir to expand.Some sufficiently advanced motherfuckery
|
||||
|
||||
/datum/PathNode/New(s,p,pg,ph,pnt)
|
||||
/datum/PathNode/New(s,p,pg,ph,pnt,_bf)
|
||||
source = s
|
||||
prevNode = p
|
||||
g = pg
|
||||
h = ph
|
||||
f = g + h
|
||||
f = g + h*(1+ PF_TIEBREAKER)
|
||||
nt = pnt
|
||||
bf = _bf
|
||||
|
||||
/datum/PathNode/proc/setp(p,pg,ph,pnt)
|
||||
prevNode = p
|
||||
g = pg
|
||||
h = ph
|
||||
f = g + h*(1+ PF_TIEBREAKER)
|
||||
nt = pnt
|
||||
|
||||
/datum/PathNode/proc/calc_f()
|
||||
@@ -61,117 +74,114 @@ Actual Adjacent procs :
|
||||
return b.f - a.f
|
||||
|
||||
//wrapper that returns an empty list if A* failed to find a path
|
||||
/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1)
|
||||
/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableTurftest, id=null, turf/exclude=null, simulated_only = 1)
|
||||
var/l = SSpathfinder.getfree(caller)
|
||||
while(!l)
|
||||
stoplag(3)
|
||||
l = SSpathfinder.getfree(caller)
|
||||
var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent,id, exclude, simulated_only)
|
||||
|
||||
SSpathfinder.found(l)
|
||||
if(!path)
|
||||
path = list()
|
||||
|
||||
return path
|
||||
|
||||
//the actual algorithm
|
||||
/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1)
|
||||
var/list/pnodelist = list()
|
||||
/proc/AStar(caller, var/turf/end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableTurftest, id=null, turf/exclude=null, simulated_only = 1)
|
||||
//sanitation
|
||||
var/start = get_turf(caller)
|
||||
if(!start)
|
||||
var/turf/start = get_turf(caller)
|
||||
if((!start)||(start.z != end.z)||(start == end)) //no pathfinding between z levels
|
||||
return 0
|
||||
|
||||
if(maxnodes)
|
||||
//if start turf is farther than maxnodes from end turf, no need to do anything
|
||||
if(call(start, dist)(end) > maxnodes)
|
||||
return 0
|
||||
maxnodedepth = maxnodes //no need to consider path longer than maxnodes
|
||||
|
||||
var/datum/Heap/open = new /datum/Heap(/proc/HeapPathWeightCompare) //the open list
|
||||
var/list/closed = new() //the closed list
|
||||
var/list/openc = new() //open list for node check
|
||||
var/list/path = null //the returned path, if any
|
||||
var/datum/PathNode/cur //current processed turf
|
||||
|
||||
//initialization
|
||||
open.Insert(new /datum/PathNode(start,null,0,call(start,dist)(end),0))
|
||||
|
||||
var/datum/PathNode/cur = new /datum/PathNode(start,null,0,call(start,dist)(end),0,15,1)//current processed turf
|
||||
open.Insert(cur)
|
||||
openc[start] = cur
|
||||
//then run the main loop
|
||||
while(!open.IsEmpty() && !path)
|
||||
//get the lower f node on the open list
|
||||
cur = open.Pop() //get the lower f turf in the open list
|
||||
closed.Add(cur.source) //and tell we've processed it
|
||||
|
||||
//get the lower f node on the open list
|
||||
//if we only want to get near the target, check if we're close enough
|
||||
var/closeenough
|
||||
if(mintargetdist)
|
||||
closeenough = call(cur.source,dist)(end) <= mintargetdist
|
||||
|
||||
//if too many steps, abandon that path
|
||||
if(maxnodedepth && (cur.nt > maxnodedepth))
|
||||
continue
|
||||
|
||||
//found the target turf (or close enough), let's create the path to it
|
||||
if(cur.source == end || closeenough)
|
||||
path = new()
|
||||
path.Add(cur.source)
|
||||
|
||||
while(cur.prevNode)
|
||||
cur = cur.prevNode
|
||||
path.Add(cur.source)
|
||||
|
||||
break
|
||||
|
||||
//get adjacents turfs using the adjacent proc, checking for access with id
|
||||
var/list/L = call(cur.source,adjacent)(caller,id, simulated_only)
|
||||
for(var/turf/T in L)
|
||||
if(T == exclude || (T in closed))
|
||||
continue
|
||||
|
||||
var/newg = cur.g + call(cur.source,dist)(T)
|
||||
|
||||
var/datum/PathNode/P = pnodelist[T]
|
||||
if(!P)
|
||||
//is not already in open list, so add it
|
||||
var/datum/PathNode/newnode = new /datum/PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)
|
||||
open.Insert(newnode)
|
||||
pnodelist[T] = newnode
|
||||
else //is already in open list, check if it's a better way from the current turf
|
||||
if(newg < P.g)
|
||||
P.prevNode = cur
|
||||
P.g = (newg * L.len / 9)
|
||||
P.calc_f()
|
||||
P.nt = cur.nt + 1
|
||||
open.ReSort(P)//reorder the changed element in the list
|
||||
for(var/i = 0 to 3)
|
||||
var/f= 1<<i //get cardinal directions.1,2,4,8
|
||||
if(cur.bf & f)
|
||||
var/T = get_step(cur.source,f)
|
||||
if(T != exclude)
|
||||
var/datum/PathNode/CN = openc[T] //current checking turf
|
||||
var/r=((f & MASK_ODD)<<1)|((f & MASK_EVEN)>>1) //getting reverse direction throught swapping even and odd bits.((f & 01010101)<<1)|((f & 10101010)>>1)
|
||||
var/newg = cur.g + call(cur.source,dist)(T)
|
||||
if(CN)
|
||||
//is already in open list, check if it's a better way from the current turf
|
||||
CN.bf &= 15^r //we have no closed, so just cut off exceed dir.00001111 ^ reverse_dir.We don't need to expand to checked turf.
|
||||
if(newg < CN.g)
|
||||
if(call(cur.source,adjacent)(caller, T, id, simulated_only))
|
||||
CN.setp(cur,newg,CN.h,cur.nt+1)
|
||||
open.ReSort(CN)//reorder the changed element in the list
|
||||
else
|
||||
//is not already in open list, so add it
|
||||
if(call(cur.source,adjacent)(caller, T, id, simulated_only))
|
||||
CN = new(T,cur,newg,call(T,dist)(end),cur.nt+1,15^r)
|
||||
open.Insert(CN)
|
||||
openc[T] = CN
|
||||
cur.bf = 0
|
||||
CHECK_TICK
|
||||
|
||||
|
||||
//cleaning after us
|
||||
pnodelist = null
|
||||
|
||||
//reverse the path to get it from start to finish
|
||||
if(path)
|
||||
for(var/i = 1; i <= path.len/2; i++)
|
||||
for(var/i = 1 to round(0.5*path.len))
|
||||
path.Swap(i,path.len-i+1)
|
||||
|
||||
openc = null
|
||||
//cleaning after us
|
||||
return path
|
||||
|
||||
//Returns adjacent turfs in cardinal directions that are reachable
|
||||
//simulated_only controls whether only simulated turfs are considered or not
|
||||
|
||||
/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only)
|
||||
var/list/L = new()
|
||||
var/turf/T
|
||||
var/static/space_type_cache = typecacheof(/turf/open/space)
|
||||
|
||||
for(var/dir in GLOB.cardinals)
|
||||
T = get_step(src,dir)
|
||||
for(var/k in 1 to GLOB.cardinals.len)
|
||||
T = get_step(src,GLOB.cardinals[k])
|
||||
if(!T || (simulated_only && space_type_cache[T.type]))
|
||||
continue
|
||||
if(!T.density && !LinkBlockedWithAccess(T,caller, ID))
|
||||
L.Add(T)
|
||||
return L
|
||||
|
||||
/turf/proc/reachableTurftest(caller, var/turf/T, ID, simulated_only)
|
||||
if(T && !T.density && !(simulated_only && SSpathfinder.space_type_cache[T.type]) && !LinkBlockedWithAccess(T,caller, ID))
|
||||
return TRUE
|
||||
|
||||
//Returns adjacent turfs in cardinal directions that are reachable via atmos
|
||||
/turf/proc/reachableAdjacentAtmosTurfs()
|
||||
return atmos_adjacent_turfs
|
||||
|
||||
/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID)
|
||||
var/adir = get_dir(src, T)
|
||||
var/rdir = get_dir(T, src)
|
||||
|
||||
var/rdir = ((adir & MASK_ODD)<<1)|((adir & MASK_EVEN)>>1)
|
||||
for(var/obj/structure/window/W in src)
|
||||
if(!W.CanAStarPass(ID, adir))
|
||||
return 1
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
L[1] = L[L.len]
|
||||
L.Cut(L.len)
|
||||
|
||||
Sink(1)
|
||||
if(L.len)
|
||||
Sink(1)
|
||||
|
||||
//Get a node up to its right position in the heap
|
||||
/datum/Heap/proc/Swim(var/index)
|
||||
@@ -73,3 +73,4 @@
|
||||
|
||||
/datum/Heap/proc/List()
|
||||
. = L.Copy()
|
||||
|
||||
|
||||
38
code/controllers/subsystem/pathfinder.dm
Normal file
38
code/controllers/subsystem/pathfinder.dm
Normal file
@@ -0,0 +1,38 @@
|
||||
SUBSYSTEM_DEF(pathfinder)
|
||||
name = "pathfinder"
|
||||
init_order = INIT_ORDER_PATH
|
||||
flags = SS_NO_FIRE
|
||||
var/lcount = 10
|
||||
var/run
|
||||
var/free
|
||||
var/list/flow
|
||||
var/static/space_type_cache
|
||||
|
||||
/datum/controller/subsystem/pathfinder/Initialize()
|
||||
space_type_cache = typecacheof(/turf/open/space)
|
||||
run = 0
|
||||
free = 1
|
||||
flow = new()
|
||||
flow.len=lcount
|
||||
|
||||
/datum/controller/subsystem/pathfinder/proc/getfree(atom/M)
|
||||
if(run < lcount)
|
||||
run += 1
|
||||
while(flow[free])
|
||||
CHECK_TICK
|
||||
free = (free % lcount) + 1
|
||||
var/t = addtimer(CALLBACK(SSpathfinder, /datum/controller/subsystem/pathfinder.proc/toolong, free), 150, TIMER_STOPPABLE)
|
||||
flow[free] = t
|
||||
flow[t] = M
|
||||
return free
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/controller/subsystem/pathfinder/proc/toolong(l)
|
||||
log_game("Pathfinder route took longer than 150 ticks, src bot [flow[flow[l]]]")
|
||||
found(l)
|
||||
|
||||
/datum/controller/subsystem/pathfinder/proc/found(l)
|
||||
deltimer(flow[l])
|
||||
flow[l] = null
|
||||
run -= 1
|
||||
@@ -220,6 +220,7 @@
|
||||
#include "code\controllers\subsystem\overlays.dm"
|
||||
#include "code\controllers\subsystem\pai.dm"
|
||||
#include "code\controllers\subsystem\parallax.dm"
|
||||
#include "code\controllers\subsystem\pathfinder.dm"
|
||||
#include "code\controllers\subsystem\persistence.dm"
|
||||
#include "code\controllers\subsystem\radiation.dm"
|
||||
#include "code\controllers\subsystem\radio.dm"
|
||||
|
||||
Reference in New Issue
Block a user