# yapf_base.hpp

Go to the documentation of this file.

    00001 /* $Id: yapf_base.hpp 18372 2009-12-02 10:13:49Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #ifndef  YAPF_BASE_HPP
    00013 #define  YAPF_BASE_HPP
    00014
    00015 #include "../../debug.h"
    00016 #include "../../settings_type.h"
    00017
    00018 extern int _total_pf_time_us;
    00019
    00049 template <class Types>
    00050 class CYapfBaseT {
    00051 public:
    00052   typedef typename Types::Tpf Tpf;
    00053   typedef typename Types::TrackFollower TrackFollower;
    00054   typedef typename Types::NodeList NodeList;
    00055   typedef typename Types::VehicleType VehicleType;
    00056   typedef typename NodeList::Titem Node;
    00057   typedef typename Node::Key Key;
    00058
    00059
    00060   NodeList             m_nodes;
    00061 protected:
    00062   Node                *m_pBestDestNode;
    00063   Node                *m_pBestIntermediateNode;
    00064   const YAPFSettings  *m_settings;
    00065   int                  m_max_search_nodes;
    00066   const VehicleType   *m_veh;
    00067
    00068   int                  m_stats_cost_calcs;
    00069   int                  m_stats_cache_hits;
    00070
    00071 public:
    00072   CPerformanceTimer    m_perf_cost;
    00073   CPerformanceTimer    m_perf_slope_cost;
    00074   CPerformanceTimer    m_perf_ts_cost;
    00075   CPerformanceTimer    m_perf_other_cost;
    00076
    00077 public:
    00078   int                  m_num_steps;
    00079
    00080 public:
    00082   FORCEINLINE CYapfBaseT()
    00083     : m_pBestDestNode(NULL)
    00084     , m_pBestIntermediateNode(NULL)
    00085     , m_settings(&_settings_game.pf.yapf)
    00086     , m_max_search_nodes(PfGetSettings().max_search_nodes)
    00087     , m_veh(NULL)
    00088     , m_stats_cost_calcs(0)
    00089     , m_stats_cache_hits(0)
    00090     , m_num_steps(0)
    00091   {
    00092   }
    00093
    00095   ~CYapfBaseT() {}
    00096
    00097 protected:
    00099   FORCEINLINE Tpf& Yapf()
    00100   {
    00101     return *static_cast<Tpf*>(this);
    00102   }
    00103
    00104 public:
    00106   FORCEINLINE const YAPFSettings& PfGetSettings() const
    00107   {
    00108     return *m_settings;
    00109   }
    00110
    00118   inline bool FindPath(const VehicleType *v)
    00119   {
    00120     m_veh = v;
    00121
    00122 #ifndef NO_DEBUG_MESSAGES
    00123     CPerformanceTimer perf;
    00124     perf.Start();
    00125 #endif /* !NO_DEBUG_MESSAGES */
    00126
    00127     Yapf().PfSetStartupNodes();
    00128
    00129     while (true) {
    00130       m_num_steps++;
    00131       Node *n = m_nodes.GetBestOpenNode();
    00132       if (n == NULL) {
    00133         break;
    00134       }
    00135
    00136       /* if the best open node was worse than the best path found, we can finish */
    00137       if (m_pBestDestNode != NULL && m_pBestDestNode->GetCost() < n->GetCostEstimate()) {
    00138         break;
    00139       }
    00140
    00141       Yapf().PfFollowNode(*n);
    00142       if (m_max_search_nodes == 0 || m_nodes.ClosedCount() < m_max_search_nodes) {
    00143         m_nodes.PopOpenNode(n->GetKey());
    00144         m_nodes.InsertClosedNode(*n);
    00145       } else {
    00146         m_pBestDestNode = m_pBestIntermediateNode;
    00147         break;
    00148       }
    00149     }
    00150
    00151     bool bDestFound = (m_pBestDestNode != NULL) && (m_pBestDestNode != m_pBestIntermediateNode);
    00152
    00153 #ifndef NO_DEBUG_MESSAGES
    00154     perf.Stop();
    00155     if (_debug_yapf_level >= 2) {
    00156       int t = perf.Get(1000000);
    00157       _total_pf_time_us += t;
    00158
    00159       if (_debug_yapf_level >= 3) {
    00160         UnitID veh_idx = (m_veh != NULL) ? m_veh->unitnumber : 0;
    00161         char ttc = Yapf().TransportTypeChar();
    00162         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);
    00163         int cost = bDestFound ? m_pBestDestNode->m_cost : -1;
    00164         int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1;
    00165
    00166         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) -- ",
    00167           ttc, bDestFound ? '-' : '!', veh_idx, t, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(),
    00168           cache_hit_ratio, cost, dist, m_perf_cost.Get(1000000), m_perf_slope_cost.Get(1000000),
    00169           m_perf_ts_cost.Get(1000000), m_perf_other_cost.Get(1000000)
    00170         );
    00171       }
    00172     }
    00173 #endif /* !NO_DEBUG_MESSAGES */
    00174     return bDestFound;
    00175   }
    00176
    00180   FORCEINLINE Node *GetBestNode()
    00181   {
    00182     return (m_pBestDestNode != NULL) ? m_pBestDestNode : m_pBestIntermediateNode;
    00183   }
    00184
    00188   FORCEINLINE Node& CreateNewNode()
    00189   {
    00190     Node& node = *m_nodes.CreateNewNode();
    00191     return node;
    00192   }
    00193
    00195   FORCEINLINE void AddStartupNode(Node& n)
    00196   {
    00197     Yapf().PfNodeCacheFetch(n);
    00198     /* insert the new node only if it is not there */
    00199     if (m_nodes.FindOpenNode(n.m_key) == NULL) {
    00200       m_nodes.InsertOpenNode(n);
    00201     } else {
    00202       /* if we are here, it means that node is already there - how it is possible?
    00203        *   probably the train is in the position that both its ends point to the same tile/exit-dir
    00204        *   very unlikely, but it happened */
    00205     }
    00206   }
    00207
    00209   FORCEINLINE void AddMultipleNodes(Node *parent, const TrackFollower &tf)
    00210   {
    00211     bool is_choice = (KillFirstBit(tf.m_new_td_bits) != TRACKDIR_BIT_NONE);
    00212     for (TrackdirBits rtds = tf.m_new_td_bits; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
    00213       Trackdir td = (Trackdir)FindFirstBit2x64(rtds);
    00214       Node& n = Yapf().CreateNewNode();
    00215       n.Set(parent, tf.m_new_tile, td, is_choice);
    00216       Yapf().AddNewNode(n, tf);
    00217     }
    00218   }
    00219
    00222   void AddNewNode(Node &n, const TrackFollower &tf)
    00223   {
    00224     /* evaluate the node */
    00225     bool bCached = Yapf().PfNodeCacheFetch(n);
    00226     if (!bCached) {
    00227       m_stats_cost_calcs++;
    00228     } else {
    00229       m_stats_cache_hits++;
    00230     }
    00231
    00232     bool bValid = Yapf().PfCalcCost(n, &tf);
    00233
    00234     if (bCached) {
    00235       Yapf().PfNodeCacheFlush(n);
    00236     }
    00237
    00238     if (bValid) bValid = Yapf().PfCalcEstimate(n);
    00239
    00240     /* have the cost or estimate callbacks marked this node as invalid? */
    00241     if (!bValid) return;
    00242
    00243     /* detect the destination */
    00244     bool bDestination = Yapf().PfDetectDestination(n);
    00245     if (bDestination) {
    00246       if (m_pBestDestNode == NULL || n < *m_pBestDestNode) {
    00247         m_pBestDestNode = &n;
    00248       }
    00249       m_nodes.FoundBestNode(n);
    00250       return;
    00251     }
    00252
    00253     if (m_max_search_nodes > 0 && (m_pBestIntermediateNode == NULL || (m_pBestIntermediateNode->GetCostEstimate() - m_pBestIntermediateNode->GetCost()) > (n.GetCostEstimate() - n.GetCost()))) {
    00254       m_pBestIntermediateNode = &n;
    00255     }
    00256
    00257     /* check new node against open list */
    00258     Node *openNode = m_nodes.FindOpenNode(n.GetKey());
    00259     if (openNode != NULL) {
    00260       /* another node exists with the same key in the open list
    00261        * is it better than new one? */
    00262       if (n.GetCostEstimate() < openNode->GetCostEstimate()) {
    00263         /* update the old node by value from new one */
    00264         m_nodes.PopOpenNode(n.GetKey());
    00265         *openNode = n;
    00266         /* add the updated old node back to open list */
    00267         m_nodes.InsertOpenNode(*openNode);
    00268       }
    00269       return;
    00270     }
    00271
    00272     /* check new node against closed list */
    00273     Node *closedNode = m_nodes.FindClosedNode(n.GetKey());
    00274     if (closedNode != NULL) {
    00275       /* another node exists with the same key in the closed list
    00276        * is it better than new one? */
    00277       int node_est = n.GetCostEstimate();
    00278       int closed_est = closedNode->GetCostEstimate();
    00279       if (node_est < closed_est) {
    00280         /* If this assert occurs, you have probably problem in
    00281          * your Tderived::PfCalcCost() or Tderived::PfCalcEstimate().
    00282          * The problem could be:
    00283          *  - PfCalcEstimate() gives too large numbers
    00284          *  - PfCalcCost() gives too small numbers
    00285          *  - You have used negative cost penalty in some cases (cost bonus) */
    00286         NOT_REACHED();
    00287       }
    00288       return;
    00289     }
    00290     /* the new node is really new
    00291      * add it to the open list */
    00292     m_nodes.InsertOpenNode(n);
    00293   }
    00294
    00295   const VehicleType * GetVehicle() const
    00296   {
    00297     return m_veh;
    00298   }
    00299
    00300   void DumpBase(DumpTarget &dmp) const
    00301   {
    00302     dmp.WriteStructT("m_nodes", &m_nodes);
    00303     dmp.WriteLine("m_num_steps = %d", m_num_steps);
    00304   }
    00305
    00306   /* methods that should be implemented at derived class Types::Tpf (derived from CYapfBaseT) */
    00307
    00308 #if 0
    00309
    00310   FORCEINLINE void PfSetStartupNodes()
    00311   {
    00312     /* example: */
    00313     Node& n1 = *base::m_nodes.CreateNewNode();
    00314     .
    00315     . // setup node members here
    00316     .
    00317     base::m_nodes.InsertOpenNode(n1);
    00318   }
    00319
    00321   FORCEINLINE void PfFollowNode(Node& org)
    00322   {
    00323     for (each follower of node org) {
    00324       Node& n = *base::m_nodes.CreateNewNode();
    00325       .
    00326       . // setup node members here
    00327       .
    00328       n.m_parent   = &org; // set node's parent to allow back tracking
    00329       AddNewNode(n);
    00330     }
    00331   }
    00332
    00334   FORCEINLINE bool PfCalcCost(Node& n)
    00335   {
    00336     /* evaluate last step cost */
    00337     int cost = ...;
    00338     /* set the node cost as sum of parent's cost and last step cost */
    00339     n.m_cost = n.m_parent->m_cost + cost;
    00340     return true; // true if node is valid follower (i.e. no obstacle was found)
    00341   }
    00342
    00344   FORCEINLINE bool PfCalcEstimate(Node& n)
    00345   {
    00346     /* evaluate the distance to our destination */
    00347     int distance = ...;
    00348     /* set estimate as sum of cost from origin + distance to the target */
    00349     n.m_estimate = n.m_cost + distance;
    00350     return true; // true if node is valid (i.e. not too far away :)
    00351   }
    00352
    00354   FORCEINLINE bool PfDetectDestination(Node& n)
    00355   {
    00356     bool bDest = (n.m_key.m_x == m_x2) && (n.m_key.m_y == m_y2);
    00357     return bDest;
    00358   }
    00359 #endif
    00360 };
    00361
    00362 #endif /* YAPF_BASE_HPP */