perf scale 2, tuning
[QuestHelper.git] / Generic / graph.lua
blobe923cb725999eb5baa8baea7b44e7945d002ad7a
1 -- Functions we use here.
2 local create = QuestHelper.create
3 local createSortedList = QuestHelper.createSortedList
4 local release = QuestHelper.release
5 local insert = table.insert
6 local erase = table.remove
8 -- The metatables for Graph and Node objects.
9 local Graph, Node = {}, {}
11 function Node:onCreate()
12 rawset(self, "_link", create())
13 end
15 function Node:onRelease()
16 release(rawget(self, "_link"))
17 end
19 -- Links this node to another.
20 function Node:link(node, dist)
21 rawset(rawget(self, "_link"), node, dist)
22 end
24 local function nodeCmpA(a, b)
25 -- In A*f is the traveled distance plus the estimated remaining distance.
26 return rawget(a, "_f") > rawget(b, "_f")
27 end
29 local function nodeCmpD(a, b)
30 -- In Dijkstra is the distance traveled so far.
31 return rawget(a, "g") > rawget(b, "g")
32 end
34 function Graph:onCreate(heuristic)
35 rawset(self, "heuristic", heuristic)
36 rawset(self, "open", createSortedList(heuristic and nodeCmpA or nodeCmpD))
37 rawset(self, "nodes", create())
38 end
40 function Graph:onRelease()
41 release(rawget(self, "open"))
42 local nodes = rawget(self, "nodes")
43 for node in pairs(nodes) do release(node) end
44 release(nodes)
45 end
47 function Graph:createNode()
48 local node = create(Node)
49 rawget(self, "nodes")[node] = true
50 return node
51 end
53 function Graph:releaseNode(node)
54 release(node)
55 rawget(self, "nodes")[node] = nil
56 end
58 -- Prepares the graph for searching.
59 function Graph:clear()
60 local open = rawget(self, "open")
61 while erase(open) do end
63 for n in pairs(rawget(self, "nodes")) do
64 -- Remove state from node.
65 -- Possible values for s:
66 -- nil/false - Hasn't yet been considered.
67 -- 1 - It's been placed in the open table.
68 -- other - We know how to reach this node.
69 rawset(n, "_s", nil)
70 end
71 end
73 -- The same node must not be added multiple times.
74 function Graph:start(node, dist)
75 assert(not rawget(node, "_s")) -- Should only be added once.
76 rawset(node, "g", dist or 0)
77 rawset(node, "_s", 1)
78 if rawget(self, "heuristic") then
79 -- Just append the node to the end of the list, it will be sorted when
80 -- we have a node with which to apply the heuristic.
81 insert(rawget(self, "open"), node)
82 else
83 -- Insert in the correct position now; with no heuristic to change things
84 -- the list won't need to be resorted later.
85 rawget(self, "open"):insert(node)
86 end
87 end
89 function Graph:dest(dest)
90 local open = rawget(self, "open")
92 if rawget(dest, "_s") == 2 then
93 -- We've already reached this node.
94 return dest
95 end
97 local heuristic = rawget(self, "heuristic")
99 if heuristic then
100 -- A* Pathing
102 for i = 1,#open do
103 local n = rawget(open, i)
104 local g, h = rawget(n, "g"), heuristic(n, dest)
105 rawset(n, "_h", h)
106 rawset(n, "_f", g+h)
109 open:sort()
111 while #open > 0 do
112 local node = erase(open)
113 rawset(node, "_s", 2)
115 local g = rawget(node, "g")
117 for n, d in pairs(rawget(node, "_link")) do
118 local s = rawget(n, "_s")
119 if s == 1 then
120 -- Is already in the open list, possibly we found a shorter route to it.
121 local ng = g+d
122 local h = rawget(n, "_h")
123 local f = ng+h
124 if f < rawget(n, "_f") then
125 -- Found a shorter path to this node.
126 local i = open:lower(n)
127 while rawget(open, i) ~= n do i = i + 1 end
128 erase(open, i)
129 rawset(n, "g", ng)
130 rawset(n, "_f", f)
131 rawset(n, "parent", node)
132 open:insert2(n, i, #open+1)
134 elseif not s then
135 -- Haven't considered this node yet.
136 local h = heuristic(n, dest)
137 local ng = g+d
138 rawset(n, "g", ng)
139 rawset(n, "_f", ng+h)
140 rawset(n, "_h", h)
141 rawset(n, "_s", 1)
142 rawset(n, "parent", node)
143 open:insert(n)
144 end -- else the node in question is ignored.
147 if node == dest then
148 -- We could have checked for this before the above loop, but then we'd have missed some links
149 -- if this was to be called multiple times.
150 return node
153 else
154 -- Dijkstra's algorithm
155 -- Same as above, but we don't need to resort the list (already sorted), and only consider the distance traveled.
157 while #open > 0 do
158 local node = erase(open)
159 rawset(node, "_s", 2)
161 local g = rawget(node, "g")
163 for n, d in pairs(rawget(node, "_link")) do
164 local s = rawget(n, "_s")
165 if s == 1 then
166 -- Is already in the open list, possibly we found a shorter route to it.
167 local ng = g+d
168 if ng < rawget(n, "g") then
169 -- Found a shorter path to this node.
170 local i = open:lower(n)
171 while rawget(open, i) ~= n do i = i + 1 end
172 erase(open, i)
173 rawset(n, "g", ng)
174 rawset(n, "parent", node)
175 open:insert2(n, i, #open+1)
177 elseif not s then
178 -- Haven't considered this node yet.
179 rawset(n, "g", g+d)
180 rawset(n, "_s", 1)
181 rawset(n, "parent", node)
182 open:insert(n)
183 end -- else the node in question is ignored.
186 if node == dest then
187 -- We could have checked for this before the above loop, but then we'd have missed some links
188 -- if this was to be called multiple times.
189 return node
195 Graph.__newindex = function()
196 assert(false, "Shouldn't assign values.")
199 Node.__index = function(node, key)
200 return rawget(node, key) or rawget(Node, key)
203 Graph.__index = function(_, key)
204 return rawget(Graph, key)
207 local function createGraph(heuristic)
208 return create(Graph, heuristic)
211 QuestHelper.createGraph = createGraph