Small refactoring for better readabilty
This commit is contained in:
@@ -9,11 +9,12 @@
|
|||||||
<mat-button-toggle [value]="NodeType.Wall">{{ 'PATHFINDING.WALL' | translate }}</mat-button-toggle>
|
<mat-button-toggle [value]="NodeType.Wall">{{ 'PATHFINDING.WALL' | translate }}</mat-button-toggle>
|
||||||
<mat-button-toggle [value]="NodeType.None">{{ 'PATHFINDING.CLEAR_NODE' | translate }}</mat-button-toggle>
|
<mat-button-toggle [value]="NodeType.None">{{ 'PATHFINDING.CLEAR_NODE' | translate }}</mat-button-toggle>
|
||||||
</mat-button-toggle-group>
|
</mat-button-toggle-group>
|
||||||
|
</div>
|
||||||
<button mat-raised-button color="primary" (click)="visualizeDijkstra()">{{ 'PATHFINDING.DIJKSTRA' | translate }}</button>
|
<div class="controls">
|
||||||
<button mat-raised-button color="accent" (click)="visualizeAStar()">{{ 'PATHFINDING.ASTAR' | translate }}</button>
|
<button matButton="filled" class="primary" (click)="visualizeDijkstra()">{{ 'PATHFINDING.DIJKSTRA' | translate }}</button>
|
||||||
<button mat-raised-button color="warn" (click)="resetBoard()">{{ 'PATHFINDING.RESET_BOARD' | translate }}</button>
|
<button matButton="filled" class="accent" (click)="visualizeAStar()">{{ 'PATHFINDING.ASTAR' | translate }}</button>
|
||||||
<button mat-raised-button color="warn" (click)="clearBoard()">{{ 'PATHFINDING.CLEAR_BOARD' | translate }}</button>
|
<button matButton="filled" class="warn" (click)="resetBoard()">{{ 'PATHFINDING.RESET_BOARD' | translate }}</button>
|
||||||
|
<button matButton="filled" class="warn" (click)="clearBoard()">{{ 'PATHFINDING.CLEAR_BOARD' | translate }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="legend">
|
<div class="legend">
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { Node, GRID_ROWS, GRID_COLS } from '../pathfinding.models';
|
|||||||
})
|
})
|
||||||
export class PathfindingService {
|
export class PathfindingService {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Helper function to get all unvisited neighbors of a given node
|
// Helper function to get all unvisited neighbors of a given node
|
||||||
getUnvisitedNeighbors(node: Node, grid: Node[][]): Node[] {
|
getUnvisitedNeighbors(node: Node, grid: Node[][]): Node[] {
|
||||||
const neighbors: Node[] = [];
|
const neighbors: Node[] = [];
|
||||||
@@ -42,16 +40,23 @@ export class PathfindingService {
|
|||||||
this.sortNodesByDistance(unvisitedNodes);
|
this.sortNodesByDistance(unvisitedNodes);
|
||||||
const closestNode = unvisitedNodes.shift() as Node;
|
const closestNode = unvisitedNodes.shift() as Node;
|
||||||
|
|
||||||
// If we encounter a wall, skip it
|
if (closestNode.isWall) {
|
||||||
if (closestNode.isWall) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If the closest node is at an infinite distance, we're trapped
|
const isTrapped = closestNode.distance === Infinity;
|
||||||
if (closestNode.distance === Infinity) return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
|
if (isTrapped)
|
||||||
|
{
|
||||||
|
return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
|
||||||
|
}
|
||||||
|
|
||||||
closestNode.isVisited = true;
|
closestNode.isVisited = true;
|
||||||
visitedNodesInOrder.push(closestNode);
|
visitedNodesInOrder.push(closestNode);
|
||||||
|
|
||||||
if (closestNode === endNode) return { visitedNodesInOrder, nodesInShortestPathOrder: this.getNodesInShortestPath(endNode) };
|
const reachedTheEnd = closestNode === endNode;
|
||||||
|
if (reachedTheEnd) {
|
||||||
|
return {visitedNodesInOrder, nodesInShortestPathOrder: this.getNodesInShortestPath(endNode)};
|
||||||
|
}
|
||||||
|
|
||||||
this.updateUnvisitedNeighbors(closestNode, grid);
|
this.updateUnvisitedNeighbors(closestNode, grid);
|
||||||
}
|
}
|
||||||
@@ -75,7 +80,6 @@ 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;
|
||||||
// hueristic distance
|
|
||||||
startNode['distance'] = this.calculateHeuristic(startNode, endNode);
|
startNode['distance'] = this.calculateHeuristic(startNode, endNode);
|
||||||
// fScore = gScore + hScore
|
// fScore = gScore + hScore
|
||||||
startNode['fScore'] = startNode.distance + startNode['distance'];
|
startNode['fScore'] = startNode.distance + startNode['distance'];
|
||||||
@@ -83,29 +87,28 @@ export class PathfindingService {
|
|||||||
const openSet: Node[] = [startNode];
|
const openSet: Node[] = [startNode];
|
||||||
const allNodes = this.getAllNodes(grid);
|
const allNodes = this.getAllNodes(grid);
|
||||||
|
|
||||||
// Initialize all nodes' fScore to infinity except for the startNode
|
this.initNodesForAStar(allNodes, startNode);
|
||||||
for (const node of allNodes) {
|
|
||||||
if (node !== startNode) {
|
|
||||||
node['fScore'] = Infinity;
|
|
||||||
node.distance = Infinity; // gScore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (openSet.length > 0) {
|
while (openSet.length > 0) {
|
||||||
openSet.sort((nodeA, nodeB) => nodeA['fScore'] - nodeB['fScore']);
|
openSet.sort((nodeA, nodeB) => nodeA['fScore'] - nodeB['fScore']);
|
||||||
const currentNode = openSet.shift() as Node;
|
const currentNode = openSet.shift() as Node;
|
||||||
|
|
||||||
if (currentNode.isWall) continue;
|
if (currentNode.isWall) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If the closest node is at an infinite distance, we're trapped
|
const isTrapped = currentNode.distance === Infinity;
|
||||||
if (currentNode.distance === Infinity) return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
|
if (isTrapped)
|
||||||
|
{
|
||||||
|
return {visitedNodesInOrder, nodesInShortestPathOrder: []};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
currentNode.isVisited = true;
|
currentNode.isVisited = true;
|
||||||
visitedNodesInOrder.push(currentNode);
|
visitedNodesInOrder.push(currentNode);
|
||||||
|
|
||||||
if (currentNode === endNode) {
|
const reachedTheEnd = currentNode === endNode;
|
||||||
|
if (reachedTheEnd) {
|
||||||
return { visitedNodesInOrder, nodesInShortestPathOrder: this.getNodesInShortestPath(endNode) };
|
return { visitedNodesInOrder, nodesInShortestPathOrder: this.getNodesInShortestPath(endNode) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,14 +117,7 @@ export class PathfindingService {
|
|||||||
const tentativeGScore = currentNode.distance + 1; // Distance from start to neighbor
|
const tentativeGScore = currentNode.distance + 1; // Distance from start to neighbor
|
||||||
|
|
||||||
if (tentativeGScore < neighbor.distance) {
|
if (tentativeGScore < neighbor.distance) {
|
||||||
neighbor.previousNode = currentNode;
|
this.updateNeighborNode(neighbor, currentNode, tentativeGScore, endNode, openSet);
|
||||||
neighbor.distance = tentativeGScore;
|
|
||||||
neighbor['distance'] = this.calculateHeuristic(neighbor, endNode);
|
|
||||||
neighbor['fScore'] = neighbor.distance + neighbor['distance'];
|
|
||||||
|
|
||||||
if (!openSet.includes(neighbor)) {
|
|
||||||
openSet.push(neighbor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,6 +125,26 @@ export class PathfindingService {
|
|||||||
return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
|
return { visitedNodesInOrder, nodesInShortestPathOrder: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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'];
|
||||||
|
|
||||||
|
if (!openSet.includes(neighbor)) {
|
||||||
|
openSet.push(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private initNodesForAStar(allNodes: Node[], startNode: Node) {
|
||||||
|
for (const node of allNodes) {
|
||||||
|
if (node !== startNode) {
|
||||||
|
node['fScore'] = Infinity;
|
||||||
|
node.distance = Infinity; // gScore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private calculateHeuristic(node: Node, endNode: Node): number {
|
private calculateHeuristic(node: Node, endNode: Node): number {
|
||||||
// Manhattan distance heuristic
|
// Manhattan distance heuristic
|
||||||
return Math.abs(node.row - endNode.row) + Math.abs(node.col - endNode.col);
|
return Math.abs(node.row - endNode.row) + Math.abs(node.col - endNode.col);
|
||||||
|
|||||||
Reference in New Issue
Block a user