# yapf_base.hpp

Go to the documentation of this file.

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