update changes
[QuestHelper.git] / graph.lua
blob72b4b8ab6ccf8008e07e2945e0c897293a644a23
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("graph_createnode")
119 node.Link = Graph_Node_Link
120 node.n = QuestHelper:CreateTable("graph_createnode.n")
121 node.r = QuestHelper:CreateTable("graph_createnode.r")
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 QuestHelper: Assert(open[m].g and g, string.format("nil-with-number issue, %s %s and %d inside %d %d", tostring(open[m].g), tostring(g), m, mn, mx))
186 if open[m].g > g then
187 mn = m+1
188 else
189 mx = m
193 table.insert(open, mn, n)
195 n.g = g
198 local function Graph_DoRouteSearch(self, end_list)
199 local open = self.open
200 local end_count = #end_list
202 while #open > 0 do
203 local current = table.remove(open)
205 if current.s == 3 or current.s == 4 then
206 current.s = 2
207 return current
210 current.s = 2
212 local cd = current.g
214 for n, d in pairs(current.n) do
215 if n.s == 0 or n.s == 3 then
216 -- Haven't visited this node yet.
217 local g = cd+d
218 n.p = current
219 n.g = g
221 n.s = n.s == 3 and 4 or 1
223 local mn, mx = 1, #open+1
225 while mn ~= mx do
226 local m = floor((mn+mx)*0.5)
228 if open[m].g > g then
229 mn = m+1
230 else
231 mx = m
235 table.insert(open, mn, n)
236 elseif n.s == 1 or n.s == 4 then
237 local g = cd+d
238 local og = n.g
239 if g < og then
240 n.p = current
242 local mn, mx = 1, #open
244 while mn ~= mx do
245 local m = floor((mn+mx)*0.5)
246 if open[m].g > og then
247 mn = m+1
248 else
249 mx = m
253 while open[mn] ~= n do
254 mn = mn + 1
257 mx = #open
259 table.remove(open, mn)
261 while mn ~= mx do
262 local m = floor((mn+mx)*0.5)
264 if open[m].g > g then
265 mn = m+1
266 else
267 mx = m
271 n.g = g
272 table.insert(open, mn, n)
279 local function Graph_PrepareSearch(self)
280 local open = self.open
281 for n in pairs(open) do open[n] = nil end
282 for n in pairs(open) do QuestHelper: Assert(nil, "not empty in preparesearch") end
283 for _, n in pairs(self.nodes) do
284 n.s = 0
288 local function Graph_AddStartNode(self, n, g, end_list)
289 local open = self.open
291 n.p = n
293 if n.s == 3 then
294 n.s = 4
295 elseif n.s == 0 then
296 n.s = 1
297 else
298 local og = n.g
299 if g < og then
300 local mn, mx = 1, #open
302 while mn ~= mx do
303 local m = floor((mn+mx)*0.5)
304 if open[m].g > og then
305 mn = m+1
306 else
307 mx = m
311 while open[mn] ~= n do
312 mn = mn + 1
315 table.remove(open, mn)
316 else
317 return -- Don't want to insert the node a second time.
321 local mn, mx = 1, #open+1
323 while mn ~= mx do
324 local m = floor((mn+mx)*0.5)
326 if open[m].g > g then
327 mn = m+1
328 else
329 mx = m
333 table.insert(open, mn, n)
335 n.g = g
338 local function Graph_DoSearch(self, end_list)
339 local open = self.open
340 local end_count = #end_list
342 while #open > 0 do
343 local current = table.remove(open)
345 if current.s == 3 or current.s == 4 then
346 current.s = 2
347 return current
350 current.s = 2
352 local cd = current.g
354 for n, d in pairs(current.n) do
355 if n.s == 0 or n.s == 3 then
356 -- Haven't visited this node yet.
357 local g = cd+d
358 n.g = g
359 n.p = current.p
361 n.s = n.s == 3 and 4 or 1
363 local mn, mx = 1, #open+1
365 while mn ~= mx do
366 local m = floor((mn+mx)*0.5)
368 if open[m].g > g then
369 mn = m+1
370 else
371 mx = m
375 table.insert(open, mn, n)
376 elseif n.s == 1 or n.s == 4 then
377 local g = cd+d
378 local og = n.g
379 if g < og then
380 n.p = current.p
382 local mn, mx = 1, #open
384 while mn ~= mx do
385 local m = floor((mn+mx)*0.5)
386 if open[m].g > og then
387 mn = m+1
388 else
389 mx = m
393 while open[mn] ~= n do
394 mn = mn + 1
397 mx = #open
399 table.remove(open, mn)
401 while mn ~= mx do
402 local m = floor((mn+mx)*0.5)
404 if open[m].g > g then
405 mn = m+1
406 else
407 mx = m
411 n.g = g
412 table.insert(open, mn, n)
419 local removed = {}
421 local function Graph_DoFullSearch(self, end_list)
422 local open = self.open
423 local end_count = #end_list
425 while #open > 0 do
426 local current = table.remove(open)
428 if current.s == 3 or current.s == 4 then
429 if end_count == 1 then
430 for i = 1,#removed do
431 table.insert(end_list, table.remove(removed))
433 return
436 for i = 1,end_count do
437 if end_list[i] == current then
438 table.insert(removed, table.remove(end_list, i))
439 break
443 end_count = end_count - 1
446 current.s = 2
448 local cd = current.g
450 for n, d in pairs(current.n) do
451 if n.s == 0 or n.s == 3 then
452 -- Haven't visited this node yet.
453 local g = cd+d
454 n.g = g
455 n.p = current.p
457 n.s = n.s == 3 and 4 or 1
459 local mn, mx = 1, #open+1
461 while mn ~= mx do
462 local m = floor((mn+mx)*0.5)
464 if open[m].g > g then
465 mn = m+1
466 else
467 mx = m
471 table.insert(open, mn, n)
472 elseif n.s == 1 or n.s == 4 then
473 local g = cd+d
474 local og = n.g
475 if g < og then
476 n.p = current.p
478 local mn, mx = 1, #open
480 while mn ~= mx do
481 local m = floor((mn+mx)*0.5)
482 if open[m].g > og then
483 mn = m+1
484 else
485 mx = m
489 while open[mn] ~= n do
490 mn = mn + 1
493 mx = #open
495 table.remove(open, mn)
497 while mn ~= mx do
498 local m = floor((mn+mx)*0.5)
500 if open[m].g > g then
501 mn = m+1
502 else
503 mx = m
507 n.g = g
508 table.insert(open, mn, n)
514 for i, n in ipairs(end_list) do
515 QuestHelper:TextOut(i..") Failed to reach: "..n.name..", s="..n.s)
518 for i = 1,#removed do
519 table.insert(end_list, table.remove(removed))
522 for i, n in ipairs(end_list) do
523 QuestHelper:TextOut(i..") End node: "..n.name..", s="..n.s)
526 QuestHelper:Error("Boom!")
528 for i = 1,#removed do
529 table.insert(end_list, table.remove(removed))
533 local function propLinks(node)
534 if node.s ~= 1 then
535 node.s = 1
536 for n in pairs(node.n) do
537 propLinks(n)
542 function Graph_SanityCheck(self)
543 for i = 1,#self.nodes do
544 for _, n in ipairs(self.nodes) do
545 n.s = 0
548 propLinks(self.nodes[i])
550 for _, n in ipairs(self.nodes) do
551 if n.s ~= 1 then
552 QuestHelper:TextOut((n.name or "unknown").." isn't reachable from "..(self.nodes[i].name or "unknown"))
558 function QuestHelper:CreateGraph()
559 local graph = self:CreateTable("graph")
560 graph.nodes = self:CreateTable("graph.nodes")
561 graph.end_nodes = self:CreateTable("graph.end_nodes")
562 graph.open = self:CreateTable("graph.open")
564 graph.CreateNode = Graph_CreateNode
565 graph.DestroyNode = Graph_DestroyNode
566 graph.Reset = Graph_Reset
568 graph.PrepareSearch = Graph_PrepareSearch
570 graph.AddRouteStartNode = Graph_AddRouteStartNode
571 graph.DoRouteSearch = Graph_DoRouteSearch
572 graph.Search = Graph_Search
573 graph.SanityCheck = Graph_SanityCheck
575 -- These don't deal with finding paths and instead only care about finding distances.
576 graph.AddStartNode = Graph_AddStartNode
577 graph.DoSearch = Graph_DoSearch
578 graph.DoFullSearch = Graph_DoFullSearch
580 return graph
583 function QuestHelper:ReleaseGraph(graph)
584 graph:Reset()
585 self:ReleaseTable(graph.nodes)
586 self:ReleaseTable(graph.end_nodes)
587 self:ReleaseTable(graph.open)
588 self:ReleaseTable(graph)
591 QuestHelper.world_graph = QuestHelper:CreateGraph()