1 QuestHelper_File
["routing_core.lua"] = "Development Version"
2 QuestHelper_Loadtime
["routing_core.lua"] = GetTime()
4 local debug_output
= (QuestHelper_File
["routing_core.lua"] == "Development Version")
8 let's think about clustering
10 Easiest way to pass in clusters, as we've already observed, is to just have a "cluster object" to pass in as an addition. This isn't a node, and we don't want to require "clusters" when people just want to add a single node. It isn't an objective either - it's a group of objectives, because when we return a route, we return a series of objectives.
12 So, "add cluster" is intrinsically different.
14 The next question, though, is how we link things. I'm liking the idea of the same ol' "link cluster X to cluster Y" thing. I think it's a good idea to create a list of "start nodes", though.
16 We're going to restrict it so that dependencies can only be done with clusters, just for the sake of my sanity.
17 This will also probably make it easier to ignore single parts of clusters, since we can do so by just tweaking the cluster definitions. I think this works.
19 I think this works tomorrow.
25 hey hey ignoring is complicated so let's discuss that!
27 Problem: One item can be ignored by multiple modules. Further problem: One item can be "de-ignored" explicitly by the player. Further further problem: either clusters or items can be ignored.
29 Current solution: We provide "ignore" and "unignore" functions that take a cluster and an identifier. Ignoring only stacks if the identifier is unique. If X depends on Y, and Y is ignored, then X is implicitly ignored.
31 Later, we'll provide something similar on items (or just dump items entirely? it's not like they're used for anything)
35 local QH_Timeslice_Yield
= QH_Timeslice_Yield
-- performance hack :(
37 local pairs
, ipairs
= pairs
, ipairs
38 local random = math
.random
40 local OptimizationHackery
= false
42 if OptimizationHackery
then debug_output
= false end -- :ughh:
45 -- Ant colony optimization. Moving from X to Y has the quality (Distance[x,y]^alpha)*(Weight[x,y]^beta). Sum all available qualities, then choose weighted randomly.
46 -- Weight adjustment: Weight[x,y] = Weight[x,y]*weightadj + sum(alltravels)(1/distance_of_travel) (note: this is somewhat out of date)
49 local PheremonePreservation
= 0.98 -- must be within 0 and 1 exclusive
50 local AntCount
= 20 -- number of ants to run before doing a pheremone pass
52 -- Weighting for the various factors
53 local WeightFactor
= 0.61
54 local DistanceFactor
= -2.5
55 local DistanceDeweight
= 1.4 -- Add this to all distances to avoid sqrt(-1) deals
57 -- Small amount to add to all weights to ensure it never hits, and to make sure things can still be chosen after a lot of iterations
58 local UniversalBonus
= 0.06
64 -- Node storage and data structures
65 local CurrentNodes
= 1
66 local ActiveNodes
= {1}
69 local NodeIgnored
= {[1] = {}}
70 local NodeIgnoredCount
= {[1] = 0}
73 local Cluster
= {} -- Goes from cluster ID to node IDs
74 local ClusterLookup
= {} -- Goes from node ID to cluster ID
75 local ClusterTableLookup
= {} -- Goes from the actual cluster table to the cluster ID
76 local ClusterDead
= {} -- List of dead clusters that can be reclaimed
78 local ClusterIgnored
= {} -- key-to-table-of-reasons-ignored
79 local ClusterIgnoredCount
= {}
80 local ClusterIgnoredNodeActive
= {}
82 local ClusterPriority
= {}
84 local PriorityCount
= {}
86 local DependencyLinks
= {} -- Every cluster that cluster X depends on
87 local DependencyLinksReverse
= {} -- Every cluster that is depended on by cluster X
88 local DependencyCounts
= {} -- How many different nodes cluster X depends on
90 local StartNode
= {ignore
= true, loc
= {x
= 37690, y
= 19671, p
= 25, c
= 0}} -- Ironforge mailbox :)
92 local NodeLookup
= {[StartNode
] = 1}
93 local NodeList
= {[1] = StartNode
}
94 local Distance
= {{0}}
97 local DistanceWaiting
= {} -- which node indices are waiting for distance data
100 -- End node storage and data structures
102 local early_exit
= false
105 ----------------------------------
106 Here's that wacky storage system.
107 ----------------------------------]]
109 local function unsigned2b(c
)
110 if c
> 65535 then -- ughh. again.
115 if not (c
< 65536) then
118 QuestHelper
: Assert(c
< 65536)
120 QuestHelper
: Assert(bit
.mod(c
, 256))
121 QuestHelper
: Assert(bit
.rshift(c
, 8))
122 local strix
= strchar(bit
.mod(c
, 256), bit
.rshift(c
, 8))
123 QuestHelper
: Assert(#strix
== 2)
129 local function Storage_Loop()
130 loopcount
= loopcount
+ 1
132 local function Storage_LoopFlush()
133 if loopcount
> 0 then
134 QH_Merger_Add(QH_Collect_Routing_Dump
, "L" .. unsigned2b(loopcount
) .. "L")
140 local function Storage_Distance_StoreFromIDToAll(id
)
141 if not QH_Collect_Routing_Dump
then return end
144 QH_Merger_Add(QH_Collect_Routing_Dump
, "-" .. unsigned2b(id
))
145 for _
, v
in ipairs(ActiveNodes
) do
146 QH_Merger_Add(QH_Collect_Routing_Dump
, unsigned2b(Distance
[id
][v
]))
148 QH_Merger_Add(QH_Collect_Routing_Dump
, "-")
152 local function Storage_Distance_StoreCrossID(id
)
153 if not QH_Collect_Routing_Dump
then return end
156 QH_Merger_Add(QH_Collect_Routing_Dump
, "X")
157 for _
, v
in ipairs(ActiveNodes
) do
158 QH_Merger_Add(QH_Collect_Routing_Dump
, unsigned2b(Distance
[id
][v
]))
159 if v
~= id
then QH_Merger_Add(QH_Collect_Routing_Dump
, unsigned2b(Distance
[v
][id
])) end
161 QH_Merger_Add(QH_Collect_Routing_Dump
, "X")
165 local function Storage_Distance_StoreAll()
166 if not QH_Collect_Routing_Dump
then return end
169 QH_Merger_Add(QH_Collect_Routing_Dump
, "#")
170 for _
, v
in ipairs(ActiveNodes
) do
171 for _
, w
in ipairs(ActiveNodes
) do
172 QH_Merger_Add(QH_Collect_Routing_Dump
, unsigned2b(Distance
[v
][w
]))
175 QH_Merger_Add(QH_Collect_Routing_Dump
, "#")
179 local function Storage_NodeAdded(id
)
180 if not QH_Collect_Routing_Dump
then return end
183 QH_Merger_Add(QH_Collect_Routing_Dump
, "A" .. unsigned2b(id
))
184 Storage_Distance_StoreCrossID(id
)
185 QH_Merger_Add(QH_Collect_Routing_Dump
, "A")
189 local function Storage_NodeRemoved(id
)
190 if not QH_Collect_Routing_Dump
then return end
193 QH_Merger_Add(QH_Collect_Routing_Dump
, "R" .. unsigned2b(id
) .. "R")
197 local function Storage_ClusterCreated(id
)
198 if not QH_Collect_Routing_Dump
then return end
201 QH_Merger_Add(QH_Collect_Routing_Dump
, "C" .. unsigned2b(id
) .. unsigned2b(#Cluster
[id
]))
202 for _
, v
in ipairs(Cluster
[id
]) do
203 QH_Merger_Add(QH_Collect_Routing_Dump
, unsigned2b(v
))
205 QH_Merger_Add(QH_Collect_Routing_Dump
, "C")
209 local function Storage_ClusterDestroyed(id
)
210 if not QH_Collect_Routing_Dump
then return end
213 QH_Merger_Add(QH_Collect_Routing_Dump
, "D" .. unsigned2b(id
) .. "D")
217 local function Storage_ClusterDependency(from
, to
)
218 if not QH_Collect_Routing_Dump
then return end
221 QH_Merger_Add(QH_Collect_Routing_Dump
, ">" .. unsigned2b(from
) .. unsigned2b(to
) .. ">")
225 ----------------------------------
226 and here's the other end of the wacky storage system
227 ----------------------------------]]
229 -- we may need to play with these
230 local QH_Route_Core_NodeAdd_Internal
231 local QH_Route_Core_NodeRemove_Internal
233 if OptimizationHackery
then
234 function Unstorage_SetDists(newdists
)
236 QuestHelper
: Assert(#newdists
== #ActiveNodes
* #ActiveNodes
)
237 for _
, v
in ipairs(ActiveNodes
) do
238 for _
, w
in ipairs(ActiveNodes
) do
239 Distance
[v
][w
] = newdists
[tc
]
243 QuestHelper
: Assert(tc
- 1 == #newdists
)
246 function Unstorage_SetDistsX(pivot
, newdists
)
248 QuestHelper
: Assert(#newdists
== #ActiveNodes
* 2 - 1)
249 for _
, v
in ipairs(ActiveNodes
) do
250 Distance
[pivot
][v
] = newdists
[tc
]
253 Distance
[v
][pivot
] = newdists
[tc
]
257 QuestHelper
: Assert(tc
- 1 == #newdists
)
260 function Unstorage_SetDistsLine(pivot
, newdists
)
262 QuestHelper
: Assert(#newdists
== #ActiveNodes
)
265 if last_best
and #last_best
> 1 then
266 last_best
.distance
= last_best
.distance
- Distance
[last_best
[1]]
[last_best
[2]]
270 for _
, v
in ipairs(ActiveNodes
) do
271 Distance
[pivot
][v
] = newdists
[tc
]
274 QuestHelper
: Assert(tc
- 1 == #newdists
)
277 if last_best
and #last_best
> 1 then
278 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[1]]
[last_best
[2]]
283 function Unstorage_Add(nod
)
284 QH_Route_Core_NodeAdd_Internal({}, nod
)
287 function Unstorage_Remove(nod
)
288 QH_Route_Core_NodeRemove_Internal({}, nod
)
291 function Unstorage_ClusterAdd(nod
, tab
)
292 QH_Route_Core_ClusterAdd({}, nod
)
293 for _
, v
in ipairs(tab
) do
294 QuestHelper
: Assert(NodeList
[v
])
295 ClusterLookup
[v
] = nod
296 table.insert(Cluster
[nod
], v
)
300 function Unstorage_ClusterRemove(nod
)
301 QH_Route_Core_ClusterRemove({}, nod
)
304 function Unstorage_Link(a
, b
)
305 QH_Route_Core_ClusterRequires(a
, b
, true)
308 function Unstorage_Nastyscan()
309 for _
, v
in ipairs(ActiveNodes
) do
310 for _
, w
in ipairs(ActiveNodes
) do
311 QuestHelper
: Assert(Distance
[v
][w
])
312 QuestHelper
: Assert(Weight
[v
][w
])
317 function Unstorage_Magic(tab
)
320 PheremonePreservation
= tab
.PheremonePreservation QuestHelper
: Assert(PheremonePreservation
) touched
.PheremonePreservation
= true
321 AntCount
= tab
.AntCount QuestHelper
: Assert(AntCount
) touched
.AntCount
= true
322 WeightFactor
= tab
.WeightFactor QuestHelper
: Assert(WeightFactor
) touched
.WeightFactor
= true
323 DistanceFactor
= tab
.DistanceFactor QuestHelper
: Assert(DistanceFactor
) touched
.DistanceFactor
= true
324 DistanceDeweight
= tab
.DistanceDeweight QuestHelper
: Assert(DistanceDeweight
) touched
.DistanceDeweight
= true
325 UniversalBonus
= tab
.UniversalBonus QuestHelper
: Assert(UniversalBonus
) touched
.UniversalBonus
= true
327 for k
, v
in pairs(tab
) do
328 QuestHelper
: Assert(touched
[k
])
334 ----------------------------------
335 here ends the butt of the wacky storage system. yeah, that's right. I said butt. Butt. Hee hee. Butt.
336 ----------------------------------]]
338 function QH_Route_Core_EarlyExit()
340 QH_Timeslice_Bump("new_routing", 50)
343 function QH_Route_Core_NodeCount()
347 function QH_Route_Core_TraverseNodes(func
)
348 for _
, v
in ipairs(ActiveNodes
) do
350 local blocked
= false
351 if ClusterLookup
[v
] and DependencyLinks
[ClusterLookup
[v]]
and #DependencyLinks
[ClusterLookup
[v]]
> 0 then blocked
= true end
352 --print("nlx", NodeList[v], blocked)
353 func(NodeList
[v
], blocked
)
358 function QH_Route_Core_TraverseClusters(func
)
359 for k
, v
in pairs(ClusterTableLookup
) do
364 function QH_Route_Core_IgnoredReasons_Cluster(clust
, func
)
365 for k
, _
in pairs(ClusterIgnored
[ClusterTableLookup
[clust]]
) do
366 if type(k
) == "table" then
372 function QH_Route_Core_IgnoredReasons_Node(node
, func
)
373 for k
, _
in pairs(NodeIgnored
[NodeLookup
[node]]
) do
374 if type(k
) == "table" then
380 function QH_Route_Core_Ignored_Cluster(clust
)
381 return ClusterIgnoredCount
[ClusterTableLookup
[clust]]
~= 0
384 -- fuck floating-point
385 local function almost(a
, b
)
386 if a
== b
then return true end
387 if type(a
) ~= "number" or type(b
) ~= "number" then return false end
388 if a
== 0 or b
== 0 then return false end
389 return math
.abs(a
/ b
- 1) < 0.0001
393 function QH_Route_Core_Init(PathNotifier
, DistanceBatch
)
394 Notifier
= PathNotifier
395 DistBatch
= DistanceBatch
396 QuestHelper
: Assert(Notifier
)
397 QuestHelper
: Assert(DistBatch
)
399 -- End initialization
401 local last_best
= nil
402 local last_best_tweaked
= false
404 local function ValidateNumber(x
)
405 QuestHelper
: Assert(x
== x
)
406 QuestHelper
: Assert(x
~= math
.huge
)
407 QuestHelper
: Assert(x
~= -math
.huge
)
410 local function GetWeight(x
, y
)
411 if x
== y
then return 0.00000000001 end -- sigh
412 --local idx = GetIndex(x, y)
413 --local revidx = GetIndex(y, x)
414 if not Weight
[x
][y
] or not Distance
[x
][y
] then
415 RTO(string.format("%d/%d %d", x
, y
, CurrentNodes
))
416 QuestHelper
: Assert(x
<= CurrentNodes
)
417 QuestHelper
: Assert(y
<= CurrentNodes
)
418 QuestHelper
: Assert(false)
420 local weight
= mpow(Weight
[x
][y
], WeightFactor
) * mpow(Distance
[x
][y
] + DistanceDeweight
, DistanceFactor
)
421 --print(Weight[idx], Weight[revidx], bonus, WeightFactor, Distance[idx], DistanceFactor)
422 --ValidateNumber(weight)
427 local function DespliceCN(cluster
, node
)
428 QuestHelper
: Assert(not cluster
or not node
)
429 QuestHelper
: Assert(cluster
or node
)
430 if not last_best
then return end
433 for i
= 2, #last_best
do
434 if last_best
[i
] and (last_best
[i
] == node
or ClusterLookup
[last_best
[i]]
== cluster
) then
436 last_best
.distance
= last_best
.distance
- Distance
[last_best
[i
- 1]]
[last_best
[i]]
437 if i
~= #last_best
then
438 last_best
.distance
= last_best
.distance
- Distance
[last_best
[i]]
[last_best
[i
+ 1]]
440 table.remove(last_best
, i
)
441 if i
~= #last_best
+ 1 then
442 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[i
- 1]]
[last_best
[i]]
450 last_best_tweaked
= true
452 --QuestHelper:TextOut("Despliced with " .. ct)
455 local function SpliceIn(index
, touched
)
456 QuestHelper
: Assert(index
)
457 QuestHelper
: Assert(last_best
)
458 if touched
[index
] then return end
462 -- First, try to splice everything it depends on
463 if DependencyLinks
[index
] then for _
, v
in ipairs(DependencyLinks
[index
]) do
464 if SpliceIn(v
, touched
) then
469 local dl_lookup
= QuestHelper
:CreateTable("splice dl lookup")
470 local dlr_lookup
= QuestHelper
:CreateTable("splice dlr lookup")
471 if DependencyLinks
[index
] then for _
, v
in ipairs(DependencyLinks
[index
]) do dl_lookup
[v
] = true end end
472 if DependencyLinksReverse
[index
] then for _
, v
in ipairs(DependencyLinksReverse
[index
]) do dlr_lookup
[v
] = true end end
474 local start_bound
= 2
477 -- Next, figure out where it can go
478 for i
= 2, #last_best
do
479 --print(index, last_best[i], ClusterLookup[last_best[i]], dl_lookup[ClusterLookup[last_best[i]]], dlr_lookup[ClusterLookup[last_best[i]]], ClusterPriority[ClusterLookup[last_best[i]]], ClusterPriority[index])
480 if dl_lookup
[ClusterLookup
[last_best
[i]]
] then start_bound
= i
+ 1 end
481 if dlr_lookup
[ClusterLookup
[last_best
[i]]
] and not end_bound
then end_bound
= i
end
482 if ClusterPriority
[ClusterLookup
[last_best
[i]]
] < ClusterPriority
[index
] then start_bound
= i
+ 1 end
483 if ClusterPriority
[ClusterLookup
[last_best
[i]]
] > ClusterPriority
[index
] and not end_bound
then end_bound
= i
end
485 if not end_bound
then end_bound
= #last_best
+ 1 end
486 --QuestHelper: TextOut(string.format("Placed cluster %d between %d and %d", index, start_bound, end_bound))
488 if end_bound
< start_bound
then
490 -- this should never happen, but I don't want it to show up all the time, sooooo
491 QuestHelper_ErrorCatcher_ExplicitError(false, string.format("Routing paradox: %d and %d, panicking and restarting", start_bound
, end_bound
))
495 -- Figure out the best place to put it
496 local best_spot
= nil
497 local best_node
= nil
498 local best_cost
= nil
499 for i
= start_bound
, end_bound
do
500 for _
, nindex
in ipairs(Cluster
[index
]) do
501 if NodeIgnoredCount
[nindex
] == 0 then
502 local tcost
= Distance
[last_best
[i
- 1]]
[nindex
] -- Cost of that-node-to-this-one
503 if i
<= #last_best
then
504 tcost
= tcost
+ Distance
[nindex
][last_best
[i]]
- Distance
[last_best
[i
- 1]]
[last_best
[i]]
506 if not best_cost
or tcost
< best_cost
then
507 best_spot
, best_node
, best_cost
= i
, nindex
, tcost
513 QuestHelper
: Assert(best_spot
)
514 table.insert(last_best
, best_spot
, best_node
)
515 last_best
.distance
= last_best
.distance
+ best_cost
517 touched
[index
] = true
518 last_best_tweaked
= true
520 QuestHelper
:ReleaseTable(dl_lookup
)
521 QuestHelper
:CreateTable(dlr_lookup
)
523 -- end realtime splice
525 -- Yeah, this function, right here? This is QH's brain. This is the only thing in all of Questhelper that actually generates routes. THIS IS IT.
526 local function RunAnt()
527 local route
= NewRoute()
531 local dependencies
= QuestHelper
:CreateTable("route_core_dependencies")
533 local needed
= QuestHelper
:CreateTable("route_core_needed")
534 local needed_count
= 0
535 local needed_ready_count
= 0
537 for k
, v
in pairs(DependencyCounts
) do
539 QuestHelper
: Assert(dependencies
[k
] >= 0)
544 local gwc
= QuestHelper
:CreateTable("route_core_gwc")
548 for k
, v
in ipairs(Priorities
) do
549 if Priorities
[k
+ 1] then
550 QuestHelper
: Assert(Priorities
[k
] < Priorities
[k
+ 1])
554 for _
, current_pri
in ipairs(Priorities
) do
556 -- Here is we add the new batch of nodes
557 for _
, v
in ipairs(ActiveNodes
) do
559 if v
~= 1 then -- if it's ignored, then we just plain don't do anything
560 local clustid
= ClusterLookup
[v
]
561 QuestHelper
: Assert(clustid
)
563 if ClusterPriority
[clustid
] < current_pri
then
564 QuestHelper
: Assert(dependencies
[clustid
] == -1 or NodeIgnoredCount
[v
] > 0 or ClusterIgnoredCount
[clustid
] >= 0)
565 elseif ClusterPriority
[clustid
] == current_pri
then
566 if NodeIgnoredCount
[v
] == 0 and ClusterIgnoredCount
[clustid
] == 0 then
569 QuestHelper
: Assert(dependencies
[clustid
])
570 if dependencies
[clustid
] == 0 then
572 needed_ready_count
= needed_ready_count
+ 1
575 needed_count
= needed_count
+ 1
578 QuestHelper
: Assert(dependencies
[clustid
] ~= -1, clustid
)
583 if not (needed_ready_count
> 0 or needed_count
== 0) then
584 QuestHelper
: Assert(needed_ready_count
> 0 or needed_count
== 0, string.format("%d %d", needed_ready_count
, needed_count
)) -- I should really rig this to output stuff of this sort more easily
587 while needed_count
> 0 do
589 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end
590 QuestHelper
: Assert(needed_ready_count
> 0)
592 local accumulated_weight
= 0
594 for k
, _
in pairs(needed
) do
595 local tw
= GetWeight(curloc
, k
)
597 accumulated_weight
= accumulated_weight
+ tw
600 tweight
= accumulated_weight
601 accumulated_weight
= accumulated_weight
* random()
604 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end
607 for k
, _
in pairs(needed
) do
608 accumulated_weight
= accumulated_weight
- gwc
[k
]
609 if accumulated_weight
< 0 then
616 RTO(string.format("no nod :( %f/%f", accumulated_weight
, tweight
))
617 for k
, _
in pairs(needed
) do
623 -- Now we've chosen stuff. Bookkeeping.
624 local clust
= ClusterLookup
[nod
]
625 QuestHelper
: Assert(clust
)
627 -- Obliterate other cluster items. Guaranteed to be at the same priority level.
628 for _
, v
in pairs(Cluster
[clust
]) do
629 if NodeIgnoredCount
[v
] == 0 then
631 needed_count
= needed_count
- 1
632 needed_ready_count
= needed_ready_count
- 1
637 if DependencyLinksReverse
[clust
] then for _
, v
in ipairs(DependencyLinksReverse
[clust
]) do
638 dependencies
[v
] = dependencies
[v
] - 1
639 QuestHelper
: Assert(dependencies
[v
] >= 0)
640 if dependencies
[v
] == 0 and ClusterIgnoredCount
[v
] == 0 and ClusterPriority
[v
] == current_pri
then
641 for _
, v
in pairs(Cluster
[v
]) do
642 if NodeIgnoredCount
[v
] == 0 then
644 needed_ready_count
= needed_ready_count
+ 1
650 QuestHelper
: Assert(dependencies
[clust
] == 0)
651 QuestHelper
: Assert(ClusterPriority
[clust
] == current_pri
)
652 dependencies
[clust
] = -1
654 --print(needed_count, needed_ready_count)
656 route
.distance
= route
.distance
+ Distance
[curloc
][nod
]
657 table.insert(route
, nod
)
661 QuestHelper
: Assert(needed_ready_count
== 0 and needed_count
== 0)
664 QuestHelper
:ReleaseTable(gwc
)
665 QuestHelper
:ReleaseTable(dependencies
)
666 QuestHelper
:ReleaseTable(needed
)
671 -- Lots of doublechecks to make sure our route is both sane and complete
672 local function CheckRoute(route
)
673 --print("starting check")
675 QuestHelper
: Assert(route
[1] == 1) -- starting at the beginning
678 for i
= 1, #route
- 1 do
679 td
= td
+ Distance
[route
[i]]
[route
[i
+ 1]]
681 --print(td, route.distance)
682 QuestHelper
: Assert(abs(td
- route
.distance
) < 5 or abs(td
/ route
.distance
- 1) < 0.0001)
684 local seen
= QuestHelper
:CreateTable("check seen")
688 QuestHelper
: Assert(NodeIgnoredCount
[route
[i]]
== 0)
690 local clust
= ClusterLookup
[route
[i]]
692 --print("seeing cluster ", clust, cpri, ClusterPriority[clust])
693 QuestHelper
: Assert(ClusterIgnoredCount
[clust
] == 0)
694 QuestHelper
: Assert(not seen
[clust
])
696 QuestHelper
: Assert(not cpri
or cpri
<= ClusterPriority
[clust
])
697 cpri
= ClusterPriority
[clust
]
699 if DependencyLinks
[clust
] then for _
, v
in ipairs(DependencyLinks
[clust
]) do QuestHelper
: Assert(seen
[v
]) end end
700 if DependencyLinksReverse
[clust
] then for _
, v
in ipairs(DependencyLinksReverse
[clust
]) do QuestHelper
: Assert(not seen
[v
]) end end
704 for k
, v
in pairs(ClusterIgnoredCount
) do
705 QuestHelper
: Assert(not seen
[k
] == (ClusterIgnoredCount
[k
] > 0))
708 QuestHelper
:ReleaseTable(seen
)
710 --print("ending check")
713 local function BetterRoute(route
)
716 for k
, v
in ipairs(route
) do
719 rt
.distance
= route
.distance
-- this is probably temporary
723 local QH_Route_Core_DistanceClear_Local
-- sigh
724 -- Core process function
725 function QH_Route_Core_Process()
727 --QuestHelper:TextOut("Startprocess")
731 --QuestHelper:TextOut("Pathing")
733 -- First we check to see if we need to add more distances, and if so, we do so
735 local route_tweak_progress
736 local better_route_progress
738 local refreshcount
= 0
739 for k
, v
in pairs(DistanceWaiting
) do
740 refreshcount
= refreshcount
+ 1
743 assert(not QuestHelper
.route_change_progress
)
745 if refreshcount
> 0 then
746 QuestHelper
.route_change_progress
= QuestHelper
.CreateLoadingCounter()
748 route_tweak_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.1)
749 better_route_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.2)
751 if debug_output
then QuestHelper
:TextOut(string.format("Refreshing %d", refreshcount
)) end
752 if refreshcount
>= #ActiveNodes
/ 2 then
753 -- Refresh everything!
754 QH_Route_Core_DistanceClear_Local(QuestHelper
.route_change_progress
:MakeSubcategory(0.7))
756 local resynch_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.7)
758 local tlnod
= QuestHelper
:CreateTable("routecore distance tlnod")
759 for _
, v
in ipairs(ActiveNodes
) do
760 table.insert(tlnod
, NodeList
[v
])
764 for idx
, _
in pairs(DistanceWaiting
) do ct
= ct
+ 1 end
767 for idx
, _
in pairs(DistanceWaiting
) do
768 -- Refresh a subset of things.
769 local forward
= DistBatch(NodeList
[idx
], tlnod
)
770 local backward
= DistBatch(NodeList
[idx
], tlnod
, true)
772 for k
, v
in ipairs(ActiveNodes
) do
773 Distance
[idx
][v
] = forward
[k
]
774 Distance
[v
][idx
] = backward
[k
]
777 QuestHelper
:ReleaseTable(forward
)
778 QuestHelper
:ReleaseTable(backward
)
781 resynch_progress
:SetPercentage(ctd
/ ct
)
783 QuestHelper
:ReleaseTable(tlnod
)
785 QuestHelper
:ReleaseTable(DistanceWaiting
)
786 DistanceWaiting
= QuestHelper
:CreateTable("routecore distance waiting")
790 --QuestHelper:TextOut("Inserting/removing")
792 -- Next we see if last_best needs tweaking
794 local touched_clusts
= QuestHelper
:CreateTable("routing touched")
795 for i
= 2, #last_best
do
796 local clust
= ClusterLookup
[last_best
[i]]
797 QuestHelper
: Assert(clust
)
798 QuestHelper
: Assert(not touched_clusts
[clust
])
799 touched_clusts
[clust
] = true
802 if not route_tweak_progress
then
803 assert(not QuestHelper
.route_change_progress
)
804 QuestHelper
.route_change_progress
= QuestHelper
.CreateLoadingCounter()
805 route_tweak_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.1)
806 better_route_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.9)
810 for k
, _
in pairs(Cluster
) do ct
= ct
+ 1 end
813 for k
, _
in pairs(Cluster
) do
814 local exists
= touched_clusts
[k
]
815 local ignored
= (ClusterIgnoredCount
[k
] ~= 0)
816 QuestHelper
: Assert(not (ignored
and exists
)) -- something went wrong
818 if not ignored
and not exists
then
820 if SpliceIn(k
, touched_clusts
) then
824 last_best_tweaked
= true
828 route_tweak_progress
:SetPercentage(ctd
/ ct
)
830 QuestHelper
:ReleaseTable(touched_clusts
)
833 --QuestHelper:TextOut("Posting")
835 if last_best_tweaked
and last_best
then
836 --QuestHelper:TextOut("Pushing tweaked")
837 BetterRoute(last_best
, better_route_progress
)
838 last_best_tweaked
= false
841 QH_Timeslice_Bump_Reset("new_routing")
843 QuestHelper
.route_change_progress
= nil
847 local best_is_local
= false
849 --QuestHelper:TextOut("Anting")
851 local trouts
= QuestHelper
:CreateTable("routing_core_trouts")
852 for x
= 1, AntCount
do
853 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end -- get money fuck routing
855 if ant
then table.insert(trouts
, ant
) end
856 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end
857 --if last_best then RTO(string.format("Path generated: %s vs %s", PathToString(trouts[#trouts]), PathToString(last_best))) end
858 if not last_best
or last_best
.distance
> trouts
[#trouts
].distance
then
859 if last_best
and not best_is_local
then QuestHelper
:ReleaseTable(last_best
) end
862 last_best
= trouts
[#trouts
]
863 BetterRoute(last_best
)
866 worst
= math
.max(worst
, trouts
[#trouts
].distance
)
871 --QuestHelper:TextOut("Cleanup")
874 if worst
== last_best
.distance
then
877 scale
= 1 / (worst
- last_best
.distance
)
882 for _
, x
in ipairs(ActiveNodes
) do
884 for _
, y
in ipairs(ActiveNodes
) do
885 --local idx = GetIndex(x, y)
886 wx
[y
] = wx
[y
] * PheremonePreservation
+ UniversalBonus
887 --ValidateNumber(Weight[idx])
893 for _
, x
in ipairs(trouts
) do
894 local amount
= 1 / x
.distance
896 --local idx = GetIndex(x[y], x[y + 1])
897 Weight
[x
[y]]
[x
[y
+ 1]]
= Weight
[x
[y]]
[x
[y
+ 1]]
+ amount
898 --ValidateNumber(Weight[idx])
906 for _
, x
in ipairs(ActiveNodes
) do
908 for _
, y
in ipairs(ActiveNodes
) do
909 --local idx = GetIndex(x, y)
910 weitotal
= weitotal
+ wx
[y
]
911 weicount
= weicount
+ 1
917 weight_ave
= weitotal
/ weicount
919 for k
, v
in pairs(trouts
) do
920 if v
~= last_best
then
921 QuestHelper
:ReleaseTable(v
)
924 QuestHelper
:ReleaseTable(trouts
)
926 QH_Timeslice_Yield() -- "heh"
928 --QuestHelper:TextOut("Done")
932 function QH_Core_Bump()
933 for _
, x
in ipairs(ActiveNodes
) do
935 for _
, y
in ipairs(ActiveNodes
) do
939 QH_Route_Core_EarlyExit()
943 local function RecursiveIgnoreCount(clustid
, accum
)
944 if accum
== 0 then return end
945 --print(clustid, accum)
947 if ClusterIgnoredCount
[clustid
] == 0 then QuestHelper
: Assert(accum
> 0) DespliceCN(clustid
) end
948 ClusterIgnoredCount
[clustid
] = ClusterIgnoredCount
[clustid
] + accum
949 if ClusterIgnoredCount
[clustid
] == 0 then QuestHelper
: Assert(accum
< 0) end -- Item being added, we'll handle this at the beginning of run
951 if DependencyLinksReverse
[clustid
] then
952 for _
, v
in pairs(DependencyLinksReverse
[clustid
]) do
953 RecursiveIgnoreCount(v
, accum
)
958 local function Internal_IgnoreCluster(clustid
, reason
)
959 QuestHelper
: Assert(clustid
)
961 if not ClusterIgnored
[clustid
][reason
] then
962 ClusterIgnored
[clustid
][reason
] = true
963 RecursiveIgnoreCount(clustid
, 1)
967 local function Internal_UnignoreCluster(clustid
, reason
)
968 QuestHelper
: Assert(clustid
)
969 if ClusterIgnored
[clustid
][reason
] then
970 ClusterIgnored
[clustid
][reason
] = nil
971 RecursiveIgnoreCount(clustid
, -1)
975 function QH_Route_Core_IgnoreCluster(clust
, reason
)
976 QuestHelper
: Assert(type(reason
) == "table")
977 local clustid
= ClusterTableLookup
[clust
]
979 -- This can just happen due to the lag introduced by the controller, so, whatever
980 --QuestHelper:TextOut("Attempted to ignore a cluster that no longer exists")
984 Internal_IgnoreCluster(clustid
, reason
)
987 function QH_Route_Core_UnignoreCluster(clust
, reason
)
988 QuestHelper
: Assert(type(reason
) == "table")
989 local clustid
= ClusterTableLookup
[clust
]
991 -- This can just happen due to the lag introduced by the controller, so, whatever
992 --QuestHelper:TextOut("Attempted to unignore a cluster that no longer exists")
995 Internal_UnignoreCluster(clustid
, reason
)
998 function QH_Route_Core_IgnoreNode(node
, reason
)
999 QuestHelper
: Assert(type(reason
) == "table")
1000 local nid
= NodeLookup
[node
]
1002 -- This can just happen due to the lag introduced by the controller, so, whatever
1003 --QuestHelper:TextOut("Attempted to ignore a node that no longer exists")
1007 QuestHelper
: Assert(nid
)
1009 if not NodeIgnored
[nid
][reason
] then
1010 NodeIgnored
[nid
][reason
] = true
1012 NodeIgnoredCount
[nid
] = NodeIgnoredCount
[nid
] + 1
1013 if NodeIgnoredCount
[nid
] == 1 then
1014 DespliceCN(nil, nid
)
1016 if ClusterLookup
[nid
] then
1017 local cloost
= ClusterLookup
[nid
]
1019 ClusterIgnoredNodeActive
[cloost
] = ClusterIgnoredNodeActive
[cloost
] - 1
1020 if ClusterIgnoredNodeActive
[cloost
] == 0 then
1021 Internal_IgnoreCluster(cloost
, "internal_node_ignored")
1023 if last_best
then -- if not last_best then this is just part of the global update and we basically don't care
1024 SpliceIn(cloost
, {})
1032 function QH_Route_Core_UnignoreNode(node
, reason
)
1033 QuestHelper
: Assert(type(reason
) == "table")
1034 local nid
= NodeLookup
[node
]
1036 -- This can just happen due to the lag introduced by the controller, so, whatever
1037 --QuestHelper:TextOut("Attempted to unignore a node that no longer exists")
1041 QuestHelper
: Assert(nid
)
1043 if NodeIgnored
[nid
][reason
] then
1044 NodeIgnored
[nid
][reason
] = nil
1046 NodeIgnoredCount
[nid
] = NodeIgnoredCount
[nid
] - 1
1047 if NodeIgnoredCount
[nid
] == 0 then
1050 if ClusterLookup
[nid
] then
1052 ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
= ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
+ 1
1053 if ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
== 1 then
1054 Internal_UnignoreCluster(ClusterLookup
[nid
], "internal_node_ignored")
1061 local QH_Route_Core_UnignoreCluster
= QH_Route_Core_UnignoreCluster
-- we're just saving this so it doesn't get splattered
1062 -- End ignore/unignore
1064 -- Node allocation and deallocation
1065 -- this is only separate so we can use it for the crazy optimization hackery
1066 local function Expand()
1067 for _
, v
in ipairs(Distance
) do
1070 for _
, v
in ipairs(Weight
) do
1073 table.insert(Distance
, {})
1074 table.insert(Weight
, {})
1076 for k
= 1, CurrentNodes
+ 1 do
1077 table.insert(Distance
[#Distance
], 0)
1078 table.insert(Weight
[#Weight
], 0)
1081 CurrentNodes
= CurrentNodes
+ 1
1084 -- This is pretty bad overall. Going from 0 nodes to N nodes is an O(n^3) operation. Eugh. Todo: allocate more than one at a time?
1085 local function AllocateExtraNode()
1086 if #DeadNodes
> 0 then
1087 local nod
= table.remove(DeadNodes
)
1088 table.insert(ActiveNodes
, nod
)
1089 table.sort(ActiveNodes
)
1093 -- We always allocate on the end, so we know this is safe.
1096 table.insert(DeadNodes
, CurrentNodes
)
1097 return AllocateExtraNode() -- ha ha
1100 -- Set the start location
1101 function QH_Route_Core_SetStart(stt
)
1102 -- We do some kind of ghastly things here.
1104 if last_best
and #last_best
> 1 then
1105 last_best
.distance
= last_best
.distance
- Distance
[last_best
[1]]
[last_best
[2]]
1108 NodeLookup
[StartNode
] = nil
1111 NodeLookup
[StartNode
] = 1
1113 local tlnod
= QuestHelper
:CreateTable("routecore setstart tlnod")
1115 for _
, v
in ipairs(ActiveNodes
) do
1117 table.insert(tlnod
, NodeList
[v
])
1121 local forward
= DistBatch(NodeList
[1], tlnod
)
1124 for _
, v
in ipairs(ActiveNodes
) do
1126 QuestHelper
: Assert(forward
[ct
])
1127 Distance
[1][v
] = forward
[ct
]
1130 Distance
[v
][1] = 65500 -- this should never be used anyway
1134 if last_best
and #last_best
> 1 then
1135 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[1]]
[last_best
[2]]
1138 QuestHelper
:ReleaseTable(forward
)
1139 QuestHelper
:ReleaseTable(tlnod
)
1141 Storage_Distance_StoreFromIDToAll(1)
1143 -- TODO: properly deallocate old startnode?
1146 QH_Route_Core_NodeAdd_Internal
= function (nod
, used_idx
)
1147 --QuestHelper:TextOut(tostring(nod))
1149 QuestHelper
: Assert(nod
)
1150 if NodeLookup
[nod
] then
1152 QuestHelper
: Assert(not NodeLookup
[nod
], QuestHelper
:StringizeTable(nod
))
1157 QuestHelper
: Assert(OptimizationHackery
)
1158 QuestHelper
: Assert(not NodeList
[used_idx
])
1160 table.insert(ActiveNodes
, used_idx
)
1161 table.sort(ActiveNodes
)
1162 if not Distance
[idx
] then Expand() QuestHelper
: Assert(Distance
[idx
]) end
1164 idx
= AllocateExtraNode()
1167 --RTO("|cffFF8080AEN: " .. tostring(idx))
1168 NodeLookup
[nod
] = idx
1171 NodeIgnored
[idx
] = {}
1172 NodeIgnoredCount
[idx
] = 0
1174 for _
, v
in ipairs(ActiveNodes
) do
1175 Weight
[v
][idx
] = weight_ave
1176 Weight
[idx
][v
] = weight_ave
1179 DistanceWaiting
[idx
] = true
1186 -- Remove a node with the given location
1187 QH_Route_Core_NodeRemove_Internal
= function (nod
, used_idx
)
1189 QuestHelper
: Assert(nod
)
1193 QuestHelper
: Assert(OptimizationHackery
)
1194 QuestHelper
: Assert(NodeList
[used_idx
])
1197 QuestHelper
: Assert(NodeLookup
[nod
])
1198 idx
= NodeLookup
[nod
]
1201 --RTO("|cffFF8080RFN: " .. tostring(NodeLookup[nod]))
1203 table.insert(DeadNodes
, idx
)
1204 local oas
= #ActiveNodes
1205 for k
, v
in pairs(ActiveNodes
) do if v
== idx
then table.remove(ActiveNodes
, k
) break end end -- this is pretty awful
1206 QuestHelper
: Assert(#ActiveNodes
< oas
)
1207 NodeLookup
[nod
] = nil
1208 -- We don't have to modify the table itself, some sections are just "dead".
1211 DistanceWaiting
[idx
] = nil -- just in case we haven't updated it in the intervening time
1213 -- If we're a standalone node, nothing depends on us. If we're part of a cluster, the cluster's getting smoked anyway.
1214 NodeIgnored
[idx
] = nil
1215 NodeIgnoredCount
[idx
] = nil
1217 DespliceCN(nil, idx
)
1221 -- End node allocation and deallocation
1223 function QH_Route_Core_ClusterAdd(clust
, clustid_used
)
1225 if clustid_used
then
1226 QuestHelper
: Assert(OptimizationHackery
)
1227 QuestHelper
: Assert(not Cluster
[clustid_used
])
1228 clustid
= clustid_used
1230 QuestHelper
: Assert(#clust
> 0)
1231 clustid
= table.remove(ClusterDead
)
1232 if not clustid
then clustid
= #Cluster
+ 1 end
1235 if debug_output
then QuestHelper
:TextOut(string.format("Adding cluster %d", clustid
)) end
1237 Cluster
[clustid
] = {}
1238 ClusterTableLookup
[clust
] = clustid
1240 ClusterIgnored
[clustid
] = {}
1241 ClusterIgnoredCount
[clustid
] = 0
1242 ClusterIgnoredNodeActive
[clustid
] = #clust
1244 ClusterPriority
[clustid
] = 0
1245 if not PriorityCount
[0] then table.insert(Priorities
, 0) table.sort(Priorities
) end
1246 PriorityCount
[0] = (PriorityCount
[0] or 0) + 1
1248 -- if we're doing hackery, clust will just be an empty table and we'll retrofit stuff later
1249 for _
, v
in ipairs(clust
) do
1250 local idx
= QH_Route_Core_NodeAdd_Internal(v
)
1251 Storage_NodeAdded(idx
)
1252 ClusterLookup
[idx
] = clustid
1253 table.insert(Cluster
[clustid
], idx
)
1256 DependencyCounts
[clustid
] = 0
1258 Storage_ClusterCreated(clustid
)
1261 function QH_Route_Core_ClusterRemove(clust
, clustid_used
)
1263 if clustid_used
then
1264 QuestHelper
: Assert(OptimizationHackery
)
1265 QuestHelper
: Assert(Cluster
[clustid_used
])
1266 clustid
= clustid_used
1268 for _
, v
in ipairs(Cluster
[clustid
]) do
1269 QH_Route_Core_NodeRemove_Internal({}, v
)
1270 ClusterLookup
[v
] = nil
1273 clustid
= ClusterTableLookup
[clust
]
1280 QuestHelper
: Assert(ct
< 100)
1282 for k
, v
in pairs(ClusterIgnored
[clustid
]) do
1284 Internal_UnignoreCluster(clustid
, k
)
1289 -- Imagine a->b->c. a is ignored, and b is deleted. This decouples a from c (todo: should it?) but we need to reduce c's ignore count appropriately so it's unignored.
1290 RecursiveIgnoreCount(clustid
, -ClusterIgnoredCount
[clustid
])
1291 QuestHelper
: Assert(ClusterIgnoredCount
[clustid
] == 0)
1294 if debug_output
then QuestHelper
:TextOut(string.format("Removing cluster %d", clustid
)) end
1296 for _
, v
in ipairs(clust
) do
1297 local idx
= QH_Route_Core_NodeRemove_Internal(v
)
1298 ClusterLookup
[idx
] = nil
1301 DependencyCounts
[clustid
] = nil
1303 if DependencyLinks
[clustid
] then
1304 for k
, v
in pairs(DependencyLinks
[clustid
]) do
1305 for m
, f
in pairs(DependencyLinksReverse
[v
]) do
1306 if f
== clustid
then
1307 if debug_output
then QuestHelper
:TextOut(string.format("Unlinking cluster %d needs %d", clustid
, v
)) end
1308 table.remove(DependencyLinksReverse
[v
], m
)
1314 DependencyLinks
[clustid
] = nil
1316 if DependencyLinksReverse
[clustid
] then
1317 for k
, v
in pairs(DependencyLinksReverse
[clustid
]) do
1318 for m
, f
in pairs(DependencyLinks
[v
]) do
1319 if f
== clustid
then
1320 if debug_output
then QuestHelper
:TextOut(string.format("Unlinking cluster %d needs %d", v
, clustid
)) end
1321 table.remove(DependencyLinks
[v
], m
)
1322 DependencyCounts
[v
] = DependencyCounts
[v
] - 1
1328 DependencyLinksReverse
[clustid
] = nil
1330 Cluster
[clustid
] = nil
1331 ClusterTableLookup
[clust
] = nil
1332 table.insert(ClusterDead
, clustid
)
1334 ClusterIgnored
[clustid
] = nil
1335 ClusterIgnoredCount
[clustid
] = nil
1336 ClusterIgnoredNodeActive
[clustid
] = nil
1338 local pri
= ClusterPriority
[clustid
]
1339 PriorityCount
[pri
] = PriorityCount
[pri
] - 1
1340 if PriorityCount
[pri
] == 0 then
1341 PriorityCount
[pri
] = nil
1343 for k
, v
in ipairs(Priorities
) do
1345 Priorities
[k
] = Priorities
[#Priorities
]
1346 table.remove(Priorities
)
1347 table.sort(Priorities
)
1352 ClusterPriority
[clustid
] = nil
1354 Storage_ClusterDestroyed(clustid
)
1357 local QH_Route_Core_SetClusterPriority_Internal
1359 -- Add a note that node 1 requires node 2.
1360 function QH_Route_Core_ClusterRequires(a
, b
, hackery
)
1364 QuestHelper
: Assert(OptimizationHackery
)
1365 QuestHelper
: Assert(Cluster
[a
])
1366 QuestHelper
: Assert(Cluster
[b
])
1369 aidx
= ClusterTableLookup
[a
]
1370 bidx
= ClusterTableLookup
[b
]
1372 QuestHelper
: Assert(aidx
)
1373 QuestHelper
: Assert(bidx
)
1374 QuestHelper
: Assert(aidx
~= bidx
)
1376 if debug_output
then QuestHelper
:TextOut(string.format("Linking cluster %d needs %d", aidx
, bidx
)) end
1378 DependencyCounts
[aidx
] = DependencyCounts
[aidx
] + 1
1380 if not DependencyLinks
[aidx
] then DependencyLinks
[aidx
] = {} end
1381 table.insert(DependencyLinks
[aidx
], bidx
)
1383 if not DependencyLinksReverse
[bidx
] then DependencyLinksReverse
[bidx
] = {} end
1384 table.insert(DependencyLinksReverse
[bidx
], aidx
)
1389 Storage_ClusterDependency(aidx
, bidx
)
1391 QH_Route_Core_SetClusterPriority_Internal(bidx
, ClusterPriority
[bidx
], true)
1394 function QH_Route_Core_GetClusterPriority(clust
)
1395 return ClusterPriority
[ClusterTableLookup
[clust]]
1398 function QH_Route_Core_SetClusterPriority_Internal(clustid
, new_pri
, force
)
1399 QuestHelper
: Assert(clustid
)
1400 if not force
and ClusterPriority
[clustid
] == new_pri
then return end
1401 --QuestHelper:TextOut(string.format("Setting %d to %d", clustid, new_pri))
1403 local pri
= ClusterPriority
[clustid
]
1404 QuestHelper
: Assert(pri
)
1405 PriorityCount
[pri
] = PriorityCount
[pri
] - 1
1406 if PriorityCount
[pri
] == 0 then
1407 PriorityCount
[pri
] = nil
1409 for k
, v
in ipairs(Priorities
) do
1411 Priorities
[k
] = Priorities
[#Priorities
]
1412 table.remove(Priorities
)
1413 table.sort(Priorities
)
1419 ClusterPriority
[clustid
] = new_pri
1420 if not PriorityCount
[new_pri
] then table.insert(Priorities
, new_pri
) table.sort(Priorities
) end
1421 PriorityCount
[new_pri
] = (PriorityCount
[new_pri
] or 0) + 1
1425 -- NOTE: These are recursive functions. It is vitally important that these not be called if nothing is changing, and it is vitally important that we change the local node first, otherwise we'll get infinite recursion and explosions. Or even EXPLOISIONS.
1427 -- Clusters that this one depends on. Must happen first (i.e. have a smaller or equal priority)
1428 if DependencyLinks
[clustid
] then for _
, v
in ipairs(DependencyLinks
[clustid
]) do
1429 if ClusterPriority
[v
] > new_pri
then QH_Route_Core_SetClusterPriority_Internal(v
, new_pri
) end
1432 -- Clusters that depend on this one. Must happen last (i.e. have a greater or equal priority)
1433 if DependencyLinksReverse
[clustid
] then for _
, v
in ipairs(DependencyLinksReverse
[clustid
]) do
1434 if ClusterPriority
[v
] < new_pri
then QH_Route_Core_SetClusterPriority_Internal(v
, new_pri
) end
1438 function QH_Route_Core_SetClusterPriority(clust
, new_pri
)
1439 QuestHelper
: Assert(clust
)
1440 local clustid
= ClusterTableLookup
[clust
]
1442 if clustid
then QH_Route_Core_SetClusterPriority_Internal(clustid
, new_pri
) end
1445 -- Wipe and re-cache all distances.
1446 function QH_Route_Core_DistanceClear(progress
)
1448 for _
, v
in ipairs(ActiveNodes
) do
1449 table.insert(tlnod
, NodeList
[v
])
1452 for ani
, idx
in ipairs(ActiveNodes
) do
1453 local forward
= DistBatch(NodeList
[idx
], tlnod
, false, true)
1455 for k
, v
in ipairs(ActiveNodes
) do
1456 Distance
[idx
][v
] = forward
[k
]
1459 if QuestHelper
.loading_preroll
and #ActiveNodes
> 1 then QuestHelper
.loading_preroll
:SetPercentage(ani
/ #ActiveNodes
) end
1461 if progress
then progress
:SetPercentage(ani
/ #ActiveNodes
) end
1465 last_best
.distance
= 0
1466 for i
= 1, #last_best
- 1 do
1467 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[i]]
[last_best
[i
+ 1]]
1471 Storage_Distance_StoreAll()
1473 QH_Route_Core_DistanceClear_Local
= QH_Route_Core_DistanceClear
1475 function findin(tab
, val
)
1477 for k
, v
in pairs(tab
) do
1478 if v
== val
then ct
= ct
+ 1 end
1485 for x = 1, #ActiveNodes do
1487 for y = 1, #ActiveNodes do
1488 ts = ts .. string.format("%f ", Distance[GetIndex(ActiveNodes[x], ActiveNodes[y])])
1496 for x = 1, #ActiveNodes do
1497 RTO(tostring(ActiveNodes[x]))
1499 RTO("Lookup table done")
1504 for x
= 1, #ActiveNodes
do
1505 for y
= 2, #ActiveNodes
do
1506 if not (almost(Dist(NodeList
[ActiveNodes
[x]]
, NodeList
[ActiveNodes
[y]]
), Distance
[ActiveNodes
[x]]
[ActiveNodes
[y]]
)) then
1507 RTO(string.format("%d/%d (%d/%d) should be %f, is %f", x
, y
, ActiveNodes
[x
], ActiveNodes
[y
], Dist(NodeList
[ActiveNodes
[x]]
, NodeList
[ActiveNodes
[y]]
),Distance
[ActiveNodes
[x]]
[ActiveNodes
[y]]
))
1513 for k
, v
in pairs(DependencyLinks
) do
1514 QuestHelper
: Assert(#v
== DependencyCounts
[k
])
1517 for k
, v
in pairs(DependencyCounts
) do
1518 QuestHelper
: Assert(v
== (DependencyLinks
[k
] and #DependencyLinks
[k
] or 0))
1521 for k
, v
in pairs(DependencyLinks
) do
1522 for _
, v2
in pairs(v
) do
1523 QuestHelper
: Assert(findin(DependencyLinksReverse
[v2
], k
))
1524 QuestHelper
: Assert(ClusterPriority
[v2
] <= ClusterPriority
[k
])
1528 for k
, v
in pairs(DependencyLinksReverse
) do
1529 for _
, v2
in pairs(v
) do
1530 QuestHelper
: Assert(findin(DependencyLinks
[v2
], k
))
1531 QuestHelper
: Assert(ClusterPriority
[v2
] >= ClusterPriority
[k
])
1535 QuestHelper
: Assert(not fail
)
1539 function HackeryDump()
1541 for k
, v
in pairs(ActiveNodes
) do
1543 st
= st
.. string.format("{c = %d, x = %f, y = %f}, ", NodeList
[k
].loc
.c
, NodeList
[k
].loc
.x
, NodeList
[k
].loc
.y
)
1547 QuestHelper
: Assert(false, st
)