yapf_base.hpp

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

Generated on Sun Jan 23 01:49:06 2011 for OpenTTD by  doxygen 1.6.1