yapf_road.cpp

Go to the documentation of this file.
00001 /* $Id: yapf_road.cpp 14311 2008-09-13 12:57:17Z rubidium $ */
00002 
00005 #include "../stdafx.h"
00006 
00007 #include "yapf.hpp"
00008 #include "yapf_node_road.hpp"
00009 
00010 
00011 template <class Types>
00012 class CYapfCostRoadT
00013 {
00014 public:
00015   typedef typename Types::Tpf Tpf; 
00016   typedef typename Types::TrackFollower TrackFollower; 
00017   typedef typename Types::NodeList::Titem Node; 
00018   typedef typename Node::Key Key;    
00019 
00020 protected:
00022   Tpf& Yapf() {return *static_cast<Tpf*>(this);}
00023 
00024   int SlopeCost(TileIndex tile, TileIndex next_tile, Trackdir trackdir)
00025   {
00026     // height of the center of the current tile
00027     int x1 = TileX(tile) * TILE_SIZE;
00028     int y1 = TileY(tile) * TILE_SIZE;
00029     int z1 = GetSlopeZ(x1 + TILE_SIZE / 2, y1 + TILE_SIZE / 2);
00030 
00031     // height of the center of the next tile
00032     int x2 = TileX(next_tile) * TILE_SIZE;
00033     int y2 = TileY(next_tile) * TILE_SIZE;
00034     int z2 = GetSlopeZ(x2 + TILE_SIZE / 2, y2 + TILE_SIZE / 2);
00035 
00036     if (z2 - z1 > 1) {
00037       /* Slope up */
00038       return Yapf().PfGetSettings().road_slope_penalty;
00039     }
00040     return 0;
00041   }
00042 
00044   FORCEINLINE int OneTileCost(TileIndex tile, Trackdir trackdir)
00045   {
00046     int cost = 0;
00047     // set base cost
00048     if (IsDiagonalTrackdir(trackdir)) {
00049       cost += YAPF_TILE_LENGTH;
00050       switch (GetTileType(tile)) {
00051         case MP_ROAD:
00052           /* Increase the cost for level crossings */
00053           if (IsLevelCrossing(tile))
00054             cost += Yapf().PfGetSettings().road_crossing_penalty;
00055           break;
00056         case MP_STATION:
00057           if (IsDriveThroughStopTile(tile))
00058             cost += Yapf().PfGetSettings().road_stop_penalty;
00059           break;
00060 
00061         default:
00062           break;
00063       }
00064     } else {
00065       // non-diagonal trackdir
00066       cost = YAPF_TILE_CORNER_LENGTH + Yapf().PfGetSettings().road_curve_penalty;
00067     }
00068     return cost;
00069   }
00070 
00071 public:
00075   FORCEINLINE bool PfCalcCost(Node& n, const TrackFollower *tf)
00076   {
00077     int segment_cost = 0;
00078     // start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment
00079     TileIndex tile = n.m_key.m_tile;
00080     Trackdir trackdir = n.m_key.m_td;
00081     while (true) {
00082       // base tile cost depending on distance between edges
00083       segment_cost += Yapf().OneTileCost(tile, trackdir);
00084 
00085       const Vehicle* v = Yapf().GetVehicle();
00086       // we have reached the vehicle's destination - segment should end here to avoid target skipping
00087       if (v->current_order.type == OT_GOTO_STATION && tile == v->dest_tile) break;
00088 
00089       // stop if we have just entered the depot
00090       if (IsTileDepotType(tile, TRANSPORT_ROAD) && trackdir == DiagdirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) {
00091         // next time we will reverse and leave the depot
00092         break;
00093       }
00094 
00095       // if there are no reachable trackdirs on new tile, we have end of road
00096       TrackFollower F(Yapf().GetVehicle());
00097       if (!F.Follow(tile, trackdir)) break;
00098 
00099       // if there are more trackdirs available & reachable, we are at the end of segment
00100       if (KillFirstBit(F.m_new_td_bits) != TRACKDIR_BIT_NONE) break;
00101 
00102       Trackdir new_td = (Trackdir)FindFirstBit2x64(F.m_new_td_bits);
00103 
00104       // stop if RV is on simple loop with no junctions
00105       if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) return false;
00106 
00107       // if we skipped some tunnel tiles, add their cost
00108       segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH;
00109 
00110       // add hilly terrain penalty
00111       segment_cost += Yapf().SlopeCost(tile, F.m_new_tile, trackdir);
00112 
00113       // add min/max speed penalties
00114       int min_speed = 0;
00115       int max_speed = F.GetSpeedLimit(&min_speed);
00116       if (max_speed < v->max_speed) segment_cost += 1 * (v->max_speed - max_speed);
00117       if (min_speed > v->max_speed) segment_cost += 10 * (min_speed - v->max_speed);
00118 
00119       // move to the next tile
00120       tile = F.m_new_tile;
00121       trackdir = new_td;
00122     };
00123 
00124     // save end of segment back to the node
00125     n.m_segment_last_tile = tile;
00126     n.m_segment_last_td = trackdir;
00127 
00128     // save also tile cost
00129     int parent_cost = (n.m_parent != NULL) ? n.m_parent->m_cost : 0;
00130     n.m_cost = parent_cost + segment_cost;
00131     return true;
00132   }
00133 };
00134 
00135 
00136 template <class Types>
00137 class CYapfDestinationAnyDepotRoadT
00138 {
00139 public:
00140   typedef typename Types::Tpf Tpf;                     
00141   typedef typename Types::TrackFollower TrackFollower;
00142   typedef typename Types::NodeList::Titem Node;        
00143   typedef typename Node::Key Key;                      
00144 
00146   Tpf& Yapf() {return *static_cast<Tpf*>(this);}
00147 
00149   FORCEINLINE bool PfDetectDestination(Node& n)
00150   {
00151     bool bDest = IsTileDepotType(n.m_segment_last_tile, TRANSPORT_ROAD);
00152     return bDest;
00153   }
00154 
00157   FORCEINLINE bool PfCalcEstimate(Node& n)
00158   {
00159     n.m_estimate = n.m_cost;
00160     return true;
00161   }
00162 };
00163 
00164 
00165 template <class Types>
00166 class CYapfDestinationTileRoadT
00167 {
00168 public:
00169   typedef typename Types::Tpf Tpf;                     
00170   typedef typename Types::TrackFollower TrackFollower;
00171   typedef typename Types::NodeList::Titem Node;        
00172   typedef typename Node::Key Key;                      
00173 
00174 protected:
00175   TileIndex    m_destTile;
00176   TrackdirBits m_destTrackdirs;
00177 
00178 public:
00179   void SetDestination(TileIndex tile, TrackdirBits trackdirs)
00180   {
00181     m_destTile = tile;
00182     m_destTrackdirs = trackdirs;
00183   }
00184 
00185 protected:
00187   Tpf& Yapf() {return *static_cast<Tpf*>(this);}
00188 
00189 public:
00191   FORCEINLINE bool PfDetectDestination(Node& n)
00192   {
00193     bool bDest = (n.m_segment_last_tile == m_destTile) && ((m_destTrackdirs & TrackdirToTrackdirBits(n.m_segment_last_td)) != TRACKDIR_BIT_NONE);
00194     return bDest;
00195   }
00196 
00199   inline bool PfCalcEstimate(Node& n)
00200   {
00201     static int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
00202     static int dg_dir_to_y_offs[] = {0, 1, 0, -1};
00203     if (PfDetectDestination(n)) {
00204       n.m_estimate = n.m_cost;
00205       return true;
00206     }
00207 
00208     TileIndex tile = n.m_segment_last_tile;
00209     DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
00210     int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
00211     int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
00212     int x2 = 2 * TileX(m_destTile);
00213     int y2 = 2 * TileY(m_destTile);
00214     int dx = abs(x1 - x2);
00215     int dy = abs(y1 - y2);
00216     int dmin = min(dx, dy);
00217     int dxy = abs(dx - dy);
00218     int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
00219     n.m_estimate = n.m_cost + d;
00220     assert(n.m_estimate >= n.m_parent->m_estimate);
00221     return true;
00222   }
00223 };
00224 
00225 
00226 
00227 template <class Types>
00228 class CYapfFollowRoadT
00229 {
00230 public:
00231   typedef typename Types::Tpf Tpf;                     
00232   typedef typename Types::TrackFollower TrackFollower;
00233   typedef typename Types::NodeList::Titem Node;        
00234   typedef typename Node::Key Key;                      
00235 
00236 protected:
00238   FORCEINLINE Tpf& Yapf() {return *static_cast<Tpf*>(this);}
00239 
00240 public:
00241 
00245   inline void PfFollowNode(Node& old_node)
00246   {
00247     TrackFollower F(Yapf().GetVehicle());
00248     if (F.Follow(old_node.m_segment_last_tile, old_node.m_segment_last_td))
00249       Yapf().AddMultipleNodes(&old_node, F);
00250   }
00251 
00253   FORCEINLINE char TransportTypeChar() const {return 'r';}
00254 
00255   static Trackdir stChooseRoadTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir)
00256   {
00257     Tpf pf;
00258     return pf.ChooseRoadTrack(v, tile, enterdir);
00259   }
00260 
00261   FORCEINLINE Trackdir ChooseRoadTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir)
00262   {
00263     // handle special case - when next tile is destination tile
00264     if (tile == v->dest_tile) {
00265       // choose diagonal trackdir reachable from enterdir
00266       return (Trackdir)DiagdirToDiagTrackdir(enterdir);
00267     }
00268     // our source tile will be the next vehicle tile (should be the given one)
00269     TileIndex src_tile = tile;
00270     // get available trackdirs on the start tile
00271     TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->u.road.compatible_roadtypes));
00272     // select reachable trackdirs only
00273     src_trackdirs &= DiagdirReachesTrackdirs(enterdir);
00274 
00275     // get available trackdirs on the destination tile
00276     TileIndex dest_tile = v->dest_tile;
00277     TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(dest_tile, TRANSPORT_ROAD, v->u.road.compatible_roadtypes));
00278 
00279     // set origin and destination nodes
00280     Yapf().SetOrigin(src_tile, src_trackdirs);
00281     Yapf().SetDestination(dest_tile, dest_trackdirs);
00282 
00283     // find the best path
00284     Yapf().FindPath(v);
00285 
00286     // if path not found - return INVALID_TRACKDIR
00287     Trackdir next_trackdir = INVALID_TRACKDIR;
00288     Node *pNode = Yapf().GetBestNode();
00289     if (pNode != NULL) {
00290       // path was found or at least suggested
00291       // walk through the path back to its origin
00292       while (pNode->m_parent != NULL) {
00293         pNode = pNode->m_parent;
00294       }
00295       // return trackdir from the best origin node (one of start nodes)
00296       Node& best_next_node = *pNode;
00297       assert(best_next_node.GetTile() == tile);
00298       next_trackdir = best_next_node.GetTrackdir();
00299     }
00300     return next_trackdir;
00301   }
00302 
00303   static uint stDistanceToTile(const Vehicle *v, TileIndex tile)
00304   {
00305     Tpf pf;
00306     return pf.DistanceToTile(v, tile);
00307   }
00308 
00309   FORCEINLINE uint DistanceToTile(const Vehicle *v, TileIndex dst_tile)
00310   {
00311     // handle special case - when current tile is the destination tile
00312     if (dst_tile == v->tile) {
00313       // distance is zero in this case
00314       return 0;
00315     }
00316 
00317     if (!SetOriginFromVehiclePos(v)) return UINT_MAX;
00318 
00319     // set destination tile, trackdir
00320     //   get available trackdirs on the destination tile
00321     TrackdirBits dst_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(dst_tile, TRANSPORT_ROAD, v->u.road.compatible_roadtypes));
00322     Yapf().SetDestination(dst_tile, dst_td_bits);
00323 
00324     // if path not found - return distance = UINT_MAX
00325     uint dist = UINT_MAX;
00326 
00327     // find the best path
00328     if (!Yapf().FindPath(v)) return dist;
00329 
00330     Node *pNode = Yapf().GetBestNode();
00331     if (pNode != NULL) {
00332       // path was found
00333       // get the path cost estimate
00334       dist = pNode->GetCostEstimate();
00335     }
00336 
00337     return dist;
00338   }
00339 
00341   FORCEINLINE bool SetOriginFromVehiclePos(const Vehicle *v)
00342   {
00343     // set origin (tile, trackdir)
00344     TileIndex src_tile = v->tile;
00345     Trackdir src_td = GetVehicleTrackdir(v);
00346     if ((TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->u.road.compatible_roadtypes)) & TrackdirToTrackdirBits(src_td)) == 0) {
00347       // sometimes the roadveh is not on the road (it resides on non-existing track)
00348       // how should we handle that situation?
00349       return false;
00350     }
00351     Yapf().SetOrigin(src_tile, TrackdirToTrackdirBits(src_td));
00352     return true;
00353   }
00354 
00355   static Depot* stFindNearestDepot(const Vehicle* v, TileIndex tile, Trackdir td)
00356   {
00357     Tpf pf;
00358     return pf.FindNearestDepot(v, tile, td);
00359   }
00360 
00361   FORCEINLINE Depot* FindNearestDepot(const Vehicle* v, TileIndex tile, Trackdir td)
00362   {
00363     // set origin and destination nodes
00364     Yapf().SetOrigin(tile, TrackdirToTrackdirBits(td));
00365 
00366     // find the best path
00367     bool bFound = Yapf().FindPath(v);
00368     if (!bFound) return false;
00369 
00370     // some path found
00371     // get found depot tile
00372     Node *n = Yapf().GetBestNode();
00373     TileIndex depot_tile = n->m_segment_last_tile;
00374     assert(IsTileDepotType(depot_tile, TRANSPORT_ROAD));
00375     Depot* ret = GetDepotByTile(depot_tile);
00376     return ret;
00377   }
00378 };
00379 
00380 template <class Tpf_, class Tnode_list, template <class Types> class Tdestination>
00381 struct CYapfRoad_TypesT
00382 {
00383   typedef CYapfRoad_TypesT<Tpf_, Tnode_list, Tdestination>  Types;
00384 
00385   typedef Tpf_                              Tpf;
00386   typedef CFollowTrackRoad                  TrackFollower;
00387   typedef Tnode_list                        NodeList;
00388   typedef CYapfBaseT<Types>                 PfBase;
00389   typedef CYapfFollowRoadT<Types>           PfFollow;
00390   typedef CYapfOriginTileT<Types>           PfOrigin;
00391   typedef Tdestination<Types>               PfDestination;
00392   typedef CYapfSegmentCostCacheNoneT<Types> PfCache;
00393   typedef CYapfCostRoadT<Types>             PfCost;
00394 };
00395 
00396 struct CYapfRoad1         : CYapfT<CYapfRoad_TypesT<CYapfRoad1        , CRoadNodeListTrackDir, CYapfDestinationTileRoadT    > > {};
00397 struct CYapfRoad2         : CYapfT<CYapfRoad_TypesT<CYapfRoad2        , CRoadNodeListExitDir , CYapfDestinationTileRoadT    > > {};
00398 
00399 struct CYapfRoadAnyDepot1 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot1, CRoadNodeListTrackDir, CYapfDestinationAnyDepotRoadT> > {};
00400 struct CYapfRoadAnyDepot2 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot2, CRoadNodeListExitDir , CYapfDestinationAnyDepotRoadT> > {};
00401 
00402 
00403 Trackdir YapfChooseRoadTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir)
00404 {
00405   // default is YAPF type 2
00406   typedef Trackdir (*PfnChooseRoadTrack)(Vehicle*, TileIndex, DiagDirection);
00407   PfnChooseRoadTrack pfnChooseRoadTrack = &CYapfRoad2::stChooseRoadTrack; // default: ExitDir, allow 90-deg
00408 
00409   // check if non-default YAPF type should be used
00410   if (_patches.yapf.disable_node_optimization)
00411     pfnChooseRoadTrack = &CYapfRoad1::stChooseRoadTrack; // Trackdir, allow 90-deg
00412 
00413   Trackdir td_ret = pfnChooseRoadTrack(v, tile, enterdir);
00414   return td_ret;
00415 }
00416 
00417 uint YapfRoadVehDistanceToTile(const Vehicle* v, TileIndex tile)
00418 {
00419   // default is YAPF type 2
00420   typedef uint (*PfnDistanceToTile)(const Vehicle*, TileIndex);
00421   PfnDistanceToTile pfnDistanceToTile = &CYapfRoad2::stDistanceToTile; // default: ExitDir, allow 90-deg
00422 
00423   // check if non-default YAPF type should be used
00424   if (_patches.yapf.disable_node_optimization)
00425     pfnDistanceToTile = &CYapfRoad1::stDistanceToTile; // Trackdir, allow 90-deg
00426 
00427   // measure distance in YAPF units
00428   uint dist = pfnDistanceToTile(v, tile);
00429   // convert distance to tiles
00430   if (dist != UINT_MAX)
00431     dist = (dist + YAPF_TILE_LENGTH - 1) / YAPF_TILE_LENGTH;
00432   return dist;
00433 }
00434 
00435 Depot* YapfFindNearestRoadDepot(const Vehicle *v)
00436 {
00437   TileIndex tile = v->tile;
00438   Trackdir trackdir = GetVehicleTrackdir(v);
00439   if ((TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->u.road.compatible_roadtypes)) & TrackdirToTrackdirBits(trackdir)) == 0)
00440     return NULL;
00441 
00442   // handle the case when our vehicle is already in the depot tile
00443   if (IsTileType(tile, MP_ROAD) && IsTileDepotType(tile, TRANSPORT_ROAD)) {
00444     // only what we need to return is the Depot*
00445     return GetDepotByTile(tile);
00446   }
00447 
00448   // default is YAPF type 2
00449   typedef Depot* (*PfnFindNearestDepot)(const Vehicle*, TileIndex, Trackdir);
00450   PfnFindNearestDepot pfnFindNearestDepot = &CYapfRoadAnyDepot2::stFindNearestDepot;
00451 
00452   // check if non-default YAPF type should be used
00453   if (_patches.yapf.disable_node_optimization)
00454     pfnFindNearestDepot = &CYapfRoadAnyDepot1::stFindNearestDepot; // Trackdir, allow 90-deg
00455 
00456   Depot* ret = pfnFindNearestDepot(v, tile, trackdir);
00457   return ret;
00458 }

Generated on Wed Oct 1 17:03:26 2008 for openttd by  doxygen 1.5.6