|
| 1 | +// A* (A-Star) search algorithm for 2D grids by Évan (xzripper): https://github.com/xzripper/AStarD |
| 2 | + |
| 3 | +module AStarD.AStar; |
| 4 | + |
| 5 | +import std.math : abs, pow, sqrt; |
| 6 | + |
| 7 | +import std.algorithm : reverse; |
| 8 | + |
| 9 | +import AStarD.DHeap.Heap; |
| 10 | + |
| 11 | +alias ASPath = int[2][], Grid2d = int[][], Position2D = int[2]; |
| 12 | + |
| 13 | +enum Heuristic { MANHATTAN, EUCLIDEAN, OCTILE, CHEBYSHEV } |
| 14 | + |
| 15 | +private static immutable STRAIGHT_DIRECTIONS = [[-1, 0], [1, 0], [0, -1], [0, 1]]; |
| 16 | +private static immutable STRAIGHT_DIAGONAL_DIRECTIONS = [[-1, 0], [1, 0], [0, -1], [0, 1], |
| 17 | + [-1, -1], [-1, 1], [1, -1], [1, 1]]; |
| 18 | + |
| 19 | +private alias Node = float[3]; |
| 20 | + |
| 21 | +private float _CalculateHeuristic( |
| 22 | + Position2D p_Position0, |
| 23 | + Position2D p_Position1, |
| 24 | + Heuristic p_Heuristic ) @safe |
| 25 | +{ |
| 26 | + if( p_Heuristic == Heuristic.MANHATTAN ) { |
| 27 | + return abs( p_Position0[0] - p_Position1[0] ) + abs( p_Position0[1] - p_Position1[1] ); |
| 28 | + } else if( p_Heuristic == Heuristic.EUCLIDEAN ) { |
| 29 | + return sqrt( cast(float) (pow( p_Position0[0] - p_Position1[0], 2 ) + |
| 30 | + pow( p_Position0[1] - p_Position1[1], 2 )) ); |
| 31 | + } else if( p_Heuristic == Heuristic.OCTILE ) { |
| 32 | + float t_DX = abs( p_Position0[0] - p_Position1[0] ); |
| 33 | + float t_DY = abs( p_Position0[1] - p_Position1[1] ); |
| 34 | + |
| 35 | + return (t_DX > t_DY ? t_DX : t_DY) + (sqrt( 2.0f ) - 1) * (t_DY > t_DX ? t_DY : t_DX); |
| 36 | + } else if( p_Heuristic == Heuristic.CHEBYSHEV ) { |
| 37 | + float t_MaxOp0 = abs( p_Position0[0] - p_Position1[0] ); |
| 38 | + float t_MaxOp1 = abs( p_Position0[1] - p_Position1[1] ); |
| 39 | + |
| 40 | + return (t_MaxOp0 > t_MaxOp1 ? t_MaxOp0 : t_MaxOp1); |
| 41 | + } else { assert( 0 ); } |
| 42 | +} |
| 43 | + |
| 44 | +ASPath AStar( |
| 45 | + Grid2d p_Grid, |
| 46 | + Position2D p_Start, |
| 47 | + Position2D p_Target, |
| 48 | + Heuristic p_Heuristic, |
| 49 | + bool p_AllowDiagonalMovement ) @safe |
| 50 | +{ |
| 51 | + Heap!Node t_NodesHeap = Heap!Node( HeapType.MIN_HEAP ); |
| 52 | + |
| 53 | + t_NodesHeap.HeapPush( [0, p_Start[0], p_Start[1]] ); |
| 54 | + |
| 55 | + int[2][int[2]] t_CameFrom; |
| 56 | + |
| 57 | + float[int[2]] t_GScores = [p_Start: 0]; |
| 58 | + float[int[2]] t_FScores = [p_Start: _CalculateHeuristic( p_Start, p_Target, p_Heuristic )]; |
| 59 | + |
| 60 | + while( !t_NodesHeap.Empty() ) |
| 61 | + { |
| 62 | + float[3] t_HeapPopOut = t_NodesHeap.HeapPop(); |
| 63 | + |
| 64 | + int[2] t_CurrentNodePos = [cast(int) t_HeapPopOut[1], cast(int) t_HeapPopOut[2]]; |
| 65 | + |
| 66 | + if( t_CurrentNodePos == p_Target ) |
| 67 | + { |
| 68 | + int[2][] t_OutPath; |
| 69 | + |
| 70 | + while( t_CurrentNodePos in t_CameFrom ) |
| 71 | + { |
| 72 | + t_OutPath ~= t_CurrentNodePos; |
| 73 | + |
| 74 | + t_CurrentNodePos = t_CameFrom[t_CurrentNodePos]; |
| 75 | + } |
| 76 | + |
| 77 | + t_OutPath ~= p_Start; |
| 78 | + |
| 79 | + return reverse( t_OutPath ); |
| 80 | + } |
| 81 | + |
| 82 | + foreach( int[2] t_Direction; (p_AllowDiagonalMovement ? STRAIGHT_DIAGONAL_DIRECTIONS : STRAIGHT_DIRECTIONS) ) |
| 83 | + { |
| 84 | + int[2] t_Neighbour = [t_CurrentNodePos[0] + t_Direction[0], t_CurrentNodePos[1] + t_Direction[1]]; |
| 85 | + |
| 86 | + if( 0 <= t_Neighbour[0] && t_Neighbour[0] < p_Grid.length && |
| 87 | + 0 <= t_Neighbour[1] && t_Neighbour[1] < p_Grid[0].length ) |
| 88 | + { |
| 89 | + if( p_Grid[t_Neighbour[0]][t_Neighbour[1]] == 1 ) { continue; } |
| 90 | + |
| 91 | + float t_TentativeGScore = t_GScores[t_CurrentNodePos] + (p_AllowDiagonalMovement ? |
| 92 | + (t_Direction[0] != 0 && t_Direction[1] != 0 ? sqrt( 2.0f ) : 1.0f) : 1); |
| 93 | + |
| 94 | + if( t_Neighbour !in t_GScores || t_TentativeGScore < t_GScores[t_Neighbour] ) |
| 95 | + { |
| 96 | + t_CameFrom[t_Neighbour] = t_CurrentNodePos; |
| 97 | + |
| 98 | + t_GScores[t_Neighbour] = t_TentativeGScore; |
| 99 | + t_FScores[t_Neighbour] = t_TentativeGScore + _CalculateHeuristic( t_Neighbour, |
| 100 | + p_Target, |
| 101 | + p_Heuristic ); |
| 102 | + |
| 103 | + t_NodesHeap.HeapPush( [t_FScores[t_Neighbour], t_Neighbour[0], t_Neighbour[1]] ); |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + return []; |
| 110 | +} |
0 commit comments