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()
342 function QH_Route_Core_NodeCount()
346 function QH_Route_Core_TraverseNodes(func
)
347 for _
, v
in ipairs(ActiveNodes
) do
349 local blocked
= false
350 if ClusterLookup
[v
] and DependencyLinks
[ClusterLookup
[v]]
and #DependencyLinks
[ClusterLookup
[v]]
> 0 then blocked
= true end
351 --print("nlx", NodeList[v], blocked)
352 func(NodeList
[v
], blocked
)
357 function QH_Route_Core_TraverseClusters(func
)
358 for k
, v
in pairs(ClusterTableLookup
) do
363 function QH_Route_Core_IgnoredReasons_Cluster(clust
, func
)
364 for k
, _
in pairs(ClusterIgnored
[ClusterTableLookup
[clust]]
) do
365 if type(k
) == "table" then
371 function QH_Route_Core_IgnoredReasons_Node(node
, func
)
372 for k
, _
in pairs(NodeIgnored
[NodeLookup
[node]]
) do
373 if type(k
) == "table" then
379 function QH_Route_Core_Ignored_Cluster(clust
)
380 return ClusterIgnoredCount
[ClusterTableLookup
[clust]]
~= 0
383 -- fuck floating-point
384 local function almost(a
, b
)
385 if a
== b
then return true end
386 if type(a
) ~= "number" or type(b
) ~= "number" then return false end
387 if a
== 0 or b
== 0 then return false end
388 return math
.abs(a
/ b
- 1) < 0.0001
392 function QH_Route_Core_Init(PathNotifier
, DistanceBatch
)
393 Notifier
= PathNotifier
394 DistBatch
= DistanceBatch
395 QuestHelper
: Assert(Notifier
)
396 QuestHelper
: Assert(DistBatch
)
398 -- End initialization
400 local last_best
= nil
401 local last_best_tweaked
= false
403 local function ValidateNumber(x
)
404 QuestHelper
: Assert(x
== x
)
405 QuestHelper
: Assert(x
~= math
.huge
)
406 QuestHelper
: Assert(x
~= -math
.huge
)
409 local function GetWeight(x
, y
)
410 if x
== y
then return 0.00000000001 end -- sigh
411 --local idx = GetIndex(x, y)
412 --local revidx = GetIndex(y, x)
413 if not Weight
[x
][y
] or not Distance
[x
][y
] then
414 RTO(string.format("%d/%d %d", x
, y
, CurrentNodes
))
415 QuestHelper
: Assert(x
<= CurrentNodes
)
416 QuestHelper
: Assert(y
<= CurrentNodes
)
417 QuestHelper
: Assert(false)
419 local weight
= mpow(Weight
[x
][y
], WeightFactor
) * mpow(Distance
[x
][y
] + DistanceDeweight
, DistanceFactor
)
420 --print(Weight[idx], Weight[revidx], bonus, WeightFactor, Distance[idx], DistanceFactor)
421 --ValidateNumber(weight)
426 local function DespliceCN(cluster
, node
)
427 QuestHelper
: Assert(not cluster
or not node
)
428 QuestHelper
: Assert(cluster
or node
)
429 if not last_best
then return end
432 for i
= 2, #last_best
do
433 if last_best
[i
] and (last_best
[i
] == node
or ClusterLookup
[last_best
[i]]
== cluster
) then
435 last_best
.distance
= last_best
.distance
- Distance
[last_best
[i
- 1]]
[last_best
[i]]
436 if i
~= #last_best
then
437 last_best
.distance
= last_best
.distance
- Distance
[last_best
[i]]
[last_best
[i
+ 1]]
439 table.remove(last_best
, i
)
440 if i
~= #last_best
+ 1 then
441 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[i
- 1]]
[last_best
[i]]
449 last_best_tweaked
= true
451 --QuestHelper:TextOut("Despliced with " .. ct)
454 local function SpliceIn(index
, touched
)
455 QuestHelper
: Assert(index
)
456 if touched
[index
] then return end
460 -- First, try to splice everything it depends on
461 if DependencyLinks
[index
] then for _
, v
in ipairs(DependencyLinks
[index
]) do
462 if SpliceIn(v
, touched
) then
467 local dl_lookup
= QuestHelper
:CreateTable("splice dl lookup")
468 local dlr_lookup
= QuestHelper
:CreateTable("splice dlr lookup")
469 if DependencyLinks
[index
] then for _
, v
in ipairs(DependencyLinks
[index
]) do dl_lookup
[v
] = true end end
470 if DependencyLinksReverse
[index
] then for _
, v
in ipairs(DependencyLinksReverse
[index
]) do dlr_lookup
[v
] = true end end
472 local start_bound
= 2
475 -- Next, figure out where it can go
476 for i
= 2, #last_best
do
477 --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])
478 if dl_lookup
[ClusterLookup
[last_best
[i]]
] then start_bound
= i
+ 1 end
479 if dlr_lookup
[ClusterLookup
[last_best
[i]]
] and not end_bound
then end_bound
= i
end
480 if ClusterPriority
[ClusterLookup
[last_best
[i]]
] < ClusterPriority
[index
] then start_bound
= i
+ 1 end
481 if ClusterPriority
[ClusterLookup
[last_best
[i]]
] > ClusterPriority
[index
] and not end_bound
then end_bound
= i
end
483 if not end_bound
then end_bound
= #last_best
+ 1 end
484 --QuestHelper: TextOut(string.format("Placed cluster %d between %d and %d", index, start_bound, end_bound))
486 if end_bound
< start_bound
then
488 -- this should never happen, but I don't want it to show up all the time, sooooo
489 QuestHelper_ErrorCatcher_ExplicitError(false, string.format("Routing paradox: %d and %d, panicking and restarting", start_bound
, end_bound
))
493 -- Figure out the best place to put it
494 local best_spot
= nil
495 local best_node
= nil
496 local best_cost
= nil
497 for i
= start_bound
, end_bound
do
498 for _
, nindex
in ipairs(Cluster
[index
]) do
499 if NodeIgnoredCount
[nindex
] == 0 then
500 local tcost
= Distance
[last_best
[i
- 1]]
[nindex
] -- Cost of that-node-to-this-one
501 if i
<= #last_best
then
502 tcost
= tcost
+ Distance
[nindex
][last_best
[i]]
- Distance
[last_best
[i
- 1]]
[last_best
[i]]
504 if not best_cost
or tcost
< best_cost
then
505 best_spot
, best_node
, best_cost
= i
, nindex
, tcost
511 QuestHelper
: Assert(best_spot
)
512 table.insert(last_best
, best_spot
, best_node
)
513 last_best
.distance
= last_best
.distance
+ best_cost
515 touched
[index
] = true
516 last_best_tweaked
= true
518 QuestHelper
:ReleaseTable(dl_lookup
)
519 QuestHelper
:CreateTable(dlr_lookup
)
521 -- end realtime splice
523 -- 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.
524 local function RunAnt()
525 local route
= NewRoute()
529 local dependencies
= QuestHelper
:CreateTable("route_core_dependencies")
531 local needed
= QuestHelper
:CreateTable("route_core_needed")
532 local needed_count
= 0
533 local needed_ready_count
= 0
535 for k
, v
in pairs(DependencyCounts
) do
537 QuestHelper
: Assert(dependencies
[k
] >= 0)
542 local gwc
= QuestHelper
:CreateTable("route_core_gwc")
546 for k
, v
in ipairs(Priorities
) do
547 if Priorities
[k
+ 1] then
548 QuestHelper
: Assert(Priorities
[k
] < Priorities
[k
+ 1])
552 for _
, current_pri
in ipairs(Priorities
) do
554 -- Here is we add the new batch of nodes
555 for _
, v
in ipairs(ActiveNodes
) do
557 if v
~= 1 then -- if it's ignored, then we just plain don't do anything
558 local clustid
= ClusterLookup
[v
]
559 QuestHelper
: Assert(clustid
)
561 if ClusterPriority
[clustid
] < current_pri
then
562 QuestHelper
: Assert(dependencies
[clustid
] == -1 or NodeIgnoredCount
[v
] > 0 or ClusterIgnoredCount
[clustid
] >= 0)
563 elseif ClusterPriority
[clustid
] == current_pri
then
564 if NodeIgnoredCount
[v
] == 0 and ClusterIgnoredCount
[clustid
] == 0 then
567 QuestHelper
: Assert(dependencies
[clustid
])
568 if dependencies
[clustid
] == 0 then
570 needed_ready_count
= needed_ready_count
+ 1
573 needed_count
= needed_count
+ 1
576 QuestHelper
: Assert(dependencies
[clustid
] ~= -1, clustid
)
581 if not (needed_ready_count
> 0 or needed_count
== 0) then
582 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
585 while needed_count
> 0 do
587 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end
588 QuestHelper
: Assert(needed_ready_count
> 0)
590 local accumulated_weight
= 0
592 for k
, _
in pairs(needed
) do
593 local tw
= GetWeight(curloc
, k
)
595 accumulated_weight
= accumulated_weight
+ tw
598 tweight
= accumulated_weight
599 accumulated_weight
= accumulated_weight
* random()
602 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end
605 for k
, _
in pairs(needed
) do
606 accumulated_weight
= accumulated_weight
- gwc
[k
]
607 if accumulated_weight
< 0 then
614 RTO(string.format("no nod :( %f/%f", accumulated_weight
, tweight
))
615 for k
, _
in pairs(needed
) do
621 -- Now we've chosen stuff. Bookkeeping.
622 local clust
= ClusterLookup
[nod
]
623 QuestHelper
: Assert(clust
)
625 -- Obliterate other cluster items. Guaranteed to be at the same priority level.
626 for _
, v
in pairs(Cluster
[clust
]) do
627 if NodeIgnoredCount
[v
] == 0 then
629 needed_count
= needed_count
- 1
630 needed_ready_count
= needed_ready_count
- 1
635 if DependencyLinksReverse
[clust
] then for _
, v
in ipairs(DependencyLinksReverse
[clust
]) do
636 dependencies
[v
] = dependencies
[v
] - 1
637 QuestHelper
: Assert(dependencies
[v
] >= 0)
638 if dependencies
[v
] == 0 and ClusterIgnoredCount
[v
] == 0 and ClusterPriority
[v
] == current_pri
then
639 for _
, v
in pairs(Cluster
[v
]) do
640 if NodeIgnoredCount
[v
] == 0 then
642 needed_ready_count
= needed_ready_count
+ 1
648 QuestHelper
: Assert(dependencies
[clust
] == 0)
649 QuestHelper
: Assert(ClusterPriority
[clust
] == current_pri
)
650 dependencies
[clust
] = -1
652 --print(needed_count, needed_ready_count)
654 route
.distance
= route
.distance
+ Distance
[curloc
][nod
]
655 table.insert(route
, nod
)
659 QuestHelper
: Assert(needed_ready_count
== 0 and needed_count
== 0)
662 QuestHelper
:ReleaseTable(gwc
)
663 QuestHelper
:ReleaseTable(dependencies
)
664 QuestHelper
:ReleaseTable(needed
)
669 -- Lots of doublechecks to make sure our route is both sane and complete
670 local function CheckRoute(route
)
671 --print("starting check")
673 QuestHelper
: Assert(route
[1] == 1) -- starting at the beginning
676 for i
= 1, #route
- 1 do
677 td
= td
+ Distance
[route
[i]]
[route
[i
+ 1]]
679 --print(td, route.distance)
680 QuestHelper
: Assert(abs(td
- route
.distance
) < 5 or abs(td
/ route
.distance
- 1) < 0.0001)
682 local seen
= QuestHelper
:CreateTable("check seen")
686 QuestHelper
: Assert(NodeIgnoredCount
[route
[i]]
== 0)
688 local clust
= ClusterLookup
[route
[i]]
690 --print("seeing cluster ", clust, cpri, ClusterPriority[clust])
691 QuestHelper
: Assert(ClusterIgnoredCount
[clust
] == 0)
692 QuestHelper
: Assert(not seen
[clust
])
694 QuestHelper
: Assert(not cpri
or cpri
<= ClusterPriority
[clust
])
695 cpri
= ClusterPriority
[clust
]
697 if DependencyLinks
[clust
] then for _
, v
in ipairs(DependencyLinks
[clust
]) do QuestHelper
: Assert(seen
[v
]) end end
698 if DependencyLinksReverse
[clust
] then for _
, v
in ipairs(DependencyLinksReverse
[clust
]) do QuestHelper
: Assert(not seen
[v
]) end end
702 for k
, v
in pairs(ClusterIgnoredCount
) do
703 QuestHelper
: Assert(not seen
[k
] == (ClusterIgnoredCount
[k
] > 0))
706 QuestHelper
:ReleaseTable(seen
)
708 --print("ending check")
711 local function BetterRoute(route
)
714 for k
, v
in ipairs(route
) do
717 rt
.distance
= route
.distance
-- this is probably temporary
721 local QH_Route_Core_DistanceClear_Local
-- sigh
722 -- Core process function
723 function QH_Route_Core_Process()
725 --QuestHelper:TextOut("Startprocess")
729 --QuestHelper:TextOut("Pathing")
731 -- First we check to see if we need to add more distances, and if so, we do so
733 local route_tweak_progress
734 local better_route_progress
736 local refreshcount
= 0
737 for k
, v
in pairs(DistanceWaiting
) do
738 refreshcount
= refreshcount
+ 1
741 assert(not QuestHelper
.route_change_progress
)
743 if refreshcount
> 0 then
744 QuestHelper
.route_change_progress
= QuestHelper
.CreateLoadingCounter()
746 route_tweak_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.1)
747 better_route_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.2)
749 if debug_output
then QuestHelper
:TextOut(string.format("Refreshing %d", refreshcount
)) end
750 if refreshcount
>= #ActiveNodes
/ 2 then
751 -- Refresh everything!
752 QH_Route_Core_DistanceClear_Local(QuestHelper
.route_change_progress
:MakeSubcategory(0.7))
754 local resynch_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.7)
756 local tlnod
= QuestHelper
:CreateTable("routecore distance tlnod")
757 for _
, v
in ipairs(ActiveNodes
) do
758 table.insert(tlnod
, NodeList
[v
])
762 for idx
, _
in pairs(DistanceWaiting
) do ct
= ct
+ 1 end
765 for idx
, _
in pairs(DistanceWaiting
) do
766 -- Refresh a subset of things.
767 local forward
= DistBatch(NodeList
[idx
], tlnod
)
768 local backward
= DistBatch(NodeList
[idx
], tlnod
, true)
770 for k
, v
in ipairs(ActiveNodes
) do
771 Distance
[idx
][v
] = forward
[k
]
772 Distance
[v
][idx
] = backward
[k
]
775 QuestHelper
:ReleaseTable(forward
)
776 QuestHelper
:ReleaseTable(backward
)
779 resynch_progress
:SetPercentage(ctd
/ ct
)
781 QuestHelper
:ReleaseTable(tlnod
)
783 QuestHelper
:ReleaseTable(DistanceWaiting
)
784 DistanceWaiting
= QuestHelper
:CreateTable("routecore distance waiting")
788 --QuestHelper:TextOut("Inserting/removing")
790 -- Next we see if last_best needs tweaking
792 local touched_clusts
= QuestHelper
:CreateTable("routing touched")
793 for i
= 2, #last_best
do
794 local clust
= ClusterLookup
[last_best
[i]]
795 QuestHelper
: Assert(clust
)
796 QuestHelper
: Assert(not touched_clusts
[clust
])
797 touched_clusts
[clust
] = true
800 if not route_tweak_progress
then
801 assert(not QuestHelper
.route_change_progress
)
802 QuestHelper
.route_change_progress
= QuestHelper
.CreateLoadingCounter()
803 route_tweak_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.1)
804 better_route_progress
= QuestHelper
.route_change_progress
:MakeSubcategory(0.9)
808 for k
, _
in pairs(Cluster
) do ct
= ct
+ 1 end
811 for k
, _
in pairs(Cluster
) do
812 local exists
= touched_clusts
[k
]
813 local ignored
= (ClusterIgnoredCount
[k
] ~= 0)
814 QuestHelper
: Assert(not (ignored
and exists
)) -- something went wrong
816 if not ignored
and not exists
then
818 if SpliceIn(k
, touched_clusts
) then
822 last_best_tweaked
= true
826 route_tweak_progress
:SetPercentage(ctd
/ ct
)
828 QuestHelper
:ReleaseTable(touched_clusts
)
831 --QuestHelper:TextOut("Posting")
833 if last_best_tweaked
and last_best
then
834 --QuestHelper:TextOut("Pushing tweaked")
835 BetterRoute(last_best
, better_route_progress
)
836 last_best_tweaked
= false
839 QuestHelper
.route_change_progress
= nil
843 local best_is_local
= false
845 --QuestHelper:TextOut("Anting")
847 local trouts
= QuestHelper
:CreateTable("routing_core_trouts")
848 for x
= 1, AntCount
do
849 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end -- get money fuck routing
851 if ant
then table.insert(trouts
, ant
) end
852 if early_exit
then if debug_output
then QuestHelper
:TextOut("early exit") end return end
853 --if last_best then RTO(string.format("Path generated: %s vs %s", PathToString(trouts[#trouts]), PathToString(last_best))) end
854 if not last_best
or last_best
.distance
> trouts
[#trouts
].distance
then
855 if last_best
and not best_is_local
then QuestHelper
:ReleaseTable(last_best
) end
858 last_best
= trouts
[#trouts
]
859 BetterRoute(last_best
)
862 worst
= math
.max(worst
, trouts
[#trouts
].distance
)
867 --QuestHelper:TextOut("Cleanup")
870 if worst
== last_best
.distance
then
873 scale
= 1 / (worst
- last_best
.distance
)
878 for _
, x
in ipairs(ActiveNodes
) do
880 for _
, y
in ipairs(ActiveNodes
) do
881 --local idx = GetIndex(x, y)
882 wx
[y
] = wx
[y
] * PheremonePreservation
+ UniversalBonus
883 --ValidateNumber(Weight[idx])
889 for _
, x
in ipairs(trouts
) do
890 local amount
= 1 / x
.distance
892 --local idx = GetIndex(x[y], x[y + 1])
893 Weight
[x
[y]]
[x
[y
+ 1]]
= Weight
[x
[y]]
[x
[y
+ 1]]
+ amount
894 --ValidateNumber(Weight[idx])
902 for _
, x
in ipairs(ActiveNodes
) do
904 for _
, y
in ipairs(ActiveNodes
) do
905 --local idx = GetIndex(x, y)
906 weitotal
= weitotal
+ wx
[y
]
907 weicount
= weicount
+ 1
913 weight_ave
= weitotal
/ weicount
915 for k
, v
in pairs(trouts
) do
916 if v
~= last_best
then
917 QuestHelper
:ReleaseTable(v
)
920 QuestHelper
:ReleaseTable(trouts
)
922 QH_Timeslice_Yield() -- "heh"
924 --QuestHelper:TextOut("Done")
929 local function RecursiveIgnoreCount(clustid
, accum
)
930 if accum
== 0 then return end
931 --print(clustid, accum)
933 if ClusterIgnoredCount
[clustid
] == 0 then QuestHelper
: Assert(accum
> 0) DespliceCN(clustid
) end
934 ClusterIgnoredCount
[clustid
] = ClusterIgnoredCount
[clustid
] + accum
935 if ClusterIgnoredCount
[clustid
] == 0 then QuestHelper
: Assert(accum
< 0) end -- Item being added, we'll handle this at the beginning of run
937 if DependencyLinksReverse
[clustid
] then
938 for _
, v
in pairs(DependencyLinksReverse
[clustid
]) do
939 RecursiveIgnoreCount(v
, accum
)
944 local function Internal_IgnoreCluster(clustid
, reason
)
945 QuestHelper
: Assert(clustid
)
947 if not ClusterIgnored
[clustid
][reason
] then
948 ClusterIgnored
[clustid
][reason
] = true
949 RecursiveIgnoreCount(clustid
, 1)
953 local function Internal_UnignoreCluster(clustid
, reason
)
954 QuestHelper
: Assert(clustid
)
955 if ClusterIgnored
[clustid
][reason
] then
956 ClusterIgnored
[clustid
][reason
] = nil
957 RecursiveIgnoreCount(clustid
, -1)
961 function QH_Route_Core_IgnoreCluster(clust
, reason
)
962 QuestHelper
: Assert(type(reason
) == "table")
963 local clustid
= ClusterTableLookup
[clust
]
965 -- This can just happen due to the lag introduced by the controller, so, whatever
966 --QuestHelper:TextOut("Attempted to ignore a cluster that no longer exists")
970 Internal_IgnoreCluster(clustid
, reason
)
973 function QH_Route_Core_UnignoreCluster(clust
, reason
)
974 QuestHelper
: Assert(type(reason
) == "table")
975 local clustid
= ClusterTableLookup
[clust
]
977 -- This can just happen due to the lag introduced by the controller, so, whatever
978 --QuestHelper:TextOut("Attempted to unignore a cluster that no longer exists")
981 Internal_UnignoreCluster(clustid
, reason
)
984 function QH_Route_Core_IgnoreNode(node
, reason
)
985 QuestHelper
: Assert(type(reason
) == "table")
986 local nid
= NodeLookup
[node
]
988 -- This can just happen due to the lag introduced by the controller, so, whatever
989 --QuestHelper:TextOut("Attempted to ignore a node that no longer exists")
993 QuestHelper
: Assert(nid
)
995 if not NodeIgnored
[nid
][reason
] then
996 NodeIgnored
[nid
][reason
] = true
998 NodeIgnoredCount
[nid
] = NodeIgnoredCount
[nid
] + 1
999 if NodeIgnoredCount
[nid
] == 1 then
1000 DespliceCN(nil, nid
)
1002 if ClusterLookup
[nid
] then
1003 ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
= ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
- 1
1004 if ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
== 0 then
1005 Internal_IgnoreCluster(ClusterLookup
[nid
], "internal_node_ignored")
1012 function QH_Route_Core_UnignoreNode(node
, reason
)
1013 QuestHelper
: Assert(type(reason
) == "table")
1014 local nid
= NodeLookup
[node
]
1016 -- This can just happen due to the lag introduced by the controller, so, whatever
1017 --QuestHelper:TextOut("Attempted to unignore a node that no longer exists")
1021 QuestHelper
: Assert(nid
)
1023 if NodeIgnored
[nid
][reason
] then
1024 NodeIgnored
[nid
][reason
] = nil
1026 NodeIgnoredCount
[nid
] = NodeIgnoredCount
[nid
] - 1
1027 if NodeIgnoredCount
[nid
] == 0 then
1030 if ClusterLookup
[nid
] then
1032 ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
= ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
+ 1
1033 if ClusterIgnoredNodeActive
[ClusterLookup
[nid]]
== 1 then
1034 Internal_UnignoreCluster(ClusterLookup
[nid
], "internal_node_ignored")
1041 local QH_Route_Core_UnignoreCluster
= QH_Route_Core_UnignoreCluster
-- we're just saving this so it doesn't get splattered
1042 -- End ignore/unignore
1044 -- Node allocation and deallocation
1045 -- this is only separate so we can use it for the crazy optimization hackery
1046 local function Expand()
1047 for _
, v
in ipairs(Distance
) do
1050 for _
, v
in ipairs(Weight
) do
1053 table.insert(Distance
, {})
1054 table.insert(Weight
, {})
1056 for k
= 1, CurrentNodes
+ 1 do
1057 table.insert(Distance
[#Distance
], 0)
1058 table.insert(Weight
[#Weight
], 0)
1061 CurrentNodes
= CurrentNodes
+ 1
1064 -- 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?
1065 local function AllocateExtraNode()
1066 if #DeadNodes
> 0 then
1067 local nod
= table.remove(DeadNodes
)
1068 table.insert(ActiveNodes
, nod
)
1069 table.sort(ActiveNodes
)
1073 -- We always allocate on the end, so we know this is safe.
1076 table.insert(DeadNodes
, CurrentNodes
)
1077 return AllocateExtraNode() -- ha ha
1080 -- Set the start location
1081 function QH_Route_Core_SetStart(stt
)
1082 -- We do some kind of ghastly things here.
1084 if last_best
and #last_best
> 1 then
1085 last_best
.distance
= last_best
.distance
- Distance
[last_best
[1]]
[last_best
[2]]
1088 NodeLookup
[StartNode
] = nil
1091 NodeLookup
[StartNode
] = 1
1093 local tlnod
= QuestHelper
:CreateTable("routecore setstart tlnod")
1095 for _
, v
in ipairs(ActiveNodes
) do
1097 table.insert(tlnod
, NodeList
[v
])
1101 local forward
= DistBatch(NodeList
[1], tlnod
)
1104 for _
, v
in ipairs(ActiveNodes
) do
1106 QuestHelper
: Assert(forward
[ct
])
1107 Distance
[1][v
] = forward
[ct
]
1110 Distance
[v
][1] = 65500 -- this should never be used anyway
1114 if last_best
and #last_best
> 1 then
1115 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[1]]
[last_best
[2]]
1118 QuestHelper
:ReleaseTable(forward
)
1119 QuestHelper
:ReleaseTable(tlnod
)
1121 Storage_Distance_StoreFromIDToAll(1)
1123 -- TODO: properly deallocate old startnode?
1126 QH_Route_Core_NodeAdd_Internal
= function (nod
, used_idx
)
1127 --QuestHelper:TextOut(tostring(nod))
1129 QuestHelper
: Assert(nod
)
1130 if NodeLookup
[nod
] then
1132 QuestHelper
: Assert(not NodeLookup
[nod
], QuestHelper
:StringizeTable(nod
))
1137 QuestHelper
: Assert(OptimizationHackery
)
1138 QuestHelper
: Assert(not NodeList
[used_idx
])
1140 table.insert(ActiveNodes
, used_idx
)
1141 table.sort(ActiveNodes
)
1142 if not Distance
[idx
] then Expand() QuestHelper
: Assert(Distance
[idx
]) end
1144 idx
= AllocateExtraNode()
1147 --RTO("|cffFF8080AEN: " .. tostring(idx))
1148 NodeLookup
[nod
] = idx
1151 NodeIgnored
[idx
] = {}
1152 NodeIgnoredCount
[idx
] = 0
1154 for _
, v
in ipairs(ActiveNodes
) do
1155 Weight
[v
][idx
] = weight_ave
1156 Weight
[idx
][v
] = weight_ave
1159 DistanceWaiting
[idx
] = true
1166 -- Remove a node with the given location
1167 QH_Route_Core_NodeRemove_Internal
= function (nod
, used_idx
)
1169 QuestHelper
: Assert(nod
)
1173 QuestHelper
: Assert(OptimizationHackery
)
1174 QuestHelper
: Assert(NodeList
[used_idx
])
1177 QuestHelper
: Assert(NodeLookup
[nod
])
1178 idx
= NodeLookup
[nod
]
1181 --RTO("|cffFF8080RFN: " .. tostring(NodeLookup[nod]))
1183 table.insert(DeadNodes
, idx
)
1184 local oas
= #ActiveNodes
1185 for k
, v
in pairs(ActiveNodes
) do if v
== idx
then table.remove(ActiveNodes
, k
) break end end -- this is pretty awful
1186 QuestHelper
: Assert(#ActiveNodes
< oas
)
1187 NodeLookup
[nod
] = nil
1188 -- We don't have to modify the table itself, some sections are just "dead".
1191 DistanceWaiting
[idx
] = nil -- just in case we haven't updated it in the intervening time
1193 -- If we're a standalone node, nothing depends on us. If we're part of a cluster, the cluster's getting smoked anyway.
1194 NodeIgnored
[idx
] = nil
1195 NodeIgnoredCount
[idx
] = nil
1197 DespliceCN(nil, idx
)
1201 -- End node allocation and deallocation
1203 function QH_Route_Core_ClusterAdd(clust
, clustid_used
)
1205 if clustid_used
then
1206 QuestHelper
: Assert(OptimizationHackery
)
1207 QuestHelper
: Assert(not Cluster
[clustid_used
])
1208 clustid
= clustid_used
1210 QuestHelper
: Assert(#clust
> 0)
1211 clustid
= table.remove(ClusterDead
)
1212 if not clustid
then clustid
= #Cluster
+ 1 end
1215 if debug_output
then QuestHelper
:TextOut(string.format("Adding cluster %d", clustid
)) end
1217 Cluster
[clustid
] = {}
1218 ClusterTableLookup
[clust
] = clustid
1220 ClusterIgnored
[clustid
] = {}
1221 ClusterIgnoredCount
[clustid
] = 0
1222 ClusterIgnoredNodeActive
[clustid
] = #clust
1224 ClusterPriority
[clustid
] = 0
1225 if not PriorityCount
[0] then table.insert(Priorities
, 0) table.sort(Priorities
) end
1226 PriorityCount
[0] = (PriorityCount
[0] or 0) + 1
1228 -- if we're doing hackery, clust will just be an empty table and we'll retrofit stuff later
1229 for _
, v
in ipairs(clust
) do
1230 local idx
= QH_Route_Core_NodeAdd_Internal(v
)
1231 Storage_NodeAdded(idx
)
1232 ClusterLookup
[idx
] = clustid
1233 table.insert(Cluster
[clustid
], idx
)
1236 DependencyCounts
[clustid
] = 0
1238 Storage_ClusterCreated(clustid
)
1241 function QH_Route_Core_ClusterRemove(clust
, clustid_used
)
1243 if clustid_used
then
1244 QuestHelper
: Assert(OptimizationHackery
)
1245 QuestHelper
: Assert(Cluster
[clustid_used
])
1246 clustid
= clustid_used
1248 for _
, v
in ipairs(Cluster
[clustid
]) do
1249 QH_Route_Core_NodeRemove_Internal({}, v
)
1250 ClusterLookup
[v
] = nil
1253 clustid
= ClusterTableLookup
[clust
]
1260 QuestHelper
: Assert(ct
< 100)
1262 for k
, v
in pairs(ClusterIgnored
[clustid
]) do
1264 Internal_UnignoreCluster(clustid
, k
)
1269 -- 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.
1270 RecursiveIgnoreCount(clustid
, -ClusterIgnoredCount
[clustid
])
1271 QuestHelper
: Assert(ClusterIgnoredCount
[clustid
] == 0)
1274 if debug_output
then QuestHelper
:TextOut(string.format("Removing cluster %d", clustid
)) end
1276 for _
, v
in ipairs(clust
) do
1277 local idx
= QH_Route_Core_NodeRemove_Internal(v
)
1278 ClusterLookup
[idx
] = nil
1281 DependencyCounts
[clustid
] = nil
1283 if DependencyLinks
[clustid
] then
1284 for k
, v
in pairs(DependencyLinks
[clustid
]) do
1285 for m
, f
in pairs(DependencyLinksReverse
[v
]) do
1286 if f
== clustid
then
1287 if debug_output
then QuestHelper
:TextOut(string.format("Unlinking cluster %d needs %d", clustid
, v
)) end
1288 table.remove(DependencyLinksReverse
[v
], m
)
1294 DependencyLinks
[clustid
] = nil
1296 if DependencyLinksReverse
[clustid
] then
1297 for k
, v
in pairs(DependencyLinksReverse
[clustid
]) do
1298 for m
, f
in pairs(DependencyLinks
[v
]) do
1299 if f
== clustid
then
1300 if debug_output
then QuestHelper
:TextOut(string.format("Unlinking cluster %d needs %d", v
, clustid
)) end
1301 table.remove(DependencyLinks
[v
], m
)
1302 DependencyCounts
[v
] = DependencyCounts
[v
] - 1
1308 DependencyLinksReverse
[clustid
] = nil
1310 Cluster
[clustid
] = nil
1311 ClusterTableLookup
[clust
] = nil
1312 table.insert(ClusterDead
, clustid
)
1314 ClusterIgnored
[clustid
] = nil
1315 ClusterIgnoredCount
[clustid
] = nil
1316 ClusterIgnoredNodeActive
[clustid
] = nil
1318 local pri
= ClusterPriority
[clustid
]
1319 PriorityCount
[pri
] = PriorityCount
[pri
] - 1
1320 if PriorityCount
[pri
] == 0 then
1321 PriorityCount
[pri
] = nil
1323 for k
, v
in ipairs(Priorities
) do
1325 Priorities
[k
] = Priorities
[#Priorities
]
1326 table.remove(Priorities
)
1327 table.sort(Priorities
)
1332 ClusterPriority
[clustid
] = nil
1334 Storage_ClusterDestroyed(clustid
)
1337 local QH_Route_Core_SetClusterPriority_Internal
1339 -- Add a note that node 1 requires node 2.
1340 function QH_Route_Core_ClusterRequires(a
, b
, hackery
)
1344 QuestHelper
: Assert(OptimizationHackery
)
1345 QuestHelper
: Assert(Cluster
[a
])
1346 QuestHelper
: Assert(Cluster
[b
])
1349 aidx
= ClusterTableLookup
[a
]
1350 bidx
= ClusterTableLookup
[b
]
1352 QuestHelper
: Assert(aidx
)
1353 QuestHelper
: Assert(bidx
)
1354 QuestHelper
: Assert(aidx
~= bidx
)
1356 if debug_output
then QuestHelper
:TextOut(string.format("Linking cluster %d needs %d", aidx
, bidx
)) end
1358 DependencyCounts
[aidx
] = DependencyCounts
[aidx
] + 1
1360 if not DependencyLinks
[aidx
] then DependencyLinks
[aidx
] = {} end
1361 table.insert(DependencyLinks
[aidx
], bidx
)
1363 if not DependencyLinksReverse
[bidx
] then DependencyLinksReverse
[bidx
] = {} end
1364 table.insert(DependencyLinksReverse
[bidx
], aidx
)
1369 Storage_ClusterDependency(aidx
, bidx
)
1371 QH_Route_Core_SetClusterPriority_Internal(bidx
, ClusterPriority
[bidx
], true)
1374 function QH_Route_Core_GetClusterPriority(clust
)
1375 return ClusterPriority
[ClusterTableLookup
[clust]]
1378 function QH_Route_Core_SetClusterPriority_Internal(clustid
, new_pri
, force
)
1379 QuestHelper
: Assert(clustid
)
1380 if not force
and ClusterPriority
[clustid
] == new_pri
then return end
1381 --QuestHelper:TextOut(string.format("Setting %d to %d", clustid, new_pri))
1383 local pri
= ClusterPriority
[clustid
]
1384 QuestHelper
: Assert(pri
)
1385 PriorityCount
[pri
] = PriorityCount
[pri
] - 1
1386 if PriorityCount
[pri
] == 0 then
1387 PriorityCount
[pri
] = nil
1389 for k
, v
in ipairs(Priorities
) do
1391 Priorities
[k
] = Priorities
[#Priorities
]
1392 table.remove(Priorities
)
1393 table.sort(Priorities
)
1399 ClusterPriority
[clustid
] = new_pri
1400 if not PriorityCount
[new_pri
] then table.insert(Priorities
, new_pri
) table.sort(Priorities
) end
1401 PriorityCount
[new_pri
] = (PriorityCount
[new_pri
] or 0) + 1
1405 -- 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.
1407 -- Clusters that this one depends on. Must happen first (i.e. have a smaller or equal priority)
1408 if DependencyLinks
[clustid
] then for _
, v
in ipairs(DependencyLinks
[clustid
]) do
1409 if ClusterPriority
[v
] > new_pri
then QH_Route_Core_SetClusterPriority_Internal(v
, new_pri
) end
1412 -- Clusters that depend on this one. Must happen last (i.e. have a greater or equal priority)
1413 if DependencyLinksReverse
[clustid
] then for _
, v
in ipairs(DependencyLinksReverse
[clustid
]) do
1414 if ClusterPriority
[v
] < new_pri
then QH_Route_Core_SetClusterPriority_Internal(v
, new_pri
) end
1418 function QH_Route_Core_SetClusterPriority(clust
, new_pri
)
1419 QuestHelper
: Assert(clust
)
1420 local clustid
= ClusterTableLookup
[clust
]
1422 if clustid
then QH_Route_Core_SetClusterPriority_Internal(clustid
, new_pri
) end
1425 -- Wipe and re-cache all distances.
1426 function QH_Route_Core_DistanceClear(progress
)
1428 for _
, v
in ipairs(ActiveNodes
) do
1429 table.insert(tlnod
, NodeList
[v
])
1432 for ani
, idx
in ipairs(ActiveNodes
) do
1433 local forward
= DistBatch(NodeList
[idx
], tlnod
, false, true)
1435 for k
, v
in ipairs(ActiveNodes
) do
1436 Distance
[idx
][v
] = forward
[k
]
1439 if QuestHelper
.loading_preroll
and #ActiveNodes
> 1 then QuestHelper
.loading_preroll
:SetPercentage(ani
/ #ActiveNodes
) end
1441 if progress
then progress
:SetPercentage(ani
/ #ActiveNodes
) end
1445 last_best
.distance
= 0
1446 for i
= 1, #last_best
- 1 do
1447 last_best
.distance
= last_best
.distance
+ Distance
[last_best
[i]]
[last_best
[i
+ 1]]
1451 Storage_Distance_StoreAll()
1453 QH_Route_Core_DistanceClear_Local
= QH_Route_Core_DistanceClear
1455 function findin(tab
, val
)
1457 for k
, v
in pairs(tab
) do
1458 if v
== val
then ct
= ct
+ 1 end
1465 for x = 1, #ActiveNodes do
1467 for y = 1, #ActiveNodes do
1468 ts = ts .. string.format("%f ", Distance[GetIndex(ActiveNodes[x], ActiveNodes[y])])
1476 for x = 1, #ActiveNodes do
1477 RTO(tostring(ActiveNodes[x]))
1479 RTO("Lookup table done")
1484 for x
= 1, #ActiveNodes
do
1485 for y
= 2, #ActiveNodes
do
1486 if not (almost(Dist(NodeList
[ActiveNodes
[x]]
, NodeList
[ActiveNodes
[y]]
), Distance
[ActiveNodes
[x]]
[ActiveNodes
[y]]
)) then
1487 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]]
))
1493 for k
, v
in pairs(DependencyLinks
) do
1494 QuestHelper
: Assert(#v
== DependencyCounts
[k
])
1497 for k
, v
in pairs(DependencyCounts
) do
1498 QuestHelper
: Assert(v
== (DependencyLinks
[k
] and #DependencyLinks
[k
] or 0))
1501 for k
, v
in pairs(DependencyLinks
) do
1502 for _
, v2
in pairs(v
) do
1503 QuestHelper
: Assert(findin(DependencyLinksReverse
[v2
], k
))
1504 QuestHelper
: Assert(ClusterPriority
[v2
] <= ClusterPriority
[k
])
1508 for k
, v
in pairs(DependencyLinksReverse
) do
1509 for _
, v2
in pairs(v
) do
1510 QuestHelper
: Assert(findin(DependencyLinks
[v2
], k
))
1511 QuestHelper
: Assert(ClusterPriority
[v2
] >= ClusterPriority
[k
])
1515 QuestHelper
: Assert(not fail
)
1519 function HackeryDump()
1521 for k
, v
in pairs(ActiveNodes
) do
1523 st
= st
.. string.format("{c = %d, x = %f, y = %f}, ", NodeList
[k
].loc
.c
, NodeList
[k
].loc
.x
, NodeList
[k
].loc
.y
)
1527 QuestHelper
: Assert(false, st
)