138 lines
2.7 KiB
JavaScript
138 lines
2.7 KiB
JavaScript
Cxn = class Cxn {
|
|
constructor(dest, weight = 1) {
|
|
this.dest = dest
|
|
this.weight = weight
|
|
}
|
|
}
|
|
|
|
SearchData = class SearchData {
|
|
constructor(id, dist = Infinity, last = undefined, custom = {}) {
|
|
this.id = id
|
|
this.dist = dist
|
|
this.last = last
|
|
this.custom = custom
|
|
}
|
|
|
|
get(id, dist = Infinity, last = undefined, custom = {}) {
|
|
if (this.id != id) {
|
|
this.id = id
|
|
this.dist = dist
|
|
this.last = last
|
|
this.custom = custom
|
|
}
|
|
|
|
return { dist: this.dist, last: this.last, custom: this.custom }
|
|
}
|
|
|
|
update(id, dist = Infinity, last = undefined, custom = {}) {
|
|
if (this.id != id || this.dist > dist) {
|
|
this.id = id
|
|
this.dist = dist
|
|
this.last = last
|
|
this.custom = custom
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
Node = class Node {
|
|
static GLOBAL_ID = 0
|
|
|
|
constructor(val) {
|
|
this.id = Node.GLOBAL_ID++
|
|
this.val = val
|
|
this.cxns = []
|
|
this.searchData = new SearchData(0)
|
|
}
|
|
|
|
addCxn(node, weight = 1) { this.cxns.push(new Cxn(node, weight)) }
|
|
mapCxnsMut(func) { this.cxns = this.cxns.map(func) }
|
|
filterCxnsMut(func) { this.cxns = this.cxns.filter(func) }
|
|
getWeightTo(node) { return this.cxns.find((cxn) => cxn.dest == node)?.weight }
|
|
|
|
unwrap() {
|
|
let path = [this]
|
|
|
|
while (path[0].searchData.last) {
|
|
path.unshift(path[0].searchData.last)
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
dijkstraTo(dests) {
|
|
if (!Array.isArray(dests)) {
|
|
dests = [dests]
|
|
}
|
|
|
|
let id = Symbol()
|
|
|
|
this.searchData.update(id, 0)
|
|
|
|
console.time("heap gen")
|
|
|
|
let heap = new BinHeap((p, c) => p.searchData.get(id, Infinity, undefined, true).dist < c.searchData.get(id, Infinity, undefined, true).dist)
|
|
let visited = {}
|
|
let toVisit = [this]
|
|
|
|
let i = 0
|
|
|
|
while (toVisit.length) {
|
|
let toVisitNew = []
|
|
|
|
toVisit.forEach((node) => {
|
|
heap.insert(node)
|
|
visited[node.id] = true
|
|
node.cxns.map((cxn) => cxn.dest).map((node) => {
|
|
if (!visited[node.id]) {
|
|
toVisitNew.push(node)
|
|
visited[node.id] = true
|
|
}
|
|
})
|
|
})
|
|
|
|
toVisit = toVisitNew
|
|
|
|
if (++i % 100 == 0) {
|
|
console.log(toVisit.length)
|
|
}
|
|
}
|
|
|
|
console.timeEnd("heap gen")
|
|
console.time("search")
|
|
|
|
i = 0
|
|
|
|
while (heap.data.length) {
|
|
let min = heap.extract()
|
|
let minDist = min.searchData.get(id, Infinity, undefined, true).dist
|
|
min.searchData.custom = false
|
|
|
|
if (dests.includes(min)) {
|
|
console.timeEnd("search")
|
|
min.searchData.get(id)
|
|
return min
|
|
}
|
|
|
|
min.cxns.filter((cxn) => cxn.dest.searchData.get(id).custom).forEach((cxn) => {
|
|
if (cxn.dest.searchData.update(id, minDist + cxn.weight, min, true)) {
|
|
let idx = heap.data.indexOf(cxn.dest)
|
|
if (idx > -1) {
|
|
heap.up(idx)
|
|
}
|
|
}
|
|
})
|
|
|
|
if (++i % 1000 == 0) {
|
|
console.log(heap.data.length)
|
|
}
|
|
}
|
|
|
|
console.timeEnd("search")
|
|
console.warn("Node.dijkstraTo: Could not find a path")
|
|
}
|
|
}
|
|
|