OpenTTD: src/pathfinder/yapf/yapf_base.hpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

yapf_base.hpp
Go to the documentation of this file.
1 /* $Id: yapf_base.hpp 25609 2013-07-14 09:21:46Z rubidium $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
9
12 #ifndef YAPF_BASE_HPP
13 #define YAPF_BASE_HPP
14
15 #include "../../debug.h"
16 #include "../../settings_type.h"
17
18 extern int _total_pf_time_us;
19
50 template <class Types>
51 class CYapfBaseT {
52 public:
53 typedef typename Types::Tpf Tpf;
54 typedef typename Types::TrackFollower TrackFollower;
55 typedef typename Types::NodeList NodeList;
56 typedef typename Types::VehicleType VehicleType;
57 typedef typename NodeList::Titem Node;
58 typedef typename Node::Key Key;
59
60
61 NodeList m_nodes;
62 protected:
63 Node *m_pBestDestNode;
64 Node *m_pBestIntermediateNode;
65 const YAPFSettings *m_settings;
66 int m_max_search_nodes;
67 const VehicleType *m_veh;
68
69 int m_stats_cost_calcs;
70 int m_stats_cache_hits;
71
72 public:
73 CPerformanceTimer m_perf_cost;
74 CPerformanceTimer m_perf_slope_cost;
75 CPerformanceTimer m_perf_ts_cost;
76 CPerformanceTimer m_perf_other_cost;
77
78 public:
79 int m_num_steps;
80
81 public:
83 inline CYapfBaseT()
84 : m_pBestDestNode(NULL)
85 , m_pBestIntermediateNode(NULL)
86 , m_settings(&_settings_game.pf.yapf)
87 , m_max_search_nodes(PfGetSettings().max_search_nodes)
88 , m_veh(NULL)
89 , m_stats_cost_calcs(0)
90 , m_stats_cache_hits(0)
91 , m_num_steps(0)
92 {
93 }
94
96 ~CYapfBaseT() {}
97
98 protected:
100 inline Tpf& Yapf()
101 {
102 return *static_cast<Tpf*>(this);
103 }
104
105 public:
107 inline const YAPFSettings& PfGetSettings() const
108 {
109 return *m_settings;
110 }
111
121 inline bool FindPath(const VehicleType *v)
122 {
123 m_veh = v;
124
125 #ifndef NO_DEBUG_MESSAGES
126 CPerformanceTimer perf;
127 perf.Start();
128 #endif /* !NO_DEBUG_MESSAGES */
129
130 Yapf().PfSetStartupNodes();
131 bool bDestFound = true;
132
133 for (;;) {
134 m_num_steps++;
135 Node *n = m_nodes.GetBestOpenNode();
136 if (n == NULL) {
137 break;
138 }
139
140 /* if the best open node was worse than the best path found, we can finish */
141 if (m_pBestDestNode != NULL && m_pBestDestNode->GetCost() < n->GetCostEstimate()) {
142 break;
143 }
144
145 Yapf().PfFollowNode(*n);
146 if (m_max_search_nodes == 0 || m_nodes.ClosedCount() < m_max_search_nodes) {
147 m_nodes.PopOpenNode(n->GetKey());
148 m_nodes.InsertClosedNode(*n);
149 } else {
150 bDestFound = false;
151 break;
152 }
153 }
154
155 bDestFound &= (m_pBestDestNode != NULL);
156
157 #ifndef NO_DEBUG_MESSAGES
158 perf.Stop();
159 if (_debug_yapf_level >= 2) {
160 int t = perf.Get(1000000);
161 _total_pf_time_us += t;
162
163 if (_debug_yapf_level >= 3) {
164 UnitID veh_idx = (m_veh != NULL) ? m_veh->unitnumber : 0;
165 char ttc = Yapf().TransportTypeChar();
166 float cache_hit_ratio = (m_stats_cache_hits == 0) ? 0.0f : ((float)m_stats_cache_hits / (float)(m_stats_cache_hits + m_stats_cost_calcs) * 100.0f);
167 int cost = bDestFound ? m_pBestDestNode->m_cost : -1;
168 int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1;
169
170 DEBUG(yapf, 3, "[YAPF%c]%c%4d- %d us - %d rounds - %d open - %d closed - CHR %4.1f%% - C %d D %d - c%d(sc%d, ts%d, o%d) -- ",
171 ttc, bDestFound ? '-' : '!', veh_idx, t, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(),
172 cache_hit_ratio, cost, dist, m_perf_cost.Get(1000000), m_perf_slope_cost.Get(1000000),
173 m_perf_ts_cost.Get(1000000), m_perf_other_cost.Get(1000000)
174 );
175 }
176 }
177 #endif /* !NO_DEBUG_MESSAGES */
178 return bDestFound;
179 }
180
185 inline Node *GetBestNode()
186 {
187 return (m_pBestDestNode != NULL) ? m_pBestDestNode : m_pBestIntermediateNode;
188 }
189
194 inline Node& CreateNewNode()
195 {
196 Node& node = *m_nodes.CreateNewNode();
197 return node;
198 }
199
201 inline void AddStartupNode(Node& n)
202 {
203 Yapf().PfNodeCacheFetch(n);
204 /* insert the new node only if it is not there */
205 if (m_nodes.FindOpenNode(n.m_key) == NULL) {
206 m_nodes.InsertOpenNode(n);
207 } else {
208 /* if we are here, it means that node is already there - how it is possible?
209 * probably the train is in the position that both its ends point to the same tile/exit-dir
210 * very unlikely, but it happened */
211 }
212 }
213
215 inline void AddMultipleNodes(Node *parent, const TrackFollower &tf)
216 {
217 bool is_choice = (KillFirstBit(tf.m_new_td_bits) != TRACKDIR_BIT_NONE);
218 for (TrackdirBits rtds = tf.m_new_td_bits; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
219 Trackdir td = (Trackdir)FindFirstBit2x64(rtds);
220 Node& n = Yapf().CreateNewNode();
221 n.Set(parent, tf.m_new_tile, td, is_choice);
222 Yapf().AddNewNode(n, tf);
223 }
224 }
225
234 void PruneIntermediateNodeBranch()
235 {
236 while (Yapf().m_pBestIntermediateNode != NULL && (Yapf().m_pBestIntermediateNode->m_segment->m_end_segment_reason & ESRB_CHOICE_FOLLOWS) == 0) {
237 Yapf().m_pBestIntermediateNode = Yapf().m_pBestIntermediateNode->m_parent;
238 }
239 }
240
245 void AddNewNode(Node &n, const TrackFollower &tf)
246 {
247 /* evaluate the node */
248 bool bCached = Yapf().PfNodeCacheFetch(n);
249 if (!bCached) {
250 m_stats_cost_calcs++;
251 } else {
252 m_stats_cache_hits++;
253 }
254
255 bool bValid = Yapf().PfCalcCost(n, &tf);
256
257 if (bCached) {
258 Yapf().PfNodeCacheFlush(n);
259 }
260
261 if (bValid) bValid = Yapf().PfCalcEstimate(n);
262
263 /* have the cost or estimate callbacks marked this node as invalid? */
264 if (!bValid) return;
265
266 /* detect the destination */
267 bool bDestination = Yapf().PfDetectDestination(n);
268 if (bDestination) {
269 if (m_pBestDestNode == NULL || n < *m_pBestDestNode) {
270 m_pBestDestNode = &n;
271 }
272 m_nodes.FoundBestNode(n);
273 return;
274 }
275
276 if (m_max_search_nodes > 0 && (m_pBestIntermediateNode == NULL || (m_pBestIntermediateNode->GetCostEstimate() - m_pBestIntermediateNode->GetCost()) > (n.GetCostEstimate() - n.GetCost()))) {
277 m_pBestIntermediateNode = &n;
278 }
279
280 /* check new node against open list */
281 Node *openNode = m_nodes.FindOpenNode(n.GetKey());
282 if (openNode != NULL) {
283 /* another node exists with the same key in the open list
284 * is it better than new one? */
285 if (n.GetCostEstimate() < openNode->GetCostEstimate()) {
286 /* update the old node by value from new one */
287 m_nodes.PopOpenNode(n.GetKey());
288 *openNode = n;
289 /* add the updated old node back to open list */
290 m_nodes.InsertOpenNode(*openNode);
291 }
292 return;
293 }
294
295 /* check new node against closed list */
296 Node *closedNode = m_nodes.FindClosedNode(n.GetKey());
297 if (closedNode != NULL) {
298 /* another node exists with the same key in the closed list
299 * is it better than new one? */
300 int node_est = n.GetCostEstimate();
301 int closed_est = closedNode->GetCostEstimate();
302 if (node_est < closed_est) {
303 /* If this assert occurs, you have probably problem in
304 * your Tderived::PfCalcCost() or Tderived::PfCalcEstimate().
305 * The problem could be:
306 * - PfCalcEstimate() gives too large numbers
307 * - PfCalcCost() gives too small numbers
308 * - You have used negative cost penalty in some cases (cost bonus) */
309 NOT_REACHED();
310 }
311 return;
312 }
313 /* the new node is really new
314 * add it to the open list */
315 m_nodes.InsertOpenNode(n);
316 }
317
318 const VehicleType * GetVehicle() const
319 {
320 return m_veh;
321 }
322
323 void DumpBase(DumpTarget &dmp) const
324 {
325 dmp.WriteStructT("m_nodes", &m_nodes);
326 dmp.WriteLine("m_num_steps = %d", m_num_steps);
327 }
328
329 /* methods that should be implemented at derived class Types::Tpf (derived from CYapfBaseT) */
330
331 #if 0
332
333 inline void PfSetStartupNodes()
334 {
335 /* example: */
336 Node& n1 = *base::m_nodes.CreateNewNode();
337 .
338 . // setup node members here
339 .
340 base::m_nodes.InsertOpenNode(n1);
341 }
342
344 inline void PfFollowNode(Node& org)
345 {
346 for (each follower of node org) {
347 Node& n = *base::m_nodes.CreateNewNode();
348 .
349 . // setup node members here
350 .
351 n.m_parent = &org; // set node's parent to allow back tracking
352 AddNewNode(n);
353 }
354 }
355
357 inline bool PfCalcCost(Node& n)
358 {
359 /* evaluate last step cost */
360 int cost = ...;
361 /* set the node cost as sum of parent's cost and last step cost */
362 n.m_cost = n.m_parent->m_cost + cost;
363 return true; // true if node is valid follower (i.e. no obstacle was found)
364 }
365
367 inline bool PfCalcEstimate(Node& n)
368 {
369 /* evaluate the distance to our destination */
370 int distance = ...;
371 /* set estimate as sum of cost from origin + distance to the target */
372 n.m_estimate = n.m_cost + distance;
373 return true; // true if node is valid (i.e. not too far away :)
374 }
375
377 inline bool PfDetectDestination(Node& n)
378 {
379 bool bDest = (n.m_key.m_x == m_x2) && (n.m_key.m_y == m_y2);
380 return bDest;
381 }
382 #endif
383 };
384
385 #endif /* YAPF_BASE_HPP */