LookAtMySuitBot/js/node_modules/mineflayer-pathfinder/lib/astar.js

126 lines
3.6 KiB
JavaScript

const { performance } = require('perf_hooks')
const Heap = require('./heap.js')
class PathNode {
constructor () {
this.data = null
this.g = 0
this.h = 0
this.f = 0
this.parent = null
}
set (data, g, h, parent = null) {
this.data = data
this.g = g
this.h = h
this.f = g + h
this.parent = parent
return this
}
}
function reconstructPath (node) {
const path = []
while (node.parent) {
path.push(node.data)
node = node.parent
}
return path.reverse()
}
class AStar {
constructor (start, movements, goal, timeout, tickTimeout = 40, searchRadius = -1) {
this.startTime = performance.now()
this.movements = movements
this.goal = goal
this.timeout = timeout
this.tickTimeout = tickTimeout
this.closedDataSet = new Set()
this.openHeap = new Heap()
this.openDataMap = new Map()
const startNode = new PathNode().set(start, 0, goal.heuristic(start))
this.openHeap.push(startNode)
this.openDataMap.set(startNode.data.hash, startNode)
this.bestNode = startNode
this.maxCost = searchRadius < 0 ? -1 : startNode.h + searchRadius
this.visitedChunks = new Set()
}
makeResult (status, node) {
return {
status,
cost: node.g,
time: performance.now() - this.startTime,
visitedNodes: this.closedDataSet.size,
generatedNodes: this.closedDataSet.size + this.openHeap.size(),
path: reconstructPath(node),
context: this
}
}
compute () {
const computeStartTime = performance.now()
while (!this.openHeap.isEmpty()) {
if (performance.now() - computeStartTime > this.tickTimeout) { // compute time per tick
return this.makeResult('partial', this.bestNode)
}
if (performance.now() - this.startTime > this.timeout) { // total compute time
return this.makeResult('timeout', this.bestNode)
}
const node = this.openHeap.pop()
if (this.goal.isEnd(node.data)) {
return this.makeResult('success', node)
}
// not done yet
this.openDataMap.delete(node.data.hash)
this.closedDataSet.add(node.data.hash)
this.visitedChunks.add(`${node.data.x >> 4},${node.data.z >> 4}`)
const neighbors = this.movements.getNeighbors(node.data)
for (const neighborData of neighbors) {
if (this.closedDataSet.has(neighborData.hash)) {
continue // skip closed neighbors
}
const gFromThisNode = node.g + neighborData.cost
let neighborNode = this.openDataMap.get(neighborData.hash)
let update = false
const heuristic = this.goal.heuristic(neighborData)
if (this.maxCost > 0 && gFromThisNode + heuristic > this.maxCost) continue
if (neighborNode === undefined) {
// add neighbor to the open set
neighborNode = new PathNode()
// properties will be set later
this.openDataMap.set(neighborData.hash, neighborNode)
} else {
if (neighborNode.g < gFromThisNode) {
// skip this one because another route is faster
continue
}
update = true
}
// found a new or better route.
// update this neighbor with this node as its new parent
neighborNode.set(neighborData, gFromThisNode, heuristic, node)
if (neighborNode.h < this.bestNode.h) this.bestNode = neighborNode
if (update) {
this.openHeap.update(neighborNode)
} else {
this.openHeap.push(neighborNode)
}
}
}
// all the neighbors of every accessible node have been exhausted
return this.makeResult('noPath', this.bestNode)
}
}
module.exports = AStar