Plasma Engine  2.0
Loading...
Searching...
No Matches
GraphSearch_inl.h
1#pragma once
2
3template <typename PathStateType>
5{
6 m_PathStates.Clear();
7 m_StateQueue.Clear();
8}
9
10template <typename PathStateType>
11plInt64 plPathSearch<PathStateType>::FindBestNodeToExpand(PathStateType*& out_pPathState)
12{
13 float fLowestEstimation = plMath::Infinity<float>();
14 out_pPathState = nullptr;
15 plUInt32 iBestInQueue = 0;
16
17 for (plUInt32 i = 0; i < m_StateQueue.GetCount(); ++i)
18 {
19 const plInt64 iNodeIndex = m_StateQueue[i];
20
21 PathStateType* pState = &m_PathStates[iNodeIndex];
22
23 if (pState->m_fEstimatedCostToTarget < fLowestEstimation)
24 {
25 fLowestEstimation = pState->m_fEstimatedCostToTarget;
26 out_pPathState = pState;
27 iBestInQueue = i;
28 }
29 }
30
31 PL_ASSERT_DEV(out_pPathState != nullptr, "Implementation Error");
32
33 const plInt64 iBestNodeIndex = m_StateQueue[iBestInQueue];
34 m_StateQueue.RemoveAtAndSwap(iBestInQueue);
35
36 return iBestNodeIndex;
37}
38
39template <typename PathStateType>
41{
42 out_Path.Clear();
43
44 while (true)
45 {
46 const PathStateType* pCurState = &m_PathStates[iEndNodeIndex];
47
48 PathResultData r;
49 r.m_iNodeIndex = iEndNodeIndex;
50 r.m_pPathState = pCurState;
51
52 out_Path.PushFront(r);
53
54 if (iEndNodeIndex == pCurState->m_iReachedThroughNode)
55 return;
56
57 iEndNodeIndex = pCurState->m_iReachedThroughNode;
58 }
59}
60
61template <typename PathStateType>
62void plPathSearch<PathStateType>::AddPathNode(plInt64 iNodeIndex, const PathStateType& NewState)
63{
64 PL_ASSERT_DEV(NewState.m_fCostToNode > m_CurState.m_fCostToNode,
65 "The costs must grow from one node to the next.\nStart Node Costs: {0}\nAdjacent Node Costs: {1}", plArgF(m_CurState.m_fCostToNode, 2),
66 plArgF(NewState.m_fCostToNode, 2));
67 // PL_ASSERT_DEV(NewState.m_fEstimatedCostToTarget >= m_CurState.m_fEstimatedCostToTarget, "The estimated path costs cannot go down, the
68 // heuristic must be 'optimistic' regarding to the real costs.\nEstimated Costs from Current: {0}\nEstimated Costs from Adjacent: {1}",
69 // plArgF(m_pCurPathState->m_fEstimatedCostToTarget, 2), plArgF(NewState.m_fEstimatedCostToTarget, 2));
70 PL_ASSERT_DEV(NewState.m_fEstimatedCostToTarget >= NewState.m_fCostToNode, "Unrealistic expectations will get you nowhere.");
71
72 PathStateType* pExistingState;
73
74 if (m_PathStates.TryGetValue(iNodeIndex, pExistingState))
75 {
76 // state already exists in the hash table, and has a lower cost -> ignore the new state
77 if (pExistingState->m_fCostToNode <= NewState.m_fCostToNode)
78 return;
79
80 // incoming state is better than the existing state -> update existing state
81 *pExistingState = NewState;
82 pExistingState->m_iReachedThroughNode = m_iCurNodeIndex;
83 return;
84 }
85
86 // the state has not been reached before -> insert it
87 pExistingState = &m_PathStates[iNodeIndex];
88
89 *pExistingState = NewState;
90 pExistingState->m_iReachedThroughNode = m_iCurNodeIndex;
91
92 // put it into the queue of states that still need to be expanded
93 m_StateQueue.PushBack(iNodeIndex);
94}
95
96template <typename PathStateType>
97plResult plPathSearch<PathStateType>::FindPath(plInt64 iStartNodeIndex, const PathStateType& StartState, plInt64 iTargetNodeIndex,
98 plDeque<PathResultData>& out_Path, float fMaxPathCost /* = Infinity */)
99{
100 PL_ASSERT_DEV(m_pStateGenerator != nullptr, "No Path State Generator is set.");
101
102 ClearPathStates();
103
104 if (iStartNodeIndex == iTargetNodeIndex)
105 {
106 m_PathStates.Reserve(1);
107
108 m_PathStates[iTargetNodeIndex] = StartState;
109
111 r.m_iNodeIndex = iTargetNodeIndex;
112 r.m_pPathState = &m_PathStates[iTargetNodeIndex];
113
114 out_Path.Clear();
115 out_Path.PushBack(r);
116
117 return PL_SUCCESS;
118 }
119
120 m_PathStates.Reserve(10000);
121
122 PathStateType& FirstState = m_PathStates[iStartNodeIndex];
123
124 m_pStateGenerator->StartSearch(iStartNodeIndex, &FirstState, iTargetNodeIndex);
125
126 // make sure the first state references itself, as that is a termination criterion
127 FirstState = StartState;
128 FirstState.m_iReachedThroughNode = iStartNodeIndex;
129
130 // put the start state into the to-be-expanded queue
131 m_StateQueue.PushBack(iStartNodeIndex);
132
133 // while the queue is not empty, expand the next node and see where that gets us
134 while (!m_StateQueue.IsEmpty())
135 {
136 PathStateType* pCurState;
137 m_iCurNodeIndex = FindBestNodeToExpand(pCurState);
138
139 // we have reached the target node, generate the final path result
140 if (m_iCurNodeIndex == iTargetNodeIndex)
141 {
142 FillOutPathResult(m_iCurNodeIndex, out_Path);
143 m_pStateGenerator->SearchFinished(PL_SUCCESS);
144 return PL_SUCCESS;
145 }
146
147 // The heuristic may overestimate how much it takes to reach the destination
148 // thus even though the heuristic tells us we may not be able to make it, we cannot rely on that, but need to look at
149 // the actual costs
150 if (pCurState->m_fCostToNode >= fMaxPathCost)
151 {
152 m_pStateGenerator->SearchFinished(PL_FAILURE);
153 return PL_FAILURE;
154 }
155
156 m_CurState = *pCurState;
157
158 // let the generate append all the nodes that we can reach from here
159 m_pStateGenerator->GenerateAdjacentStates(m_iCurNodeIndex, m_CurState, this);
160 }
161
162 m_pStateGenerator->SearchFinished(PL_FAILURE);
163 return PL_FAILURE;
164}
165
166
167template <typename PathStateType>
169 plInt64 iStartNodeIndex, const PathStateType& StartState, IsSearchedObjectCallback Callback, plDeque<PathResultData>& out_Path, float fMaxPathCost)
170{
171 PL_ASSERT_DEV(m_pStateGenerator != nullptr, "No Path State Generator is set.");
172
173 ClearPathStates();
174
175 m_PathStates.Reserve(10000);
176
177 PathStateType& FirstState = m_PathStates[iStartNodeIndex];
178
179 m_pStateGenerator->StartSearchForClosest(iStartNodeIndex, &FirstState);
180
181 // make sure the first state references itself, as that is a termination criterion
182 FirstState = StartState;
183 FirstState.m_iReachedThroughNode = iStartNodeIndex;
184
185 // put the start state into the to-be-expanded queue
186 m_StateQueue.PushBack(iStartNodeIndex);
187
188 // while the queue is not empty, expand the next node and see where that gets us
189 while (!m_StateQueue.IsEmpty())
190 {
191 PathStateType* pCurState;
192 m_iCurNodeIndex = FindBestNodeToExpand(pCurState);
193
194 // we have reached the target node, generate the final path result
195 if (Callback(m_iCurNodeIndex, *pCurState))
196 {
197 FillOutPathResult(m_iCurNodeIndex, out_Path);
198 m_pStateGenerator->SearchFinished(PL_SUCCESS);
199 return PL_SUCCESS;
200 }
201
202 // The heuristic may overestimate how much it takes to reach the destination
203 // thus even though the heuristic tells us we may not be able to make it, we cannot rely on that, but need to look at
204 // the actual costs
205 if (pCurState->m_fCostToNode >= fMaxPathCost)
206 {
207 m_pStateGenerator->SearchFinished(PL_FAILURE);
208 return PL_FAILURE;
209 }
210
211 m_CurState = *pCurState;
212
213 // let the generate append all the nodes that we can reach from here
214 m_pStateGenerator->GenerateAdjacentStates(m_iCurNodeIndex, m_CurState, this);
215 }
216
217 m_pStateGenerator->SearchFinished(PL_FAILURE);
218 return PL_FAILURE;
219}
void Clear()
Destructs all elements and sets the count to zero. Does not deallocate any data.
Definition Deque_inl.h:130
void PushFront(const T &element)
Adds one element to the front of the deque.
Definition Deque_inl.h:542
void PushBack()
Adds one default constructed element to the back of the deque.
Definition Deque_inl.h:489
Definition Deque.h:270
Implements a directed breadth-first search through a graph (A*).
Definition GraphSearch.h:16
plResult FindPath(plInt64 iStartNodeIndex, const PathStateType &StartState, plInt64 iTargetNodeIndex, plDeque< PathResultData > &out_Path, float fMaxPathCost=plMath::Infinity< float >())
Searches for a path that starts at the graph node iStartNodeIndex with the start state StartState and...
Definition GraphSearch_inl.h:97
void AddPathNode(plInt64 iNodeIndex, const PathStateType &NewState)
Needs to be called by the used plPathStateGenerator to add nodes to evaluate.
Definition GraphSearch_inl.h:62
bool(*)(plInt64 iStartNodeIndex, const PathStateType &StartState) IsSearchedObjectCallback
Used by FindClosest() to query whether the currently visited node fulfills the termination criteria.
Definition GraphSearch.h:19
plResult FindClosest(plInt64 iStartNodeIndex, const PathStateType &StartState, IsSearchedObjectCallback Callback, plDeque< PathResultData > &out_Path, float fMaxPathCost=plMath::Infinity< float >())
Searches for a path that starts at the graph node iStartNodeIndex with the start state StartState and...
Definition GraphSearch_inl.h:168
constexpr TYPE Infinity()
Returns the value for Infinity as the template type. Returns zero, if the type does not support Infin...
Definition Constants_inl.h:110
Definition FormatStringArgs.h:48
FindPath() and FindClosest() return an array of these objects as the path result.
Definition GraphSearch.h:23
plInt64 m_iNodeIndex
The index of the node that was visited.
Definition GraphSearch.h:27
const PathStateType * m_pPathState
Pointer to the path state that was active at that step along the path.
Definition GraphSearch.h:30
Default enum for returning failure or success, instead of using a bool.
Definition Types.h:54