mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 14:39:58 +01:00
Minor modifications to Yvar's A* fixes:
-Removed the priorityqueue datum. It is now a list() -priorityqueue/proc/insert is now a helper /proc/sorted_insert() as it may be helpful in maintaining pre-sorted lists or insertion sorts. -Replaced priorityqueue/proc/extract_last() calls with calls to pop(), which already existed. -Removed last few references to "bestF" (from old awful A) in lighting and MC -Replaced a section were it'd append to the end of the returned path list and then reverse the list. Now it inserts at the start of the list.
This commit is contained in:
+10
-6
@@ -134,12 +134,16 @@ proc/listclearnulls(list/list)
|
||||
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
|
||||
|
||||
//Returns the top(last) element from the list and removes it from the list (typical stack function)
|
||||
/proc/pop(list/listfrom)
|
||||
if (listfrom.len > 0)
|
||||
var/picked = listfrom[listfrom.len]
|
||||
listfrom.len--
|
||||
return picked
|
||||
return null
|
||||
/proc/pop(list/L)
|
||||
if(L.len)
|
||||
. = L[L.len]
|
||||
L.len--
|
||||
|
||||
/proc/sorted_insert(list/L, thing, comparator)
|
||||
var/pos = L.len
|
||||
while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
|
||||
pos--
|
||||
L.Insert(pos+1, thing)
|
||||
|
||||
/*
|
||||
* Sorting
|
||||
|
||||
@@ -117,7 +117,7 @@ datum/controller/lighting/proc/Recover()
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] lighting_controller restarted. Reports:\n"
|
||||
for(var/varname in lighting_controller.vars)
|
||||
switch(varname)
|
||||
if("tag","bestF","type","parent_type","vars") continue
|
||||
if("tag","type","parent_type","vars") continue
|
||||
else
|
||||
var/varval1 = lighting_controller.vars[varname]
|
||||
var/varval2 = vars[varname]
|
||||
|
||||
@@ -287,7 +287,7 @@ datum/controller/game_controller/proc/Recover() //Mostly a placeholder for now.
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
|
||||
for(var/varname in master_controller.vars)
|
||||
switch(varname)
|
||||
if("tag","bestF","type","parent_type","vars") continue
|
||||
if("tag","type","parent_type","vars") continue
|
||||
else
|
||||
var/varval = master_controller.vars[varname]
|
||||
if(istype(varval,/datum))
|
||||
|
||||
+33
-60
@@ -1,25 +1,5 @@
|
||||
priorityqueue
|
||||
var/pathnode/nodes[] //list of pathnodes, sorted descending by f_score
|
||||
New()
|
||||
nodes = new()
|
||||
|
||||
priorityqueue/proc/insert(pathnode/n)
|
||||
if (!nodes.len)
|
||||
nodes.Add(n)
|
||||
else
|
||||
//linear search - slower than binary but guaranteed to work!
|
||||
var/pos = nodes.len
|
||||
while (pos > 0 && compare(n,nodes[pos]) > 0)
|
||||
pos--
|
||||
nodes.Insert(pos+1,n)
|
||||
|
||||
priorityqueue/proc/extract_last()
|
||||
var/pathnode/t = nodes[nodes.len]
|
||||
nodes.Cut(nodes.len)
|
||||
return t
|
||||
|
||||
priorityqueue/proc/compare(pathnode/a, pathnode/b)
|
||||
return (a.f_score - b.f_score)
|
||||
/proc/cmp_pathnodes(pathnode/A, pathnode/B)
|
||||
return (A.f_score - B.f_score)
|
||||
|
||||
pathnode
|
||||
var/turf/me //turf
|
||||
@@ -81,87 +61,80 @@ near a target, but not right to it - for an AI mob with a gun, for example."
|
||||
length to avoid portals or something i guess?? Not that they're counted right now but w/e."
|
||||
*********************/
|
||||
proc/AStar(turf/start_turf, turf/dest_turf, adjacent_proc, distance_proc, maxnodes, max_length, id=null, var/turf/exclude=null)
|
||||
set background=1 //don't hog the CPU
|
||||
set background=1 //allows proc to sleep should it take more than a single frame to complete
|
||||
|
||||
var/const/DEPTH_BIAS = 0.5 //preference of discovered paths. decrease with extreme caution and don't set >1!
|
||||
//var/evaluations = 0 //Total number of tiles examined, including discards and duplicates. Used for debugging
|
||||
var/lpct = 0 //Loop counter - turfs properly examined. Debugging and infinite loop prevention.
|
||||
var/pathnode/checked[] = new() //list of pathnodes already examined
|
||||
var/priorityqueue/pq = new /priorityqueue() //list of pathnodes not yet examined, sorted descending by f_score
|
||||
var/list/checked = list() //list of pathnodes already examined
|
||||
var/list/priorityqueue = list() //list of pathnodes not yet examined, sorted descending by f_score
|
||||
var/pathnode/current //current pathnode being examined
|
||||
var/next_g //calculated g_score of a potential turf
|
||||
var/next_f //calculated f_score of a potential turf
|
||||
var/skip //used for internal logic branching, don't touch.
|
||||
var/pathnode/temp //temporary pathnode used for comparison, don't touch.
|
||||
|
||||
var/list/path = list() //return value
|
||||
|
||||
//sanity check to avoid a huge waste of CPU time on impossible tasks
|
||||
for(var/obj/O in dest_turf)
|
||||
if (O.density && (!istype(O, /obj/machinery/door) || !(O.flags & ON_BORDER)))
|
||||
var/list/path = new //something is blocking the destination
|
||||
if(O.density && (!istype(O, /obj/machinery/door) || !(O.flags & ON_BORDER))) //something is blocking the destination
|
||||
return path
|
||||
|
||||
priorityqueue += new /pathnode(start_turf, null, call(start_turf,distance_proc)(dest_turf), 0)//add the starting turf and manhattan distance to the queue!
|
||||
|
||||
pq.insert(new /pathnode(start_turf, null, call(start_turf,distance_proc)(dest_turf),0)) //add the starting turf and manhattan distance to the queue!
|
||||
for(lpct=0, lpct<1800, lpct++) //maximum for DEPTH_BIAS=0.5 is 958 loops to Research Division (originally 2743)
|
||||
if(!priorityqueue.len) break
|
||||
|
||||
for (lpct = 0; lpct < 1800; lpct++) //maximum for DEPTH_BIAS=0.5 is 958 loops to Research Division (originally 2743)
|
||||
if (!pq.nodes.len) break
|
||||
|
||||
current = pq.extract_last()
|
||||
current = pop(priorityqueue)
|
||||
checked.Add(current)
|
||||
|
||||
next_g = current.g_score + DEPTH_BIAS
|
||||
//if (maxnodes && next_g > (maxnodes*DEPTH_BIAS)) continue
|
||||
//if(maxnodes && next_g > (maxnodes*DEPTH_BIAS)) continue
|
||||
|
||||
if (!current) //no more possibilities, or an unhandled error
|
||||
if(!current) //no more possibilities, or an unhandled error
|
||||
//world << "Impossible path"
|
||||
var/list/path = new //returning 0 will get runtime errors
|
||||
return path //FAILURE
|
||||
|
||||
if (current.me == dest_turf) //reached the destination
|
||||
var/list/path = new()
|
||||
path.Add(current.me)
|
||||
while (current.parent) //recreate path by looping through parents
|
||||
if(current.me == dest_turf) //reached the destination
|
||||
while(current) //recreate path by looping through parents
|
||||
path.Insert(1, current.me)
|
||||
current = current.parent
|
||||
path.Add(current.me)
|
||||
//world << "Path found! ([path.len] steps, [lpct] loops, [evaluations] evaluations)"
|
||||
|
||||
for(var/i = 1; i <= path.len/2; i++) //reverse path
|
||||
path.Swap(i,path.len-i+1)
|
||||
|
||||
if (path.len > max_length) //Truncate list at max_length (if necessary)
|
||||
path.Cut(max_length,0)
|
||||
path.len = min(path.len, max_length-1)
|
||||
return path //SUCCESS
|
||||
|
||||
var/adjacent = call(current.me,adjacent_proc)(id)
|
||||
for (var/next_turf in adjacent)
|
||||
for(var/next_turf in adjacent)
|
||||
//evaluations++
|
||||
if (next_turf == exclude)
|
||||
if(next_turf == exclude)
|
||||
continue
|
||||
skip = 0
|
||||
next_f = next_g + call(next_turf,distance_proc)(dest_turf)
|
||||
var/i = 0
|
||||
while ( i < checked.len && !skip)
|
||||
while(i < checked.len && !skip)
|
||||
i++
|
||||
temp = checked[i]
|
||||
if (temp.me == next_turf) //if this turf has already been checked
|
||||
if(temp.me == next_turf) //if this turf has already been checked
|
||||
skip = 1
|
||||
if (temp.f_score > next_f) //BUT the current path to it is better
|
||||
if(temp.f_score > next_f) //BUT the current path to it is better
|
||||
checked[i] = new /pathnode(next_turf, current, next_f, next_g)
|
||||
//then update it
|
||||
if (!skip) //if it hasn't been checked
|
||||
if(!skip) //if it hasn't been checked
|
||||
i = 0
|
||||
while (i < pq.nodes.len && !skip)
|
||||
while(i < priorityqueue.len && !skip)
|
||||
i++
|
||||
temp = pq.nodes[i]
|
||||
if (temp.me == next_turf) //if this turf was already on the openlist
|
||||
if (temp.f_score > next_f) //BUT the current path to it is better
|
||||
pq.nodes.Cut(i,i+1) //then remove it
|
||||
temp = priorityqueue[i]
|
||||
if(temp.me == next_turf) //if this turf was already on the openlist
|
||||
if(temp.f_score > next_f) //BUT the current path to it is better
|
||||
priorityqueue.Cut(i,i+1) //then remove it
|
||||
skip = 2
|
||||
else
|
||||
skip = 1 //otherwise leave it and don't add this version
|
||||
if (!(skip % 2))
|
||||
pq.insert(new /pathnode(next_turf, current, next_f, next_g))
|
||||
|
||||
if(!(skip % 2))
|
||||
sorted_insert(priorityqueue, new /pathnode(next_turf, current, next_f, next_g), /proc/cmp_pathnodes)
|
||||
|
||||
//world << "Didn't find a path within [evaluations] evaluations and [lpct] loops."
|
||||
var/list/path = new //prevents runtime errors
|
||||
return path //FAILURE
|
||||
//This return should only be reached if the path is too long, and the while loop gives up.
|
||||
|
||||
Reference in New Issue
Block a user