Updated translations.
[QuestHelper.git] / graph.lua
blobd6e11fd802579a4e8c538c3650ce88748fc4f329
1 QuestHelper_File["graph.lua"] = "Development Version"
3 local floor = math.floor
5 local function Graph_Search(self, first, last)
6 if first ~= last then
7 local open = self.open
8 while #open > 0 do table.remove(open) end
9 for _, n in ipairs(self.nodes) do n.s = 0 end
11 table.insert(open, first)
13 first.g = 0
14 first.s = 2
16 while #open > 0 do
17 local current = table.remove(open)
19 if current == last then
20 first.p = nil
21 return true
22 end
24 current.s = 2
25 local cd = current.g
27 for n, d in pairs(current.n) do
28 if n.s == 0 then
29 -- Haven't visited this node yet.
30 local g = cd+d
31 n.g = g
32 n.s = 1
33 n.p = current
35 local mn, mx = 1, #open+1
37 while mn ~= mx do
38 local m = floor((mn+mx)*0.5)
40 if open[m].g > g then
41 mn = m+1
42 else
43 mx = m
44 end
45 end
47 table.insert(open, mn, n)
48 elseif n.s == 1 then
49 local g = cd+d
50 if g < n.g then
51 n.g = g
52 n.p = current
53 local mn, mx = 1, #open
55 while mn ~= mx do
56 local m = floor((mn+mx)*0.5)
57 if open[m].g > g then
58 mn = m+1
59 else
60 mx = m
61 end
62 end
64 while open[mn] ~= n do
65 mn = mn + 1
66 end
67 mx = #open
68 table.remove(open, mn)
70 while mn ~= mx do
71 local m = floor((mn+mx)*0.5)
73 if open[m].g > g then
74 mn = m+1
75 else
76 mx = m
77 end
78 end
80 table.insert(open, mn, n)
81 end
82 end
83 end
84 end
85 end
86 last.p = nil
87 return first == last
88 end
90 local function sanity(list, node)
91 local last = nil
92 local contains = false
93 for i, n in ipairs(list) do
94 if not (not last or last >= n.g) then
95 for i, n in ipairs(list) do
96 QuestHelper:TextOut(i..") "..n.g)
97 end
98 QuestHelper:Error("Order "..i.."/"..#list.." ("..n.g..")")
99 end
100 assert(not last or last >= n.g)
101 last = n.g
102 if n == node then
103 contains = true
106 if node and not contains then QuestHelper:Error("Missing") end
109 local function Graph_Node_Link(self, next_node, distance)
110 if type(distance) ~= "number" or distance < 0 then QuestHelper:Error("Boom!") end
111 if self ~= next_node then
112 self.n[next_node] = distance
113 table.insert(next_node.r, self)
117 local function Graph_CreateNode(self)
118 local node = QuestHelper:CreateTable()
119 node.Link = Graph_Node_Link
120 node.n = QuestHelper:CreateTable()
121 node.r = QuestHelper:CreateTable()
122 table.insert(self.nodes, node)
123 return node
126 local function Graph_DestroyNode(self, node)
127 for i = 1,#self.nodes do
128 if self.nodes[i] == node then
129 table.remove(self.nodes, i)
130 QuestHelper:ReleaseTable(node.n)
131 QuestHelper:ReleaseTable(node.r)
132 QuestHelper:ReleaseTable(node)
133 break
138 local function Graph_Reset(self)
139 while #self.nodes > 0 do
140 local node = table.remove(self.nodes)
141 QuestHelper:ReleaseTable(node.n)
142 QuestHelper:ReleaseTable(node.r)
143 QuestHelper:ReleaseTable(node)
147 local function Graph_AddRouteStartNode(self, n, g, end_list)
148 local open = self.open
150 n.p = nil
152 if n.s == 3 then
153 n.s = 4
154 elseif n.s == 0 then
155 n.s = 1
156 else
157 local og = n.g
158 if g < og then
159 local mn, mx = 1, #open
161 while mn ~= mx do
162 local m = floor((mn+mx)*0.5)
163 if open[m].g > og then
164 mn = m+1
165 else
166 mx = m
170 while open[mn] ~= n do
171 mn = mn + 1
174 table.remove(open, mn)
175 else
176 return -- Don't want to insert the node a second time.
180 local mn, mx = 1, #open+1
182 while mn ~= mx do
183 local m = floor((mn+mx)*0.5)
185 if open[m].g > g then
186 mn = m+1
187 else
188 mx = m
192 table.insert(open, mn, n)
194 n.g = g
197 local function Graph_DoRouteSearch(self, end_list)
198 local open = self.open
199 local end_count = #end_list
201 while #open > 0 do
202 local current = table.remove(open)
204 if current.s == 3 or current.s == 4 then
205 current.s = 2
206 return current
209 current.s = 2
211 local cd = current.g
213 for n, d in pairs(current.n) do
214 if n.s == 0 or n.s == 3 then
215 -- Haven't visited this node yet.
216 local g = cd+d
217 n.p = current
218 n.g = g
220 n.s = n.s == 3 and 4 or 1
222 local mn, mx = 1, #open+1
224 while mn ~= mx do
225 local m = floor((mn+mx)*0.5)
227 if open[m].g > g then
228 mn = m+1
229 else
230 mx = m
234 table.insert(open, mn, n)
235 elseif n.s == 1 or n.s == 4 then
236 local g = cd+d
237 local og = n.g
238 if g < og then
239 n.p = current
241 local mn, mx = 1, #open
243 while mn ~= mx do
244 local m = floor((mn+mx)*0.5)
245 if open[m].g > og then
246 mn = m+1
247 else
248 mx = m
252 while open[mn] ~= n do
253 mn = mn + 1
256 mx = #open
258 table.remove(open, mn)
260 while mn ~= mx do
261 local m = floor((mn+mx)*0.5)
263 if open[m].g > g then
264 mn = m+1
265 else
266 mx = m
270 n.g = g
271 table.insert(open, mn, n)
278 local function Graph_PrepareSearch(self)
279 local open = self.open
280 for n in pairs(open) do open[n] = nil end
281 for _, n in pairs(self.nodes) do
282 n.s = 0
286 local function Graph_AddStartNode(self, n, g, end_list)
287 local open = self.open
289 n.p = n
291 if n.s == 3 then
292 n.s = 4
293 elseif n.s == 0 then
294 n.s = 1
295 else
296 local og = n.g
297 if g < og then
298 local mn, mx = 1, #open
300 while mn ~= mx do
301 local m = floor((mn+mx)*0.5)
302 if open[m].g > og then
303 mn = m+1
304 else
305 mx = m
309 while open[mn] ~= n do
310 mn = mn + 1
313 table.remove(open, mn)
314 else
315 return -- Don't want to insert the node a second time.
319 local mn, mx = 1, #open+1
321 while mn ~= mx do
322 local m = floor((mn+mx)*0.5)
324 if open[m].g > g then
325 mn = m+1
326 else
327 mx = m
331 table.insert(open, mn, n)
333 n.g = g
336 local function Graph_DoSearch(self, end_list)
337 local open = self.open
338 local end_count = #end_list
340 while #open > 0 do
341 local current = table.remove(open)
343 if current.s == 3 or current.s == 4 then
344 current.s = 2
345 return current
348 current.s = 2
350 local cd = current.g
352 for n, d in pairs(current.n) do
353 if n.s == 0 or n.s == 3 then
354 -- Haven't visited this node yet.
355 local g = cd+d
356 n.g = g
357 n.p = current.p
359 n.s = n.s == 3 and 4 or 1
361 local mn, mx = 1, #open+1
363 while mn ~= mx do
364 local m = floor((mn+mx)*0.5)
366 if open[m].g > g then
367 mn = m+1
368 else
369 mx = m
373 table.insert(open, mn, n)
374 elseif n.s == 1 or n.s == 4 then
375 local g = cd+d
376 local og = n.g
377 if g < og then
378 n.p = current.p
380 local mn, mx = 1, #open
382 while mn ~= mx do
383 local m = floor((mn+mx)*0.5)
384 if open[m].g > og then
385 mn = m+1
386 else
387 mx = m
391 while open[mn] ~= n do
392 mn = mn + 1
395 mx = #open
397 table.remove(open, mn)
399 while mn ~= mx do
400 local m = floor((mn+mx)*0.5)
402 if open[m].g > g then
403 mn = m+1
404 else
405 mx = m
409 n.g = g
410 table.insert(open, mn, n)
417 local removed = {}
419 local function Graph_DoFullSearch(self, end_list)
420 local open = self.open
421 local end_count = #end_list
423 while #open > 0 do
424 local current = table.remove(open)
426 if current.s == 3 or current.s == 4 then
427 if end_count == 1 then
428 for i = 1,#removed do
429 table.insert(end_list, table.remove(removed))
431 return
434 for i = 1,end_count do
435 if end_list[i] == current then
436 table.insert(removed, table.remove(end_list, i))
437 break
441 end_count = end_count - 1
444 current.s = 2
446 local cd = current.g
448 for n, d in pairs(current.n) do
449 if n.s == 0 or n.s == 3 then
450 -- Haven't visited this node yet.
451 local g = cd+d
452 n.g = g
453 n.p = current.p
455 n.s = n.s == 3 and 4 or 1
457 local mn, mx = 1, #open+1
459 while mn ~= mx do
460 local m = floor((mn+mx)*0.5)
462 if open[m].g > g then
463 mn = m+1
464 else
465 mx = m
469 table.insert(open, mn, n)
470 elseif n.s == 1 or n.s == 4 then
471 local g = cd+d
472 local og = n.g
473 if g < og then
474 n.p = current.p
476 local mn, mx = 1, #open
478 while mn ~= mx do
479 local m = floor((mn+mx)*0.5)
480 if open[m].g > og then
481 mn = m+1
482 else
483 mx = m
487 while open[mn] ~= n do
488 mn = mn + 1
491 mx = #open
493 table.remove(open, mn)
495 while mn ~= mx do
496 local m = floor((mn+mx)*0.5)
498 if open[m].g > g then
499 mn = m+1
500 else
501 mx = m
505 n.g = g
506 table.insert(open, mn, n)
512 for i, n in ipairs(end_list) do
513 QuestHelper:TextOut(i..") Failed to reach: "..n.name..", s="..n.s)
516 for i = 1,#removed do
517 table.insert(end_list, table.remove(removed))
520 for i, n in ipairs(end_list) do
521 QuestHelper:TextOut(i..") End node: "..n.name..", s="..n.s)
524 QuestHelper:Error("Boom!")
526 for i = 1,#removed do
527 table.insert(end_list, table.remove(removed))
531 local function propLinks(node)
532 if node.s ~= 1 then
533 node.s = 1
534 for n in pairs(node.n) do
535 propLinks(n)
540 function Graph_SanityCheck(self)
541 for i = 1,#self.nodes do
542 for _, n in ipairs(self.nodes) do
543 n.s = 0
546 propLinks(self.nodes[i])
548 for _, n in ipairs(self.nodes) do
549 if n.s ~= 1 then
550 QuestHelper:TextOut((n.name or "unknown").." isn't reachable from "..(self.nodes[i].name or "unknown"))
556 function QuestHelper:CreateGraph()
557 local graph = self:CreateTable()
558 graph.nodes = self:CreateTable()
559 graph.end_nodes = self:CreateTable()
560 graph.open = self:CreateTable()
562 graph.CreateNode = Graph_CreateNode
563 graph.DestroyNode = Graph_DestroyNode
564 graph.Reset = Graph_Reset
566 graph.PrepareSearch = Graph_PrepareSearch
568 graph.AddRouteStartNode = Graph_AddRouteStartNode
569 graph.DoRouteSearch = Graph_DoRouteSearch
570 graph.Search = Graph_Search
571 graph.SanityCheck = Graph_SanityCheck
573 -- These don't deal with finding paths and instead only care about finding distances.
574 graph.AddStartNode = Graph_AddStartNode
575 graph.DoSearch = Graph_DoSearch
576 graph.DoFullSearch = Graph_DoFullSearch
578 return graph
581 function QuestHelper:ReleaseGraph(graph)
582 graph:Reset()
583 self:ReleaseTable(graph.nodes)
584 self:ReleaseTable(graph.end_nodes)
585 self:ReleaseTable(graph.open)
586 self:ReleaseTable(graph)
589 QuestHelper.world_graph = QuestHelper:CreateGraph()