diff --git a/solutions/typescript/libs/lib/src/model/graph/graph.class.ts b/solutions/typescript/libs/lib/src/model/graph/graph.class.ts index 9dd7d7c1e..cf6e1d16b 100644 --- a/solutions/typescript/libs/lib/src/model/graph/graph.class.ts +++ b/solutions/typescript/libs/lib/src/model/graph/graph.class.ts @@ -168,121 +168,19 @@ export class Graph< Omit, 'pathConstructor' | 'allNodes'>, ): PathFindingResult { return dijkstra({ + ...options, allNodes: this.nodes, - start: options.start, - end: options.end, - currentPathWeighter: options.currentPathWeighter, - edgeFilter: options.edgeFilter, - edgeGenerator: options.edgeGenerator, }); } - /** - * - */ - public floodDiskstra(start: N | undefined): Map { - if (!start) { - return new Map(); - } - const q = new Set(this.nodes.values()); - - const dist = new Map(); - const prev = new Map(); - dist.set(start, 0); - - while (q.size > 0) { - // refactor this to a prio queue - const umin = [...q.values()].reduce( - (acc, b) => { - const u = dist.get(b) ?? Number.POSITIVE_INFINITY; - if (!acc.node || u < acc.dist) { - acc.node = b; - acc.dist = dist.get(b) ?? Number.POSITIVE_INFINITY; - } - return acc; - }, - { node: undefined as N | undefined, dist: Number.POSITIVE_INFINITY }, - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const u = umin.node!; - - q.delete(u); - - for (const neighbour of u) { - const alt = umin.dist + (neighbour.weight ?? 1); - if (alt < (dist.get(neighbour.to) ?? Number.POSITIVE_INFINITY)) { - dist.set(neighbour.to, alt); - prev.set(neighbour.to, u); - } - } - } - - return dist; - } - /** * A gutted out aStar, not trying to find a path, but calculating a distanceMap - * to all reachable node. + * to all reachable nodes. */ - public flood(options: GraphTraversalOptions): Map { - if (!options.start) { - return new Map(); - } - const openSet = new Set([options.start]); // q? - const cameFrom = new Map(); // prev! - const gScore = new Map(); // weightMap! Infinity - const dMap = new Map(); // distanceMap Infinity - - const h = options?.heuristic ?? (() => 1); - - gScore.set(options.start, 0); - dMap.set(options.start, 0); - - const fScore = new Map(); // Infinity - fScore.set(options.start, h(options.start, [])); - - while (openSet.size > 0) { - const umin = [...openSet.values()].reduce( - (acc, b) => { - const u = fScore.get(b) ?? Number.POSITIVE_INFINITY; - if (!acc.node || u < acc.dist) { - acc.node = b; - acc.dist = fScore.get(b) ?? Number.POSITIVE_INFINITY; - } - return acc; - }, - { node: undefined as N | undefined, dist: Number.POSITIVE_INFINITY }, - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const current = umin.node!; - - openSet.delete(current); - - for (const neighbour of options?.edgeGenerator?.(this.nodes, current, []) ?? current) { - const tentativegScore = - (gScore.get(current) ?? Number.POSITIVE_INFINITY) + - (options?.currentPathWeighter - ? options.currentPathWeighter( - neighbour.from, - neighbour.to, - neighbour.direction, - [], - ) - : neighbour.weight ?? 1); - const tentativeDistance = (dMap.get(current) ?? 0) + 1; - if (tentativegScore < (gScore.get(neighbour.to) ?? Number.POSITIVE_INFINITY)) { - cameFrom.set(neighbour.to, current); - gScore.set(neighbour.to, tentativegScore); - fScore.set(neighbour.to, tentativegScore); - dMap.set(neighbour.to, tentativeDistance); - if (!openSet.has(neighbour.to)) { - openSet.add(neighbour.to); - } - } - } - } - - return dMap; + public flood( + options: Omit, 'end' | 'heuristic'>, + ): Map { + return aStar({ ...options, allNodes: this.nodes, end: undefined }).distances; } /** diff --git a/solutions/typescript/libs/lib/src/pathfinding/astar.ts b/solutions/typescript/libs/lib/src/pathfinding/astar.ts index ba60580f8..33fd4793d 100644 --- a/solutions/typescript/libs/lib/src/pathfinding/astar.ts +++ b/solutions/typescript/libs/lib/src/pathfinding/astar.ts @@ -12,11 +12,19 @@ import { type PathFindingResult, } from './dijkstra.js'; +/** + * Finds the shortest path between two nodes in a graph. The nodes must be aware + * of their own neighbours. + * + * When an end is not defined the algorithm will travel though the reachable + * parts of the graph from the start and calculate the distance to + * each nodes from the start. + */ export const aStar = >( options: GraphTraversalOptions & Omit, 'pathConstructor'>, ): PathFindingResult => { - if (!options.start || !options.end) { + if (!options.start) { return { path: [], distances: new Map() }; } @@ -28,7 +36,7 @@ export const aStar = (); // How many nodes there are to reach the end gScore.set(options.start, 0); - fScore.set(options.start, h(options.start, [])); + fScore.set(options.start, options.end ? h(options.start, []) : 1); pathLengthMap.set(options.start, 0); // This is used to support weights of 0 const orderOfDiscovery = [options.start]; @@ -51,7 +59,6 @@ export const aStar = = (to) => constructPath(options.start, to, prev); const isFinished = isNotNullish(options.end) @@ -66,7 +73,6 @@ export const aStar = 0) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const current = pq.pop()!; // u, closest yet - orderOfDiscovery.removeItem(current); if (isFinished(current)) { @@ -90,12 +96,16 @@ export const aStar = (options.start, current, prev); - prev.set(neighbour.to, current); gScore.set(neighbour.to, tentativegScore); - fScore.set(neighbour.to, tentativegScore + h(neighbour.to, currentPath)); - pathLengthMap.set(neighbour.to, (pathLengthMap.get(neighbour.to) ?? 0) + 1); + fScore.set( + neighbour.to, + tentativegScore + + (options.end + ? h(neighbour.to, constructPath(options.start, current, prev)) + : 1), + ); + pathLengthMap.set(neighbour.to, (pathLengthMap.get(current) ?? 0) + 1); if (!pq.updateItem(neighbour.to)) { pq.push(neighbour.to); @@ -106,7 +116,7 @@ export const aStar = (options.start, goal, prev), + path: goal ? constructPath(options.start, goal, prev) : [], distances: pathLengthMap, }; }; diff --git a/solutions/typescript/libs/lib/src/pathfinding/dijkstra.ts b/solutions/typescript/libs/lib/src/pathfinding/dijkstra.ts index f394f8119..3fc50acf3 100644 --- a/solutions/typescript/libs/lib/src/pathfinding/dijkstra.ts +++ b/solutions/typescript/libs/lib/src/pathfinding/dijkstra.ts @@ -146,13 +146,8 @@ export const dijkstra = < } } - return target - ? { - distances: pathLengthMap, - path: constructPath(options.start, target, prev), - } - : { - distances: pathLengthMap, - path: [], - }; + return { + distances: pathLengthMap, + path: target ? constructPath(options.start, target, prev) : [], + }; };