Made purge command reset the locale of the saved data, and added files for 0.44 release.
[QuestHelper.git] / graph.lua
blobd8c9ebd949b353c164e3a05a611c0c109472735d
1 local function Graph_Search(self, first, last)
2 if first ~= last then
3 local heuristic = self.h
4 local open = self.open
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)
10 first.g = 0
11 first.s = 2
13 while #open > 0 do
14 local current = table.remove(open)
16 if current == last then
17 first.p = nil
18 return true
19 end
21 current.s = 2
22 local cd = current.g
24 for n, d in pairs(current.n) do
25 if n.s == 0 then
26 -- Haven't visited this node yet.
27 n.g = cd+d
28 n.s = 1
29 n.p = current
30 n.h = heuristic(n, last)
31 local f = n.g+n.h
33 local mn, mx = 1, #open+1
35 while mn ~= mx do
36 local m = math.floor((mn+mx)*0.5)
38 if open[m].f > f then
39 mn = m+1
40 else
41 mx = m
42 end
43 end
45 table.insert(open, mn, n)
46 n.f = f
47 elseif n.s == 1 then
48 local g = cd+d
49 local f = g+n.h
50 if f < n.f then
51 n.g = g
52 n.p = current
53 local of = n.f
54 local mn, mx = 1, #open
56 while mn ~= mx do
57 local m = math.floor((mn+mx)*0.5)
58 if open[m].f > of then
59 mn = m+1
60 else
61 mx = m
62 end
63 end
65 while open[mn] ~= n do
66 mn = mn + 1
67 end
68 mx = #open
69 table.remove(open, mn)
71 while mn ~= mx do
72 local m = math.floor((mn+mx)*0.5)
74 if open[m].f > f then
75 mn = m+1
76 else
77 mx = m
78 end
79 end
81 table.insert(open, mn, n)
82 n.f = f
83 end
84 end
85 end
86 end
87 end
88 last.p = nil
89 return first == last
90 end
92 local function sanity(list, node)
93 local last = nil
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)
99 end
100 QuestHelper:Error("Order "..i.."/"..#list.." ("..n.f..")")
102 assert(not last or last >= n.f)
103 last = n.f
104 if n == node then
105 contains = true
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)
125 return 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)
135 break
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)
156 self.h = heuristic
159 local function Graph_AddRouteStartNode(self, n, g, end_list)
160 local heuristic = self.h
161 local open = self.open
163 n.p = nil
165 if n.s == 3 then
166 n.s = 4
167 n.h = n.e*n.w
168 elseif n.s == 0 then
169 n.s = 1
171 local e = end_list[1]
172 n.h = (heuristic(n, e)+e.e)*e.w
173 for i = 2,#end_list do
174 e = end_list[i]
175 n.h = math.min(n.h, (heuristic(n, e)+e.e)*e.w)
177 else
178 local of = n.f
179 if g+n.h < of then
180 local mn, mx = 1, #open
182 while mn ~= mx do
183 local m = math.floor((mn+mx)*0.5)
184 if open[m].f > of then
185 mn = m+1
186 else
187 mx = m
191 while open[mn] ~= n do
192 mn = mn + 1
195 table.remove(open, mn)
196 else
197 return -- Don't want to insert the node a second time.
201 local f = g+n.h
203 local mn, mx = 1, #open+1
205 while mn ~= mx do
206 local m = math.floor((mn+mx)*0.5)
208 if open[m].f > f then
209 mn = m+1
210 else
211 mx = m
215 table.insert(open, mn, n)
217 n.g = g
218 n.f = f
221 local function Graph_DoRouteSearch(self, end_list)
222 local heuristic = self.h
223 local open = self.open
224 local end_count = #end_list
226 while #open > 0 do
227 local current = table.remove(open)
229 if current.s == 3 or current.s == 4 then
230 current.s = 2
231 return current
234 current.s = 2
236 local cd = current.g
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.
241 n.p = current
242 local f
244 if n.s == 3 then
245 n.s = 4
246 n.h = n.e*n.w
247 n.g = cd+d
248 else
249 n.s = 1
250 n.g = cd+d
252 local e = end_list[1]
253 n.h = (heuristic(n, e)+e.e)*e.w
255 for i = 2,end_count do
256 e = end_list[i]
257 n.h = math.min(n.h, (heuristic(n, e)+e.e)*e.w)
261 local f = n.g+n.h
263 local mn, mx = 1, #open+1
265 while mn ~= mx do
266 local m = math.floor((mn+mx)*0.5)
268 if open[m].f > f then
269 mn = m+1
270 else
271 mx = m
275 n.f = f
277 table.insert(open, mn, n)
279 elseif n.s == 1 or n.s == 4 then
280 local g = cd+d
281 local f = g+n.h
282 if f < n.f then
283 n.p = current
284 local of = n.f
286 local mn, mx = 1, #open
288 while mn ~= mx do
289 local m = math.floor((mn+mx)*0.5)
290 if open[m].f > of then
291 mn = m+1
292 else
293 mx = m
297 while open[mn] ~= n do
298 mn = mn + 1
301 mx = #open
303 table.remove(open, mn)
305 while mn ~= mx do
306 local m = math.floor((mn+mx)*0.5)
308 if open[m].f > f then
309 mn = m+1
310 else
311 mx = m
315 n.f = f
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
327 n.s = 0
331 local function Graph_AddStartNode(self, n, g, end_list)
332 local heuristic = self.h
333 local open = self.open
335 n.p = n
337 if n.s == 3 then
338 n.s = 4
339 n.h = n.e*n.w
340 elseif n.s == 0 then
341 n.s = 1
343 local e = end_list[1]
344 n.h = (heuristic(n, e)+e.e)*e.w
345 for i = 2,#end_list do
346 e = end_list[i]
347 n.h = math.min(n.h, (heuristic(n, e)+e.e)*e.w)
349 else
350 local of = n.f
351 if g+n.h < of then
352 local mn, mx = 1, #open
354 while mn ~= mx do
355 local m = math.floor((mn+mx)*0.5)
356 if open[m].f > of then
357 mn = m+1
358 else
359 mx = m
363 while open[mn] ~= n do
364 mn = mn + 1
367 table.remove(open, mn)
368 else
369 return -- Don't want to insert the node a second time.
373 local f = g+n.h
375 local mn, mx = 1, #open+1
377 while mn ~= mx do
378 local m = math.floor((mn+mx)*0.5)
380 if open[m].f > f then
381 mn = m+1
382 else
383 mx = m
387 table.insert(open, mn, n)
389 n.g = g
390 n.f = f
393 local function Graph_DoSearch(self, end_list)
394 local heuristic = self.h
395 local open = self.open
396 local end_count = #end_list
398 while #open > 0 do
399 local current = table.remove(open)
401 if current.s == 3 or current.s == 4 then
402 current.s = 2
403 return current
406 current.s = 2
408 local cd = current.g
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.
413 n.p = current.p
414 local f
416 if n.s == 3 then
417 n.s = 4
418 n.h = n.e*n.w
419 n.g = cd+d
420 else
421 n.s = 1
422 n.g = cd+d
424 local e = end_list[1]
425 n.h = (heuristic(n, e)+e.e)*e.w
427 for i = 2,end_count do
428 e = end_list[i]
429 n.h = math.min(n.h, (heuristic(n, e)+e.e)*e.w)
433 local f = n.g+n.h
435 local mn, mx = 1, #open+1
437 while mn ~= mx do
438 local m = math.floor((mn+mx)*0.5)
440 if open[m].f > f then
441 mn = m+1
442 else
443 mx = m
447 n.f = f
449 table.insert(open, mn, n)
451 elseif n.s == 1 or n.s == 4 then
452 local g = cd+d
453 local f = g+n.h
454 if f < n.f then
455 n.p = current.p
456 local of = n.f
458 local mn, mx = 1, #open
460 while mn ~= mx do
461 local m = math.floor((mn+mx)*0.5)
462 if open[m].f > of then
463 mn = m+1
464 else
465 mx = m
469 while open[mn] ~= n do
470 mn = mn + 1
473 mx = #open
475 table.remove(open, mn)
477 while mn ~= mx do
478 local m = math.floor((mn+mx)*0.5)
480 if open[m].f > f then
481 mn = m+1
482 else
483 mx = m
487 n.f = f
488 table.insert(open, mn, n)
495 local removed = {}
497 local function Graph_DoFullSearch(self, end_list)
498 local heuristic = self.h
499 local open = self.open
500 local end_count = #end_list
502 while #open > 0 do
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))
510 return
513 for i = 1,end_count do
514 if end_list[i] == current then
515 table.insert(removed, table.remove(end_list, i))
516 break
520 end_count = end_count - 1
523 current.s = 2
525 local cd = current.g
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.
530 n.p = current.p
531 local f
533 if n.s == 3 then
534 n.s = 4
535 n.h = n.e*n.w
536 n.g = cd+d
537 else
538 n.s = 1
539 n.g = cd+d
541 local e = end_list[1]
542 n.h = (heuristic(n, e)+e.e)*e.w
543 for i = 2,end_count do
544 e = end_list[i]
545 n.h = math.min(n.h, (heuristic(n, e)+e.e)*e.w)
549 local f = n.g+n.h
551 local mn, mx = 1, #open+1
553 while mn ~= mx do
554 local m = math.floor((mn+mx)*0.5)
556 if open[m].f > f then
557 mn = m+1
558 else
559 mx = m
563 n.f = f
565 table.insert(open, mn, n)
567 elseif n.s == 1 or n.s == 4 then
568 local g = cd+d
569 local f = g+n.h
570 if f < n.f then
571 n.p = current.p
572 local of = n.f
574 local mn, mx = 1, #open
576 while mn ~= mx do
577 local m = math.floor((mn+mx)*0.5)
578 if open[m].f > of then
579 mn = m+1
580 else
581 mx = m
585 while open[mn] ~= n do
586 mn = mn + 1
589 mx = #open
591 table.remove(open, mn)
593 while mn ~= mx do
594 local m = math.floor((mn+mx)*0.5)
596 if open[m].f > f then
597 mn = m+1
598 else
599 mx = m
603 n.f = f
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)
630 if node.s ~= 1 then
631 node.s = 1
632 for n in pairs(node.n) do
633 propLinks(n)
638 function Graph_SanityCheck(self)
639 for i = 1,#self.nodes do
640 for _, n in ipairs(self.nodes) do
641 n.s = 0
644 propLinks(self.nodes[i])
646 for _, n in ipairs(self.nodes) do
647 if n.s ~= 1 then
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
677 return graph
680 function QuestHelper:ReleaseGraph(graph)
681 graph:Reset()
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()