1 local function Graph_Search(self
, first
, last
)
3 local heuristic
= self
.h
5 while #open
> 0 do table.remove(open
) end
6 for _
, n
in ipairs(self
.nodes
) do n
.s
= 0 end
8 table.insert(open
, first
)
14 local current
= table.remove(open
)
16 if current
== last
then
24 for n
, d
in pairs(current
.n
) do
26 -- Haven't visited this node yet.
30 n
.h
= heuristic(n
, last
)
33 local mn
, mx
= 1, #open
+1
36 local m
= math
.floor((mn
+mx
)*0.5)
45 table.insert(open
, mn
, n
)
54 local mn
, mx
= 1, #open
57 local m
= math
.floor((mn
+mx
)*0.5)
58 if open
[m
].f
> of
then
65 while open
[mn
] ~= n
do
69 table.remove(open
, mn
)
72 local m
= math
.floor((mn
+mx
)*0.5)
81 table.insert(open
, mn
, n
)
92 local function sanity(list
, node
)
94 local contains
= false
95 for i
, n
in ipairs(list
) do
96 if not (not last
or last
>= n
.f
) then
97 for i
, n
in ipairs(list
) do
98 QuestHelper
:TextOut(i
..") "..n
.f
)
100 QuestHelper
:Error("Order "..i
.."/"..#list
.." ("..n
.f
..")")
102 assert(not last
or last
>= n
.f
)
108 if node
and not contains
then QuestHelper
:Error("Missing") end
111 local function Graph_Node_Link(self
, next_node
, distance
)
112 if type(distance
) ~= "number" or distance
< 0 then QuestHelper
:Error("Boom!") end
113 if self
~= next_node
then
114 self
.n
[next_node
] = distance
115 table.insert(next_node
.r
, self
)
119 local function Graph_CreateNode(self
)
120 local node
= QuestHelper
:CreateTable()
121 node
.Link
= Graph_Node_Link
122 node
.n
= QuestHelper
:CreateTable()
123 node
.r
= QuestHelper
:CreateTable()
124 table.insert(self
.nodes
, node
)
128 local function Graph_DestroyNode(self
, node
)
129 for i
= 1,#self
.nodes
do
130 if self
.nodes
[i
] == node
then
131 table.remove(self
.nodes
, i
)
132 QuestHelper
:ReleaseTable(node
.n
)
133 QuestHelper
:ReleaseTable(node
.r
)
134 QuestHelper
:ReleaseTable(node
)
140 local function Graph_Reset(self
)
141 while #self
.nodes
> 0 do
142 local node
= table.remove(self
.nodes
)
143 QuestHelper
:ReleaseTable(node
.n
)
144 QuestHelper
:ReleaseTable(node
.r
)
145 QuestHelper
:ReleaseTable(node
)
149 -- Tries to find a path from first to last.
150 -- heuristic is a function that takes two nodes and estimates how long a path between them would be.
151 -- If a path is found, last.p will be set to the second last node, it's .p will be set to the third
152 -- last node, and so on and so forth until you get to first, which will have .p set to nil. So, basically
153 -- you end up with a reversed linked list.
155 local function Graph_SetHeuristic(self
, heuristic
)
159 local function Graph_AddRouteStartNode(self
, n
, g
, end_list
)
160 local heuristic
= self
.h
161 local open
= self
.open
171 local e
= end_list
[1]
172 n
.h
= (heuristic(n
, e
)+e
.e
)*e
.w
173 for i
= 2,#end_list
do
175 n
.h
= math
.min(n
.h
, (heuristic(n
, e
)+e
.e
)*e
.w
)
180 local mn
, mx
= 1, #open
183 local m
= math
.floor((mn
+mx
)*0.5)
184 if open
[m
].f
> of
then
191 while open
[mn
] ~= n
do
195 table.remove(open
, mn
)
197 return -- Don't want to insert the node a second time.
203 local mn
, mx
= 1, #open
+1
206 local m
= math
.floor((mn
+mx
)*0.5)
208 if open
[m
].f
> f
then
215 table.insert(open
, mn
, n
)
221 local function Graph_DoRouteSearch(self
, end_list
)
222 local heuristic
= self
.h
223 local open
= self
.open
224 local end_count
= #end_list
227 local current
= table.remove(open
)
229 if current
.s
== 3 or current
.s
== 4 then
238 for n
, d
in pairs(current
.n
) do
239 if n
.s
== 0 or n
.s
== 3 then
240 -- Haven't visited this node yet.
252 local e
= end_list
[1]
253 n
.h
= (heuristic(n
, e
)+e
.e
)*e
.w
255 for i
= 2,end_count
do
257 n
.h
= math
.min(n
.h
, (heuristic(n
, e
)+e
.e
)*e
.w
)
263 local mn
, mx
= 1, #open
+1
266 local m
= math
.floor((mn
+mx
)*0.5)
268 if open
[m
].f
> f
then
277 table.insert(open
, mn
, n
)
279 elseif n
.s
== 1 or n
.s
== 4 then
286 local mn
, mx
= 1, #open
289 local m
= math
.floor((mn
+mx
)*0.5)
290 if open
[m
].f
> of
then
297 while open
[mn
] ~= n
do
303 table.remove(open
, mn
)
306 local m
= math
.floor((mn
+mx
)*0.5)
308 if open
[m
].f
> f
then
316 table.insert(open
, mn
, n
)
323 local function Graph_PrepareSearch(self
)
324 local open
= self
.open
325 while #open
> 0 do table.remove(open
) end
326 for _
, n
in ipairs(self
.nodes
) do
331 local function Graph_AddStartNode(self
, n
, g
, end_list
)
332 local heuristic
= self
.h
333 local open
= self
.open
343 local e
= end_list
[1]
344 n
.h
= (heuristic(n
, e
)+e
.e
)*e
.w
345 for i
= 2,#end_list
do
347 n
.h
= math
.min(n
.h
, (heuristic(n
, e
)+e
.e
)*e
.w
)
352 local mn
, mx
= 1, #open
355 local m
= math
.floor((mn
+mx
)*0.5)
356 if open
[m
].f
> of
then
363 while open
[mn
] ~= n
do
367 table.remove(open
, mn
)
369 return -- Don't want to insert the node a second time.
375 local mn
, mx
= 1, #open
+1
378 local m
= math
.floor((mn
+mx
)*0.5)
380 if open
[m
].f
> f
then
387 table.insert(open
, mn
, n
)
393 local function Graph_DoSearch(self
, end_list
)
394 local heuristic
= self
.h
395 local open
= self
.open
396 local end_count
= #end_list
399 local current
= table.remove(open
)
401 if current
.s
== 3 or current
.s
== 4 then
410 for n
, d
in pairs(current
.n
) do
411 if n
.s
== 0 or n
.s
== 3 then
412 -- Haven't visited this node yet.
424 local e
= end_list
[1]
425 n
.h
= (heuristic(n
, e
)+e
.e
)*e
.w
427 for i
= 2,end_count
do
429 n
.h
= math
.min(n
.h
, (heuristic(n
, e
)+e
.e
)*e
.w
)
435 local mn
, mx
= 1, #open
+1
438 local m
= math
.floor((mn
+mx
)*0.5)
440 if open
[m
].f
> f
then
449 table.insert(open
, mn
, n
)
451 elseif n
.s
== 1 or n
.s
== 4 then
458 local mn
, mx
= 1, #open
461 local m
= math
.floor((mn
+mx
)*0.5)
462 if open
[m
].f
> of
then
469 while open
[mn
] ~= n
do
475 table.remove(open
, mn
)
478 local m
= math
.floor((mn
+mx
)*0.5)
480 if open
[m
].f
> f
then
488 table.insert(open
, mn
, n
)
497 local function Graph_DoFullSearch(self
, end_list
)
498 local heuristic
= self
.h
499 local open
= self
.open
500 local end_count
= #end_list
503 local current
= table.remove(open
)
505 if current
.s
== 3 or current
.s
== 4 then
506 if end_count
== 1 then
507 for i
= 1,#removed
do
508 table.insert(end_list
, table.remove(removed
))
513 for i
= 1,end_count
do
514 if end_list
[i
] == current
then
515 table.insert(removed
, table.remove(end_list
, i
))
520 end_count
= end_count
- 1
527 for n
, d
in pairs(current
.n
) do
528 if n
.s
== 0 or n
.s
== 3 then
529 -- Haven't visited this node yet.
541 local e
= end_list
[1]
542 n
.h
= (heuristic(n
, e
)+e
.e
)*e
.w
543 for i
= 2,end_count
do
545 n
.h
= math
.min(n
.h
, (heuristic(n
, e
)+e
.e
)*e
.w
)
551 local mn
, mx
= 1, #open
+1
554 local m
= math
.floor((mn
+mx
)*0.5)
556 if open
[m
].f
> f
then
565 table.insert(open
, mn
, n
)
567 elseif n
.s
== 1 or n
.s
== 4 then
574 local mn
, mx
= 1, #open
577 local m
= math
.floor((mn
+mx
)*0.5)
578 if open
[m
].f
> of
then
585 while open
[mn
] ~= n
do
591 table.remove(open
, mn
)
594 local m
= math
.floor((mn
+mx
)*0.5)
596 if open
[m
].f
> f
then
604 table.insert(open
, mn
, n
)
610 for i
, n
in ipairs(end_list
) do
611 QuestHelper
:TextOut(i
..") Failed to reach: "..n
.name
..", s="..n
.s
)
614 for i
= 1,#removed
do
615 table.insert(end_list
, table.remove(removed
))
618 for i
, n
in ipairs(end_list
) do
619 QuestHelper
:TextOut(i
..") End node: "..n
.name
..", s="..n
.s
)
622 QuestHelper
:Error("Boom!")
624 for i
= 1,#removed
do
625 table.insert(end_list
, table.remove(removed
))
629 local function propLinks(node
)
632 for n
in pairs(node
.n
) do
638 function Graph_SanityCheck(self
)
639 for i
= 1,#self
.nodes
do
640 for _
, n
in ipairs(self
.nodes
) do
644 propLinks(self
.nodes
[i
])
646 for _
, n
in ipairs(self
.nodes
) do
648 QuestHelper
:TextOut((n
.name
or "unknown").." isn't reachable from "..(self
.nodes
[i
].name
or "unknown"))
654 function QuestHelper
:CreateGraph()
655 local graph
= self
:CreateTable()
656 graph
.nodes
= self
:CreateTable()
657 graph
.end_nodes
= self
:CreateTable()
658 graph
.open
= self
:CreateTable()
660 graph
.CreateNode
= Graph_CreateNode
661 graph
.DestroyNode
= Graph_DestroyNode
662 graph
.Reset
= Graph_Reset
664 graph
.SetHeuristic
= Graph_SetHeuristic
665 graph
.PrepareSearch
= Graph_PrepareSearch
667 graph
.AddRouteStartNode
= Graph_AddRouteStartNode
668 graph
.DoRouteSearch
= Graph_DoRouteSearch
669 graph
.Search
= Graph_Search
670 graph
.SanityCheck
= Graph_SanityCheck
672 -- These don't deal with finding paths and instead only care about finding distances.
673 graph
.AddStartNode
= Graph_AddStartNode
674 graph
.DoSearch
= Graph_DoSearch
675 graph
.DoFullSearch
= Graph_DoFullSearch
680 function QuestHelper
:ReleaseGraph(graph
)
682 self
:ReleaseTable(graph
.nodes
)
683 self
:ReleaseTable(graph
.end_nodes
)
684 self
:ReleaseTable(graph
.open
)
685 self
:ReleaseTable(graph
)
688 QuestHelper
.world_graph
= QuestHelper
:CreateGraph()