Added TomTom to menu button. If both TomTom and Cartographer are available, the names...
[QuestHelper.git] / graph.lua
blobc52ebf43171ed679ff15e3b7bfa53bfbeb495184
1 local floor = math.floor
3 local function Graph_Search(self, first, last)
4 if first ~= last then
5 local open = self.open
6 while #open > 0 do table.remove(open) end
7 for _, n in ipairs(self.nodes) do n.s = 0 end
9 table.insert(open, first)
11 first.g = 0
12 first.s = 2
14 while #open > 0 do
15 local current = table.remove(open)
17 if current == last then
18 first.p = nil
19 return true
20 end
22 current.s = 2
23 local cd = current.g
25 for n, d in pairs(current.n) do
26 if n.s == 0 then
27 -- Haven't visited this node yet.
28 local g = cd+d
29 n.g = g
30 n.s = 1
31 n.p = current
33 local mn, mx = 1, #open+1
35 while mn ~= mx do
36 local m = floor((mn+mx)*0.5)
38 if open[m].g > g then
39 mn = m+1
40 else
41 mx = m
42 end
43 end
45 table.insert(open, mn, n)
46 elseif n.s == 1 then
47 local g = cd+d
48 if g < n.g then
49 n.g = g
50 n.p = current
51 local mn, mx = 1, #open
53 while mn ~= mx do
54 local m = floor((mn+mx)*0.5)
55 if open[m].g > g then
56 mn = m+1
57 else
58 mx = m
59 end
60 end
62 while open[mn] ~= n do
63 mn = mn + 1
64 end
65 mx = #open
66 table.remove(open, mn)
68 while mn ~= mx do
69 local m = floor((mn+mx)*0.5)
71 if open[m].g > g then
72 mn = m+1
73 else
74 mx = m
75 end
76 end
78 table.insert(open, mn, n)
79 end
80 end
81 end
82 end
83 end
84 last.p = nil
85 return first == last
86 end
88 local function sanity(list, node)
89 local last = nil
90 local contains = false
91 for i, n in ipairs(list) do
92 if not (not last or last >= n.g) then
93 for i, n in ipairs(list) do
94 QuestHelper:TextOut(i..") "..n.g)
95 end
96 QuestHelper:Error("Order "..i.."/"..#list.." ("..n.g..")")
97 end
98 assert(not last or last >= n.g)
99 last = n.g
100 if n == node then
101 contains = true
104 if node and not contains then QuestHelper:Error("Missing") end
107 local function Graph_Node_Link(self, next_node, distance)
108 if type(distance) ~= "number" or distance < 0 then QuestHelper:Error("Boom!") end
109 if self ~= next_node then
110 self.n[next_node] = distance
111 table.insert(next_node.r, self)
115 local function Graph_CreateNode(self)
116 local node = QuestHelper:CreateTable()
117 node.Link = Graph_Node_Link
118 node.n = QuestHelper:CreateTable()
119 node.r = QuestHelper:CreateTable()
120 table.insert(self.nodes, node)
121 return node
124 local function Graph_DestroyNode(self, node)
125 for i = 1,#self.nodes do
126 if self.nodes[i] == node then
127 table.remove(self.nodes, i)
128 QuestHelper:ReleaseTable(node.n)
129 QuestHelper:ReleaseTable(node.r)
130 QuestHelper:ReleaseTable(node)
131 break
136 local function Graph_Reset(self)
137 while #self.nodes > 0 do
138 local node = table.remove(self.nodes)
139 QuestHelper:ReleaseTable(node.n)
140 QuestHelper:ReleaseTable(node.r)
141 QuestHelper:ReleaseTable(node)
145 local function Graph_AddRouteStartNode(self, n, g, end_list)
146 local open = self.open
148 n.p = nil
150 if n.s == 3 then
151 n.s = 4
152 elseif n.s == 0 then
153 n.s = 1
154 else
155 local og = n.g
156 if g < og then
157 local mn, mx = 1, #open
159 while mn ~= mx do
160 local m = floor((mn+mx)*0.5)
161 if open[m].g > og then
162 mn = m+1
163 else
164 mx = m
168 while open[mn] ~= n do
169 mn = mn + 1
172 table.remove(open, mn)
173 else
174 return -- Don't want to insert the node a second time.
178 local mn, mx = 1, #open+1
180 while mn ~= mx do
181 local m = floor((mn+mx)*0.5)
183 if open[m].g > g then
184 mn = m+1
185 else
186 mx = m
190 table.insert(open, mn, n)
192 n.g = g
195 local function Graph_DoRouteSearch(self, end_list)
196 local open = self.open
197 local end_count = #end_list
199 while #open > 0 do
200 local current = table.remove(open)
202 if current.s == 3 or current.s == 4 then
203 current.s = 2
204 return current
207 current.s = 2
209 local cd = current.g
211 for n, d in pairs(current.n) do
212 if n.s == 0 or n.s == 3 then
213 -- Haven't visited this node yet.
214 local g = cd+d
215 n.p = current
216 n.g = g
218 n.s = n.s == 3 and 4 or 1
220 local mn, mx = 1, #open+1
222 while mn ~= mx do
223 local m = floor((mn+mx)*0.5)
225 if open[m].g > g then
226 mn = m+1
227 else
228 mx = m
232 table.insert(open, mn, n)
233 elseif n.s == 1 or n.s == 4 then
234 local g = cd+d
235 local og = n.g
236 if g < og then
237 n.p = current
239 local mn, mx = 1, #open
241 while mn ~= mx do
242 local m = floor((mn+mx)*0.5)
243 if open[m].g > og then
244 mn = m+1
245 else
246 mx = m
250 while open[mn] ~= n do
251 mn = mn + 1
254 mx = #open
256 table.remove(open, mn)
258 while mn ~= mx do
259 local m = floor((mn+mx)*0.5)
261 if open[m].g > g then
262 mn = m+1
263 else
264 mx = m
268 n.g = g
269 table.insert(open, mn, n)
276 local function Graph_PrepareSearch(self)
277 local open = self.open
278 for n in pairs(open) do open[n] = nil end
279 for _, n in pairs(self.nodes) do
280 n.s = 0
284 local function Graph_AddStartNode(self, n, g, end_list)
285 local open = self.open
287 n.p = n
289 if n.s == 3 then
290 n.s = 4
291 elseif n.s == 0 then
292 n.s = 1
293 else
294 local og = n.g
295 if g < og then
296 local mn, mx = 1, #open
298 while mn ~= mx do
299 local m = floor((mn+mx)*0.5)
300 if open[m].g > og then
301 mn = m+1
302 else
303 mx = m
307 while open[mn] ~= n do
308 mn = mn + 1
311 table.remove(open, mn)
312 else
313 return -- Don't want to insert the node a second time.
317 local mn, mx = 1, #open+1
319 while mn ~= mx do
320 local m = floor((mn+mx)*0.5)
322 if open[m].g > g then
323 mn = m+1
324 else
325 mx = m
329 table.insert(open, mn, n)
331 n.g = g
334 local function Graph_DoSearch(self, end_list)
335 local open = self.open
336 local end_count = #end_list
338 while #open > 0 do
339 local current = table.remove(open)
341 if current.s == 3 or current.s == 4 then
342 current.s = 2
343 return current
346 current.s = 2
348 local cd = current.g
350 for n, d in pairs(current.n) do
351 if n.s == 0 or n.s == 3 then
352 -- Haven't visited this node yet.
353 local g = cd+d
354 n.g = g
355 n.p = current.p
357 n.s = n.s == 3 and 4 or 1
359 local mn, mx = 1, #open+1
361 while mn ~= mx do
362 local m = floor((mn+mx)*0.5)
364 if open[m].g > g then
365 mn = m+1
366 else
367 mx = m
371 table.insert(open, mn, n)
372 elseif n.s == 1 or n.s == 4 then
373 local g = cd+d
374 local og = n.g
375 if g < og then
376 n.p = current.p
378 local mn, mx = 1, #open
380 while mn ~= mx do
381 local m = floor((mn+mx)*0.5)
382 if open[m].g > og then
383 mn = m+1
384 else
385 mx = m
389 while open[mn] ~= n do
390 mn = mn + 1
393 mx = #open
395 table.remove(open, mn)
397 while mn ~= mx do
398 local m = floor((mn+mx)*0.5)
400 if open[m].g > g then
401 mn = m+1
402 else
403 mx = m
407 n.g = g
408 table.insert(open, mn, n)
415 local removed = {}
417 local function Graph_DoFullSearch(self, end_list)
418 local open = self.open
419 local end_count = #end_list
421 while #open > 0 do
422 local current = table.remove(open)
424 if current.s == 3 or current.s == 4 then
425 if end_count == 1 then
426 for i = 1,#removed do
427 table.insert(end_list, table.remove(removed))
429 return
432 for i = 1,end_count do
433 if end_list[i] == current then
434 table.insert(removed, table.remove(end_list, i))
435 break
439 end_count = end_count - 1
442 current.s = 2
444 local cd = current.g
446 for n, d in pairs(current.n) do
447 if n.s == 0 or n.s == 3 then
448 -- Haven't visited this node yet.
449 local g = cd+d
450 n.g = g
451 n.p = current.p
453 n.s = n.s == 3 and 4 or 1
455 local mn, mx = 1, #open+1
457 while mn ~= mx do
458 local m = floor((mn+mx)*0.5)
460 if open[m].g > g then
461 mn = m+1
462 else
463 mx = m
467 table.insert(open, mn, n)
468 elseif n.s == 1 or n.s == 4 then
469 local g = cd+d
470 local og = n.g
471 if g < og then
472 n.p = current.p
474 local mn, mx = 1, #open
476 while mn ~= mx do
477 local m = floor((mn+mx)*0.5)
478 if open[m].g > og then
479 mn = m+1
480 else
481 mx = m
485 while open[mn] ~= n do
486 mn = mn + 1
489 mx = #open
491 table.remove(open, mn)
493 while mn ~= mx do
494 local m = floor((mn+mx)*0.5)
496 if open[m].g > g then
497 mn = m+1
498 else
499 mx = m
503 n.g = g
504 table.insert(open, mn, n)
510 for i, n in ipairs(end_list) do
511 QuestHelper:TextOut(i..") Failed to reach: "..n.name..", s="..n.s)
514 for i = 1,#removed do
515 table.insert(end_list, table.remove(removed))
518 for i, n in ipairs(end_list) do
519 QuestHelper:TextOut(i..") End node: "..n.name..", s="..n.s)
522 QuestHelper:Error("Boom!")
524 for i = 1,#removed do
525 table.insert(end_list, table.remove(removed))
529 local function propLinks(node)
530 if node.s ~= 1 then
531 node.s = 1
532 for n in pairs(node.n) do
533 propLinks(n)
538 function Graph_SanityCheck(self)
539 for i = 1,#self.nodes do
540 for _, n in ipairs(self.nodes) do
541 n.s = 0
544 propLinks(self.nodes[i])
546 for _, n in ipairs(self.nodes) do
547 if n.s ~= 1 then
548 QuestHelper:TextOut((n.name or "unknown").." isn't reachable from "..(self.nodes[i].name or "unknown"))
554 function QuestHelper:CreateGraph()
555 local graph = self:CreateTable()
556 graph.nodes = self:CreateTable()
557 graph.end_nodes = self:CreateTable()
558 graph.open = self:CreateTable()
560 graph.CreateNode = Graph_CreateNode
561 graph.DestroyNode = Graph_DestroyNode
562 graph.Reset = Graph_Reset
564 graph.PrepareSearch = Graph_PrepareSearch
566 graph.AddRouteStartNode = Graph_AddRouteStartNode
567 graph.DoRouteSearch = Graph_DoRouteSearch
568 graph.Search = Graph_Search
569 graph.SanityCheck = Graph_SanityCheck
571 -- These don't deal with finding paths and instead only care about finding distances.
572 graph.AddStartNode = Graph_AddStartNode
573 graph.DoSearch = Graph_DoSearch
574 graph.DoFullSearch = Graph_DoFullSearch
576 return graph
579 function QuestHelper:ReleaseGraph(graph)
580 graph:Reset()
581 self:ReleaseTable(graph.nodes)
582 self:ReleaseTable(graph.end_nodes)
583 self:ReleaseTable(graph.open)
584 self:ReleaseTable(graph)
587 QuestHelper.world_graph = QuestHelper:CreateGraph()