feat: Implement PriorityQueue class for D* Lite pathfinding with min-heap functionality
This commit is contained in:
175
server/pathfinding/PriorityQueue.js
Normal file
175
server/pathfinding/PriorityQueue.js
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* PriorityQueue - Min-heap priority queue for D* Lite
|
||||
* Uses two-element key pairs [k1, k2] for ordering
|
||||
*/
|
||||
export class PriorityQueue {
|
||||
constructor() {
|
||||
this.heap = [];
|
||||
this.keyMap = new Map(); // key string -> index in heap
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.heap.length;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.heap.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two key pairs
|
||||
* Returns negative if a < b, positive if a > b, 0 if equal
|
||||
*/
|
||||
static compareKeys(a, b) {
|
||||
if (a[0] !== b[0]) return a[0] - b[0];
|
||||
return a[1] - b[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum key without removing
|
||||
*/
|
||||
topKey() {
|
||||
if (this.heap.length === 0) return [Infinity, Infinity];
|
||||
return this.heap[0].priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update a node with a priority
|
||||
*/
|
||||
insertOrUpdate(node, priority) {
|
||||
const key = node.key;
|
||||
|
||||
if (this.keyMap.has(key)) {
|
||||
// Update existing entry
|
||||
const index = this.keyMap.get(key);
|
||||
const oldPriority = this.heap[index].priority;
|
||||
this.heap[index].priority = priority;
|
||||
this.heap[index].node = node;
|
||||
|
||||
// Determine whether to bubble up or sift down
|
||||
if (PriorityQueue.compareKeys(priority, oldPriority) < 0) {
|
||||
this._bubbleUp(index);
|
||||
} else {
|
||||
this._siftDown(index);
|
||||
}
|
||||
} else {
|
||||
// Insert new entry
|
||||
const entry = { node, priority };
|
||||
this.heap.push(entry);
|
||||
const index = this.heap.length - 1;
|
||||
this.keyMap.set(key, index);
|
||||
this._bubbleUp(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return the minimum node
|
||||
*/
|
||||
pop() {
|
||||
if (this.heap.length === 0) return null;
|
||||
|
||||
const min = this.heap[0];
|
||||
this.keyMap.delete(min.node.key);
|
||||
|
||||
const last = this.heap.pop();
|
||||
if (this.heap.length > 0) {
|
||||
this.heap[0] = last;
|
||||
this.keyMap.set(last.node.key, 0);
|
||||
this._siftDown(0);
|
||||
}
|
||||
|
||||
return min.node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific node by key
|
||||
*/
|
||||
remove(node) {
|
||||
const key = node.key;
|
||||
if (!this.keyMap.has(key)) return;
|
||||
|
||||
const index = this.keyMap.get(key);
|
||||
this.keyMap.delete(key);
|
||||
|
||||
const last = this.heap.pop();
|
||||
if (index < this.heap.length) {
|
||||
this.heap[index] = last;
|
||||
this.keyMap.set(last.node.key, index);
|
||||
this._bubbleUp(index);
|
||||
this._siftDown(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is in the queue
|
||||
*/
|
||||
contains(node) {
|
||||
return this.keyMap.has(node.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the queue
|
||||
*/
|
||||
clear() {
|
||||
this.heap = [];
|
||||
this.keyMap.clear();
|
||||
}
|
||||
|
||||
// --- Internal heap operations ---
|
||||
|
||||
_parent(i) {
|
||||
return Math.floor((i - 1) / 2);
|
||||
}
|
||||
|
||||
_leftChild(i) {
|
||||
return 2 * i + 1;
|
||||
}
|
||||
|
||||
_rightChild(i) {
|
||||
return 2 * i + 2;
|
||||
}
|
||||
|
||||
_swap(i, j) {
|
||||
const temp = this.heap[i];
|
||||
this.heap[i] = this.heap[j];
|
||||
this.heap[j] = temp;
|
||||
|
||||
this.keyMap.set(this.heap[i].node.key, i);
|
||||
this.keyMap.set(this.heap[j].node.key, j);
|
||||
}
|
||||
|
||||
_bubbleUp(i) {
|
||||
while (i > 0) {
|
||||
const parent = this._parent(i);
|
||||
if (PriorityQueue.compareKeys(this.heap[i].priority, this.heap[parent].priority) < 0) {
|
||||
this._swap(i, parent);
|
||||
i = parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_siftDown(i) {
|
||||
const n = this.heap.length;
|
||||
while (true) {
|
||||
let smallest = i;
|
||||
const left = this._leftChild(i);
|
||||
const right = this._rightChild(i);
|
||||
|
||||
if (left < n && PriorityQueue.compareKeys(this.heap[left].priority, this.heap[smallest].priority) < 0) {
|
||||
smallest = left;
|
||||
}
|
||||
if (right < n && PriorityQueue.compareKeys(this.heap[right].priority, this.heap[smallest].priority) < 0) {
|
||||
smallest = right;
|
||||
}
|
||||
|
||||
if (smallest !== i) {
|
||||
this._swap(i, smallest);
|
||||
i = smallest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user