Skip to content

Commit d4a8b69

Browse files
authored
AStarD::AStar / AStarD::DHeap::Heap
1 parent e1a6b88 commit d4a8b69

File tree

2 files changed

+252
-0
lines changed

2 files changed

+252
-0
lines changed

AStarD/AStar.d

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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+
}

AStarD/DHeap/Heap.d

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* This is a heap implementation that can be used as a Min-Heap or Max-Heap.
3+
* It keeps the smallest or biggest item at the top depending on the type of heap.
4+
* The heap can be built from an already existing array or can be initially empty.
5+
* New items can be added, and the data structure will rearrange itself to maintain order.
6+
* Popping the top element replaces it with the last and reorders the heap.
7+
* It is iterable and optimized to avoid useless allocations of memory.
8+
* Uses safe code and minimizes reliance on the garbage collector.
9+
*
10+
* https://github.com/xzripper/DHeap by Évan
11+
*/
12+
13+
module DHeap.Heap;
14+
15+
enum HeapType { MAX_HEAP, MIN_HEAP }
16+
17+
struct Heap(T)
18+
{
19+
private T[] m_HeapData;
20+
21+
private HeapType m_HeapType;
22+
23+
this( T[] p_HeapInitObjs, HeapType p_HeapType ) @safe
24+
{
25+
m_HeapData = p_HeapInitObjs;
26+
m_HeapType = p_HeapType;
27+
28+
for(int t_Idx=((cast(int) m_HeapData.length) / 2) - 1; t_Idx >= 0; t_Idx--)
29+
{
30+
_HeapSiftDown( t_Idx );
31+
}
32+
}
33+
34+
this( HeapType p_HeapType ) @safe @nogc
35+
{
36+
m_HeapType = p_HeapType;
37+
}
38+
39+
void HeapPush( T p_HeapObject ) @safe
40+
{
41+
m_HeapData ~= p_HeapObject;
42+
43+
_HeapSiftUp( m_HeapData.length - 1 );
44+
}
45+
46+
T HeapPop() @safe
47+
{
48+
T t_HeapFront = m_HeapData[0];
49+
50+
m_HeapData[0] = m_HeapData[$-1];
51+
m_HeapData = m_HeapData[0..$-1];
52+
53+
_HeapSiftDown( 0 );
54+
55+
return t_HeapFront;
56+
}
57+
58+
private void _HeapSiftUp( size_t p_Idx ) @safe {
59+
while( p_Idx > 0 )
60+
{
61+
size_t t_HeapParent = (p_Idx - 1) / 2;
62+
63+
if( _Compare( m_HeapData[p_Idx], m_HeapData[t_HeapParent] ) )
64+
{
65+
_Swap( m_HeapData[p_Idx], m_HeapData[t_HeapParent] );
66+
67+
p_Idx = t_HeapParent;
68+
} else { break; }
69+
}
70+
}
71+
72+
private void _HeapSiftDown( size_t p_Idx ) @safe {
73+
while( 1 )
74+
{
75+
size_t t_HeapLeft = 2 * p_Idx + 1;
76+
size_t t_HeapRight = 2 * p_Idx + 2;
77+
78+
size_t t_HeapTarget = p_Idx;
79+
80+
if( t_HeapLeft < m_HeapData.length && _Compare( m_HeapData[t_HeapLeft], m_HeapData[t_HeapTarget] ) )
81+
{
82+
t_HeapTarget = t_HeapLeft;
83+
}
84+
85+
if( t_HeapRight < m_HeapData.length && _Compare( m_HeapData[t_HeapRight], m_HeapData[t_HeapTarget] ) )
86+
{
87+
t_HeapTarget = t_HeapRight;
88+
}
89+
90+
if( t_HeapTarget == p_Idx )
91+
{
92+
break;
93+
}
94+
95+
_Swap( m_HeapData[p_Idx], m_HeapData[t_HeapTarget] );
96+
97+
p_Idx = t_HeapTarget;
98+
}
99+
}
100+
101+
private void _Swap( ref T p_Object0, ref T p_Object1 ) @safe @nogc
102+
{
103+
T t_Tmp0 = p_Object0;
104+
105+
p_Object0 = p_Object1;
106+
p_Object1 = t_Tmp0;
107+
}
108+
109+
private bool _Compare( T p_Object0, T p_Object1 ) @safe @nogc
110+
{
111+
return (m_HeapType == HeapType.MIN_HEAP) ? p_Object0 < p_Object1 : p_Object0 > p_Object1;
112+
}
113+
114+
T HeapFront() const @property @safe @nogc
115+
{
116+
return m_HeapData[0];
117+
}
118+
119+
bool Empty() const @property @safe @nogc
120+
{
121+
return m_HeapData.length == 0;
122+
}
123+
124+
size_t Size() const @property @safe @nogc
125+
{
126+
return m_HeapData.length;
127+
}
128+
129+
int opApply( int delegate(ref T) HEAP_DELEGATE )
130+
{
131+
foreach( ref t_HeapObject; m_HeapData )
132+
{
133+
auto t_DelegateOut = HEAP_DELEGATE( t_HeapObject );
134+
135+
if( t_DelegateOut ) {
136+
return t_DelegateOut;
137+
}
138+
}
139+
140+
return 0;
141+
}
142+
}

0 commit comments

Comments
 (0)