OpenTTD
yapf_base.hpp
Go to the documentation of this file.
1 /* $Id: yapf_base.hpp 27362 2015-08-08 10:06:24Z alberth $ */
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 
62 protected:
67  const VehicleType *m_veh;
68 
71 
72 public:
77 
78 public:
80 
81 public:
83  inline CYapfBaseT()
84  : m_pBestDestNode(NULL)
86  , m_settings(&_settings_game.pf.yapf)
87  , m_max_search_nodes(PfGetSettings().max_search_nodes)
88  , m_veh(NULL)
91  , m_num_steps(0)
92  {
93  }
94 
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  {
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 
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) {
251  } else {
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()))) {
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 */