Adjusted small things, norhting serious
This commit is contained in:
@@ -13,9 +13,23 @@
|
|||||||
<strong>A*</strong> {{ 'PATHFINDING.EXPLANATION.ASTAR_EXPLANATION' | translate}}
|
<strong>A*</strong> {{ 'PATHFINDING.EXPLANATION.ASTAR_EXPLANATION' | translate}}
|
||||||
<a href="{{UrlConstants.ASTAR_WIKI}}" target="_blank" rel="noopener noreferrer">Wikipedia</a>
|
<a href="{{UrlConstants.ASTAR_WIKI}}" target="_blank" rel="noopener noreferrer">Wikipedia</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>{{ 'PATHFINDING.EXPLANATION.NOTE' | translate}}</strong> {{ 'PATHFINDING.EXPLANATION.DISCLAIMER' | translate}}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="controls-container">
|
<div class="controls-container">
|
||||||
|
<div class="controls">
|
||||||
|
<button matButton="filled" (click)="visualizeDijkstra()">{{ 'PATHFINDING.DIJKSTRA' | translate }}</button>
|
||||||
|
<button matButton="filled" (click)="visualizeAStar()">{{ 'PATHFINDING.ASTAR' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
<button matButton="filled" (click)="normalCase()">{{ 'PATHFINDING.NORMAL_CASE' | translate }}</button>
|
||||||
|
<button matButton="filled" (click)="edgeCase()">{{ 'PATHFINDING.EDGE_CASE' | translate }}</button>
|
||||||
|
<button matButton="filled" (click)="clearBoard()">{{ 'PATHFINDING.CLEAR_BOARD' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<mat-button-toggle-group [(ngModel)]="selectedNodeType" aria-label="Node Type Selection">
|
<mat-button-toggle-group [(ngModel)]="selectedNodeType" aria-label="Node Type Selection">
|
||||||
<mat-button-toggle [value]="NodeType.Start">{{ 'PATHFINDING.START_NODE' | translate }}</mat-button-toggle>
|
<mat-button-toggle [value]="NodeType.Start">{{ 'PATHFINDING.START_NODE' | translate }}</mat-button-toggle>
|
||||||
@@ -25,14 +39,6 @@
|
|||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="controls">
|
|
||||||
<button matButton="filled" (click)="visualizeDijkstra()">{{ 'PATHFINDING.DIJKSTRA' | translate }}</button>
|
|
||||||
<button matButton="filled" (click)="visualizeAStar()">{{ 'PATHFINDING.ASTAR' | translate }}</button>
|
|
||||||
<button matButton="filled" (click)="normalCase()">{{ 'PATHFINDING.NORMAL_CASE' | translate }}</button>
|
|
||||||
<button matButton="filled" (click)="edgeCase()">{{ 'PATHFINDING.EDGE_CASE' | translate }}</button>
|
|
||||||
<button matButton="filled" (click)="clearBoard()">{{ 'PATHFINDING.CLEAR_BOARD' | translate }}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="grid-size">
|
<div class="grid-size">
|
||||||
<mat-form-field appearance="outline" class="grid-field">
|
<mat-form-field appearance="outline" class="grid-field">
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ export class PathfindingComponent implements AfterViewInit {
|
|||||||
|
|
||||||
// Mouse interactions
|
// Mouse interactions
|
||||||
private onMouseDown(event: MouseEvent): void {
|
private onMouseDown(event: MouseEvent): void {
|
||||||
|
this.stopAnimations();
|
||||||
|
this.clearPath();
|
||||||
const pos = this.getGridPosition(event);
|
const pos = this.getGridPosition(event);
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
return;
|
return;
|
||||||
@@ -263,7 +265,8 @@ export class PathfindingComponent implements AfterViewInit {
|
|||||||
isPath: false,
|
isPath: false,
|
||||||
distance: Infinity,
|
distance: Infinity,
|
||||||
previousNode: null,
|
previousNode: null,
|
||||||
fScore: 0
|
hScore: 0,
|
||||||
|
fScore: Infinity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface Node {
|
|||||||
distance: number;
|
distance: number;
|
||||||
previousNode: Node | null;
|
previousNode: Node | null;
|
||||||
fScore: number;
|
fScore: number;
|
||||||
|
hScore: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_GRID_ROWS = 50;
|
export const DEFAULT_GRID_ROWS = 50;
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ export class PathfindingService {
|
|||||||
aStar(grid: Node[][], startNode: Node, endNode: Node): { visitedNodesInOrder: Node[], nodesInShortestPathOrder: Node[] } {
|
aStar(grid: Node[][], startNode: Node, endNode: Node): { visitedNodesInOrder: Node[], nodesInShortestPathOrder: Node[] } {
|
||||||
const visitedNodesInOrder: Node[] = [];
|
const visitedNodesInOrder: Node[] = [];
|
||||||
startNode.distance = 0;
|
startNode.distance = 0;
|
||||||
startNode['distance'] = this.calculateHeuristic(startNode, endNode);
|
startNode['hScore'] = this.calculateHeuristic(startNode, endNode);
|
||||||
// fScore = gScore + hScore
|
// fScore = gScore + hScore
|
||||||
startNode['fScore'] = startNode.distance + startNode['distance'];
|
startNode['fScore'] = startNode.distance + startNode['hScore'];
|
||||||
|
|
||||||
const openSet: Node[] = [startNode];
|
const openSet: Node[] = [startNode];
|
||||||
const allNodes = this.getAllNodes(grid);
|
const allNodes = this.getAllNodes(grid);
|
||||||
@@ -90,7 +90,7 @@ export class PathfindingService {
|
|||||||
this.initNodesForAStar(allNodes, startNode);
|
this.initNodesForAStar(allNodes, startNode);
|
||||||
|
|
||||||
while (openSet.length > 0) {
|
while (openSet.length > 0) {
|
||||||
openSet.sort((nodeA, nodeB) => nodeA['fScore'] - nodeB['fScore']);
|
this.sortOpenSet(openSet);
|
||||||
const currentNode = openSet.shift() as Node;
|
const currentNode = openSet.shift() as Node;
|
||||||
|
|
||||||
if (currentNode.isWall) {
|
if (currentNode.isWall) {
|
||||||
@@ -125,11 +125,22 @@ export class PathfindingService {
|
|||||||
return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
|
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[]) {
|
private updateNeighborNode(neighbor: Node, currentNode: Node, tentativeGScore: number, endNode: Node, openSet: Node[]) {
|
||||||
neighbor.previousNode = currentNode;
|
neighbor.previousNode = currentNode;
|
||||||
neighbor.distance = tentativeGScore;
|
neighbor.distance = tentativeGScore;
|
||||||
neighbor['distance'] = this.calculateHeuristic(neighbor, endNode);
|
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)) {
|
if (!openSet.includes(neighbor)) {
|
||||||
openSet.push(neighbor);
|
openSet.push(neighbor);
|
||||||
|
|||||||
@@ -299,10 +299,10 @@
|
|||||||
"END_NODE": "Endknoten",
|
"END_NODE": "Endknoten",
|
||||||
"WALL": "Wand",
|
"WALL": "Wand",
|
||||||
"CLEAR_NODE": "Löschen",
|
"CLEAR_NODE": "Löschen",
|
||||||
"DIJKSTRA": "Dijkstra",
|
"DIJKSTRA": "Start Dijkstra",
|
||||||
"ASTAR": "A*",
|
"ASTAR": "Start A*",
|
||||||
"NORMAL_CASE": "Testaufbau",
|
"NORMAL_CASE": "Testaufbau",
|
||||||
"EDGE_CASE": "A* Grenzfall",
|
"EDGE_CASE": "A* Grenzfall-Aufbau",
|
||||||
"CLEAR_BOARD": "Board leeren",
|
"CLEAR_BOARD": "Board leeren",
|
||||||
"VISITED": "Besucht",
|
"VISITED": "Besucht",
|
||||||
"PATH": "Pfad",
|
"PATH": "Pfad",
|
||||||
@@ -311,7 +311,9 @@
|
|||||||
"EXPLANATION": {
|
"EXPLANATION": {
|
||||||
"TITLE": "Algorithmen",
|
"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).",
|
"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": {
|
"ALERT": {
|
||||||
"START_END_NODES": "Bitte wählen Sie einen Start- und Endknoten aus, bevor Sie den Algorithmus starten."
|
"START_END_NODES": "Bitte wählen Sie einen Start- und Endknoten aus, bevor Sie den Algorithmus starten."
|
||||||
|
|||||||
@@ -299,10 +299,10 @@
|
|||||||
"END_NODE": "End Node",
|
"END_NODE": "End Node",
|
||||||
"WALL": "Wall",
|
"WALL": "Wall",
|
||||||
"CLEAR_NODE": "Clear",
|
"CLEAR_NODE": "Clear",
|
||||||
"DIJKSTRA": "Dijkstra",
|
"DIJKSTRA": "Start Dijkstra",
|
||||||
"ASTAR": "A*",
|
"ASTAR": "Start A*",
|
||||||
"NORMAL_CASE": "Test Scenario",
|
"NORMAL_CASE": "Test Scenario",
|
||||||
"EDGE_CASE": "A* Edge Case",
|
"EDGE_CASE": "A* Edge Case Scenario",
|
||||||
"CLEAR_BOARD": "Clear Board",
|
"CLEAR_BOARD": "Clear Board",
|
||||||
"VISITED": "Visited",
|
"VISITED": "Visited",
|
||||||
"PATH": "Path",
|
"PATH": "Path",
|
||||||
@@ -311,7 +311,9 @@
|
|||||||
"EXPLANATION": {
|
"EXPLANATION": {
|
||||||
"TITLE": "Algorithms",
|
"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).",
|
"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": {
|
"ALERT": {
|
||||||
"START_END_NODES": "Please select a start and end node before running the algorithm."
|
"START_END_NODES": "Please select a start and end node before running the algorithm."
|
||||||
|
|||||||
Reference in New Issue
Block a user