|
| 1 | +import { randomIntFromInterval } from "./../../utils/randomIntFromInterval"; |
| 2 | + |
1 | 3 | export function astar(grid = [], startNode, finishNode) {
|
2 |
| - const closedlist = []; |
3 |
| - const openlist = []; |
| 4 | + const closedlist = []; |
| 5 | + const openlist = []; |
| 6 | + |
| 7 | + startNode.cost = { |
| 8 | + F: 0, |
| 9 | + G: 0, |
| 10 | + H: 0, |
| 11 | + }; |
| 12 | + |
| 13 | + openlist.push(startNode); |
| 14 | + |
| 15 | + while (!!openlist.length) { |
| 16 | + openlist.sort((a, b) => a.cost.F - b.cost.F); |
| 17 | + const current = openlist.shift(); |
4 | 18 |
|
5 |
| - startNode.cost = { |
6 |
| - F: 0, |
7 |
| - G: 0, |
8 |
| - H: 0, |
9 |
| - }; |
| 19 | + closedlist.push(current); |
10 | 20 |
|
11 |
| - openlist.push(startNode); |
| 21 | + if (current.isWall) continue; |
12 | 22 |
|
13 |
| - while (!!openlist.length) { |
14 |
| - openlist.sort((a, b) => a.cost.F - b.cost.F); |
15 |
| - const current = openlist.shift(); |
| 23 | + if (current === finishNode) return [closedlist, calculatePath(finishNode)]; |
16 | 24 |
|
17 |
| - closedlist.push(current); |
| 25 | + const neighbors = getNeighbors(grid, current); |
18 | 26 |
|
19 |
| - if (current.isWall) continue; |
| 27 | + for (let i = 0; i < neighbors.length; i++) { |
| 28 | + const nNode = neighbors[i]; |
| 29 | + nNode.isVisited = true; |
| 30 | + if (closedlist.includes(nNode)) continue; |
20 | 31 |
|
21 |
| - if (current === finishNode) |
22 |
| - return [closedlist, calculatePath(finishNode)]; |
| 32 | + var m = getCostFunction(["E", "D", "M"], 0); |
23 | 33 |
|
24 |
| - const neighbors = getNeighbors(grid, current); |
| 34 | + nNode.cost.G = calculateCost(nNode, startNode, m); |
| 35 | + nNode.cost.H = calculateCost(nNode, finishNode, m); |
25 | 36 |
|
26 |
| - for (let i = 0; i < neighbors.length; i++) { |
27 |
| - const nNode = neighbors[i]; |
28 |
| - nNode.isVisited = true; |
29 |
| - if (closedlist.includes(nNode)) continue; |
| 37 | + // Needs more research on calculateMinimumDistance function |
| 38 | + // nNode.cost.G = calculateMinimumDistance(nNode, startNode); |
| 39 | + // nNode.cost.H = calculateMinimumDistance(nNode, finishNode); |
30 | 40 |
|
31 |
| - nNode.cost.G = calculateCost(nNode, startNode, "E"); |
32 |
| - nNode.cost.H = calculateCost(nNode, finishNode, "E"); |
33 |
| - nNode.cost.F = nNode.cost.G + nNode.cost.H; |
| 41 | + nNode.cost.F = nNode.cost.G + nNode.cost.H; |
34 | 42 |
|
35 |
| - if (!openlist.includes(nNode)) { |
36 |
| - nNode.previousNode = current; |
37 |
| - openlist.push(nNode); |
38 |
| - } |
39 |
| - } |
| 43 | + if (!openlist.includes(nNode)) { |
| 44 | + nNode.previousNode = current; |
| 45 | + openlist.push(nNode); |
| 46 | + } |
40 | 47 | }
|
41 |
| - return [closedlist, calculatePath(finishNode)]; |
| 48 | + } |
| 49 | + return [closedlist, calculatePath(finishNode)]; |
42 | 50 | }
|
43 | 51 |
|
| 52 | +// Doesnt Always Guarantee Minimum Distance, Needs more planning |
| 53 | +// function calculateMinimumDistance(node1, node2) { |
| 54 | +// var manhattan = calculateCost(node1, node2, "M"); |
| 55 | +// var diagonal = calculateCost(node1, node2, "D"); |
| 56 | +// var euclidean = calculateCost(node1, node2, "E"); |
| 57 | + |
| 58 | +// var temp = manhattan < diagonal ? manhattan : diagonal; |
| 59 | +// return euclidean < temp ? euclidean : temp; |
| 60 | +// } |
| 61 | + |
44 | 62 | function calculateCost(currentNode, node, distanceType) {
|
45 |
| - switch (distanceType) { |
46 |
| - // Euclidean Distance |
47 |
| - case "E": |
48 |
| - return Math.floor( |
49 |
| - Math.sqrt( |
50 |
| - Math.pow(currentNode.row - node.row, 2) + |
51 |
| - Math.pow(currentNode.col - node.col, 2) |
52 |
| - ) * 10 |
53 |
| - ); |
54 |
| - |
55 |
| - // Manhattan Distance |
56 |
| - case "M": |
57 |
| - return ( |
58 |
| - Math.abs(currentNode.row - node.row) + |
59 |
| - Math.abs(currentNode.col - node.col) |
60 |
| - ); |
61 |
| - |
62 |
| - // Diagonal Distance |
63 |
| - case "D": |
64 |
| - return Math.max( |
65 |
| - Math.abs(currentNode.row - node.row), |
66 |
| - Math.abs(currentNode.col - node.col) |
67 |
| - ); |
68 |
| - default: |
69 |
| - return 0; |
70 |
| - } |
| 63 | + switch (distanceType) { |
| 64 | + // Euclidean Distance |
| 65 | + case "E": |
| 66 | + return Math.floor( |
| 67 | + Math.sqrt( |
| 68 | + Math.pow(currentNode.row - node.row, 2) + |
| 69 | + Math.pow(currentNode.col - node.col, 2) |
| 70 | + ) * 10 |
| 71 | + ); |
| 72 | + |
| 73 | + // Manhattan Distance |
| 74 | + case "M": |
| 75 | + return ( |
| 76 | + Math.abs(currentNode.row - node.row) + |
| 77 | + Math.abs(currentNode.col - node.col) |
| 78 | + ); |
| 79 | + |
| 80 | + // Diagonal Distance |
| 81 | + case "D": |
| 82 | + return Math.max( |
| 83 | + Math.abs(currentNode.row - node.row), |
| 84 | + Math.abs(currentNode.col - node.col) |
| 85 | + ); |
| 86 | + default: |
| 87 | + return 0; |
| 88 | + } |
71 | 89 | }
|
72 | 90 |
|
73 |
| -function getNeighbors(grid = [], currentNode) { |
74 |
| - const ROWS = grid.length; |
75 |
| - const COLS = grid[0].length; |
76 |
| - const { row, col } = currentNode; |
77 |
| - const neighbors = []; |
78 |
| - |
79 |
| - // Top, Left, Right, Bottom Nodes |
80 |
| - if ( |
81 |
| - row + 1 >= 0 && |
82 |
| - row + 1 < ROWS && |
83 |
| - col >= 0 && |
84 |
| - col < COLS && |
85 |
| - !grid[row + 1][col].isWall |
86 |
| - ) { |
87 |
| - neighbors.push(grid[row + 1][col]); |
88 |
| - } |
89 |
| - if ( |
90 |
| - row - 1 >= 0 && |
91 |
| - row - 1 < ROWS && |
92 |
| - col >= 0 && |
93 |
| - col < COLS && |
94 |
| - !grid[row - 1][col].isWall |
95 |
| - ) { |
96 |
| - neighbors.push(grid[row - 1][col]); |
97 |
| - } |
98 |
| - if ( |
99 |
| - row >= 0 && |
100 |
| - row < ROWS && |
101 |
| - col - 1 >= 0 && |
102 |
| - col - 1 < COLS && |
103 |
| - !grid[row][col - 1].isWall |
104 |
| - ) { |
105 |
| - neighbors.push(grid[row][col - 1]); |
106 |
| - } |
107 |
| - if ( |
108 |
| - row >= 0 && |
109 |
| - row < ROWS && |
110 |
| - col + 1 >= 0 && |
111 |
| - col + 1 < COLS && |
112 |
| - !grid[row][col + 1].isWall |
113 |
| - ) { |
114 |
| - neighbors.push(grid[row][col + 1]); |
115 |
| - } |
| 91 | +function getCostFunction(costs = [], type) { |
| 92 | + if (type === 3) return costs[randomIntFromInterval(0, costs.length - 1)]; |
| 93 | + return costs[type]; |
| 94 | +} |
116 | 95 |
|
117 |
| - return neighbors; |
| 96 | +function getNeighbors(grid = [], currentNode) { |
| 97 | + const ROWS = grid.length; |
| 98 | + const COLS = grid[0].length; |
| 99 | + const { row, col } = currentNode; |
| 100 | + const neighbors = []; |
| 101 | + |
| 102 | + // Top, Left, Right, Bottom Nodes |
| 103 | + if ( |
| 104 | + row + 1 >= 0 && |
| 105 | + row + 1 < ROWS && |
| 106 | + col >= 0 && |
| 107 | + col < COLS && |
| 108 | + !grid[row + 1][col].isWall |
| 109 | + ) { |
| 110 | + neighbors.push(grid[row + 1][col]); |
| 111 | + } |
| 112 | + if ( |
| 113 | + row - 1 >= 0 && |
| 114 | + row - 1 < ROWS && |
| 115 | + col >= 0 && |
| 116 | + col < COLS && |
| 117 | + !grid[row - 1][col].isWall |
| 118 | + ) { |
| 119 | + neighbors.push(grid[row - 1][col]); |
| 120 | + } |
| 121 | + if ( |
| 122 | + row >= 0 && |
| 123 | + row < ROWS && |
| 124 | + col - 1 >= 0 && |
| 125 | + col - 1 < COLS && |
| 126 | + !grid[row][col - 1].isWall |
| 127 | + ) { |
| 128 | + neighbors.push(grid[row][col - 1]); |
| 129 | + } |
| 130 | + if ( |
| 131 | + row >= 0 && |
| 132 | + row < ROWS && |
| 133 | + col + 1 >= 0 && |
| 134 | + col + 1 < COLS && |
| 135 | + !grid[row][col + 1].isWall |
| 136 | + ) { |
| 137 | + neighbors.push(grid[row][col + 1]); |
| 138 | + } |
| 139 | + |
| 140 | + return neighbors; |
118 | 141 | }
|
119 | 142 |
|
120 | 143 | function calculatePath(finishNode) {
|
121 |
| - const shortestPathNodes = []; |
122 |
| - let currentNode = finishNode; |
123 |
| - while (currentNode !== null) { |
124 |
| - shortestPathNodes.unshift(currentNode); |
125 |
| - currentNode = currentNode.previousNode; |
126 |
| - } |
127 |
| - return shortestPathNodes; |
| 144 | + const shortestPathNodes = []; |
| 145 | + let currentNode = finishNode; |
| 146 | + while (currentNode !== null) { |
| 147 | + shortestPathNodes.unshift(currentNode); |
| 148 | + currentNode = currentNode.previousNode; |
| 149 | + } |
| 150 | + return shortestPathNodes; |
128 | 151 | }
|
0 commit comments