yapf_base.hpp

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

Generated on Mon Feb 16 23:12:13 2009 for openttd by  doxygen 1.5.6