diff --git a/src/app/pages/algorithms/pathfinding/pathfinding.component.ts b/src/app/pages/algorithms/pathfinding/pathfinding.component.ts
index bbd8b49..0f9b62b 100644
--- a/src/app/pages/algorithms/pathfinding/pathfinding.component.ts
+++ b/src/app/pages/algorithms/pathfinding/pathfinding.component.ts
@@ -163,6 +163,8 @@ export class PathfindingComponent implements AfterViewInit {
// Mouse interactions
private onMouseDown(event: MouseEvent): void {
+ this.stopAnimations();
+ this.clearPath();
const pos = this.getGridPosition(event);
if (!pos) {
return;
@@ -263,7 +265,8 @@ export class PathfindingComponent implements AfterViewInit {
isPath: false,
distance: Infinity,
previousNode: null,
- fScore: 0
+ hScore: 0,
+ fScore: Infinity,
};
}
diff --git a/src/app/pages/algorithms/pathfinding/pathfinding.models.ts b/src/app/pages/algorithms/pathfinding/pathfinding.models.ts
index f766798..b5fb259 100644
--- a/src/app/pages/algorithms/pathfinding/pathfinding.models.ts
+++ b/src/app/pages/algorithms/pathfinding/pathfinding.models.ts
@@ -9,6 +9,7 @@ export interface Node {
distance: number;
previousNode: Node | null;
fScore: number;
+ hScore: number;
}
export const DEFAULT_GRID_ROWS = 50;
diff --git a/src/app/pages/algorithms/pathfinding/service/pathfinding.service.ts b/src/app/pages/algorithms/pathfinding/service/pathfinding.service.ts
index e6cc349..9519c84 100644
--- a/src/app/pages/algorithms/pathfinding/service/pathfinding.service.ts
+++ b/src/app/pages/algorithms/pathfinding/service/pathfinding.service.ts
@@ -80,9 +80,9 @@ export class PathfindingService {
aStar(grid: Node[][], startNode: Node, endNode: Node): { visitedNodesInOrder: Node[], nodesInShortestPathOrder: Node[] } {
const visitedNodesInOrder: Node[] = [];
startNode.distance = 0;
- startNode['distance'] = this.calculateHeuristic(startNode, endNode);
+ startNode['hScore'] = this.calculateHeuristic(startNode, endNode);
// fScore = gScore + hScore
- startNode['fScore'] = startNode.distance + startNode['distance'];
+ startNode['fScore'] = startNode.distance + startNode['hScore'];
const openSet: Node[] = [startNode];
const allNodes = this.getAllNodes(grid);
@@ -90,7 +90,7 @@ export class PathfindingService {
this.initNodesForAStar(allNodes, startNode);
while (openSet.length > 0) {
- openSet.sort((nodeA, nodeB) => nodeA['fScore'] - nodeB['fScore']);
+ this.sortOpenSet(openSet);
const currentNode = openSet.shift() as Node;
if (currentNode.isWall) {
@@ -125,11 +125,22 @@ export class PathfindingService {
return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
}
+ private sortOpenSet(openSet: Node[]) {
+ openSet.sort((a, b) => {
+ const f = a['fScore'] - b['fScore'];
+ if (f !== 0) return f;
+
+ // tie-break: smaller heuristic first (more goal-directed)
+ return (a['hScore'] ?? 0) - (b['hScore'] ?? 0);
+ });
+ }
+
private updateNeighborNode(neighbor: Node, currentNode: Node, tentativeGScore: number, endNode: Node, openSet: Node[]) {
neighbor.previousNode = currentNode;
neighbor.distance = tentativeGScore;
neighbor['distance'] = this.calculateHeuristic(neighbor, endNode);
- neighbor['fScore'] = neighbor.distance + neighbor['distance'];
+ neighbor['hScore'] = this.calculateHeuristic(neighbor, endNode);
+ neighbor['fScore'] = neighbor.distance + neighbor['hScore'];
if (!openSet.includes(neighbor)) {
openSet.push(neighbor);
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 77b96d7..d30988a 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -299,10 +299,10 @@
"END_NODE": "Endknoten",
"WALL": "Wand",
"CLEAR_NODE": "Löschen",
- "DIJKSTRA": "Dijkstra",
- "ASTAR": "A*",
+ "DIJKSTRA": "Start Dijkstra",
+ "ASTAR": "Start A*",
"NORMAL_CASE": "Testaufbau",
- "EDGE_CASE": "A* Grenzfall",
+ "EDGE_CASE": "A* Grenzfall-Aufbau",
"CLEAR_BOARD": "Board leeren",
"VISITED": "Besucht",
"PATH": "Pfad",
@@ -311,7 +311,9 @@
"EXPLANATION": {
"TITLE": "Algorithmen",
"DIJKSTRA_EXPLANATION": " findet garantiert den kürzesten Weg, wenn alle Kantenkosten nicht-negativ sind. Vorteil: optimal und ohne Heuristik. Nachteil: besucht oft sehr viele Knoten (kann bei großen Grids langsamer wirken).",
- "ASTAR_EXPLANATION": " erweitert Dijkstra um eine Heuristik (z.B. Manhattan-Distanz) und kann dadurch wesentlich zielgerichteter suchen. Vorteil: oft deutlich schneller bei guter Heuristik; bei zulässiger Heuristik bleibt der Weg optimal. Nachteil: hängt stark von der Heuristik ab (schlechte Heuristik ≈ Dijkstra)."
+ "ASTAR_EXPLANATION": " erweitert Dijkstra um eine Heuristik (z.B. Manhattan-Distanz) und kann dadurch wesentlich zielgerichteter suchen. Vorteil: oft deutlich schneller bei guter Heuristik; bei zulässiger Heuristik bleibt der Weg optimal. Nachteil: hängt stark von der Heuristik ab (schlechte Heuristik ≈ Dijkstra).",
+ "NOTE": "HINWEIS",
+ "DISCLAIMER": "Diese A*-Implementierung ist bewusst einfach gehalten. Es wird nur in vier Richtungen gegangen und jeder Schritt kostet 1. Die Heuristik ist minimal und dient nur dazu, das Prinzip von A* gegenüber Dijkstra zu demonstrieren. Ziel ist nicht ein optimaler oder produktionsreifer A*-Algorithmus, sondern eine anschauliche Visualisierung, wie Heuristiken die Suche beschleunigen können."
},
"ALERT": {
"START_END_NODES": "Bitte wählen Sie einen Start- und Endknoten aus, bevor Sie den Algorithmus starten."
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 4bb9c6e..71e8757 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -299,10 +299,10 @@
"END_NODE": "End Node",
"WALL": "Wall",
"CLEAR_NODE": "Clear",
- "DIJKSTRA": "Dijkstra",
- "ASTAR": "A*",
+ "DIJKSTRA": "Start Dijkstra",
+ "ASTAR": "Start A*",
"NORMAL_CASE": "Test Scenario",
- "EDGE_CASE": "A* Edge Case",
+ "EDGE_CASE": "A* Edge Case Scenario",
"CLEAR_BOARD": "Clear Board",
"VISITED": "Visited",
"PATH": "Path",
@@ -311,7 +311,9 @@
"EXPLANATION": {
"TITLE": "Algorithms",
"DIJKSTRA_EXPLANATION": " is guaranteed to find the shortest path if all edge costs are non-negative. Advantage: optimal and without heuristics. Disadvantage: often visits a large number of nodes (can be slower for large grids).",
- "ASTAR_EXPLANATION": " extends Dijkstra with a heuristic (e.g. Manhattan distance) and can therefore search in a much more targeted manner. Advantage: often significantly faster with good heuristics; with permissible heuristics, the path remains optimal. Disadvantage: highly dependent on heuristics (poor heuristics ≈ Dijkstra)."
+ "ASTAR_EXPLANATION": " extends Dijkstra with a heuristic (e.g. Manhattan distance) and can therefore search in a much more targeted manner. Advantage: often significantly faster with good heuristics; with permissible heuristics, the path remains optimal. Disadvantage: highly dependent on heuristics (poor heuristics ≈ Dijkstra).",
+ "NOTE": "Note",
+ "DISCLAIMER": "This A* implementation is deliberately kept simple. It only moves in four directions and each step costs 1. The heuristic is minimal and only serves to demonstrate the principle of A* compared to Dijkstra. The goal is not an optimal or production-ready A* algorithm, but a clear visualisation of how heuristics can speed up the search."
},
"ALERT": {
"START_END_NODES": "Please select a start and end node before running the algorithm."