UNPKG

@amarillion/helixgraph

Version:

A collection of graph algorithms for game development

81 lines (80 loc) 3.01 kB
import { toSet } from "./pathFinding.js"; export function bfsVisit(source, getAdjacent, callback) { for (const node of bfsGenerator(source, getAdjacent)) { callback(node); } } export function* bfsGenerator(source, getAdjacent) { const open = []; const visited = new Set(); open.push(source); visited.add(source); while (open.length) { const current = open.shift(); yield current; for (const [, destNode] of getAdjacent(current)) { if (!visited.has(destNode)) { open.push(destNode); visited.add(destNode); } } } } /** * Performs a breadth-first search on a graph. * Starting form the source node, expanding until all connected nodes are visited. * Nodes are treated as opaque data objects and are not modified. You can use any kind of * variable type to represent nodes: ints, strings, objects, ... * * @param source starting node * @param distinations Single node or Array of nodes. Search will continue until all destinations are reached. * @param getAdjacent adjacency function representing the graph. For this algorithm, edges do not need to be unique. * * @returns a map of examined nodes. Pass this to a backtracking function to extract a simple path. * * Input graph may be undirected or directed (getAdjacent should act correspondingly) * * Guaranteed to return shortest paths for unweighted networks. * Complexity: O(V + E) * Faster than dijkstra, because it accesses the open list with O(1) instead of O(N) (or dijkstra with priorityQ: O(log N)) * But unlike dijkstra, this can't handle weighted edges. * * For more discussion, see: https://stackoverflow.com/questions/25449781/what-is-difference-between-bfs-and-dijkstras-algorithms-when-looking-for-shorte */ export function breadthFirstSearch(source, dest, getAdjacent, { maxIterations = 0 } = {}) { const open = []; const dist = new Map(); const prev = new Map(); const remain = toSet(dest); open.push(source); dist.set(source, 0); // TODO: important to make sure distMap.get(source).cost === 0! prev.set(source, { to: source, cost: 0 }); let i = maxIterations; while (open.length) { i--; // 0 -> -1 means Infinite. if (i === 0) break; const current = open.shift(); const distance = dist.get(current) || 0; if (remain.has(current)) { remain.delete(current); if (remain.size === 0) break; } for (const [edge, destNode] of getAdjacent(current)) { const visited = prev.has(destNode); if (!visited) { open.push(destNode); dist.set(destNode, distance + 1); prev.set(destNode, { edge, from: current, to: destNode, cost: distance + 1, }); } } } return prev; }