yapf_road.cpp

Go to the documentation of this file.
00001 /* $Id: yapf_road.cpp 21510 2010-12-13 21:56:25Z 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 #include "../../stdafx.h"
00013 #include "yapf.hpp"
00014 #include "yapf_node_road.hpp"
00015 #include "../../roadstop_base.h"
00016 
00017 
00018 template <class Types>
00019 class CYapfCostRoadT
00020 {
00021 public:
00022   typedef typename Types::Tpf Tpf; 
00023   typedef typename Types::TrackFollower TrackFollower; 
00024   typedef typename Types::NodeList::Titem Node; 
00025   typedef typename Node::Key Key;    
00026 
00027 protected:
00029   Tpf& Yapf()
00030   {
00031     return *static_cast<Tpf*>(this);
00032   }
00033 
00034   int SlopeCost(TileIndex tile, TileIndex next_tile, Trackdir trackdir)
00035   {
00036     /* height of the center of the current tile */
00037     int x1 = TileX(tile) * TILE_SIZE;
00038     int y1 = TileY(tile) * TILE_SIZE;
00039     int z1 = GetSlopeZ(x1 + TILE_SIZE / 2, y1 + TILE_SIZE / 2);
00040 
00041     /* height of the center of the next tile */
00042     int x2 = TileX(next_tile) * TILE_SIZE;
00043     int y2 = TileY(next_tile) * TILE_SIZE;
00044     int z2 = GetSlopeZ(x2 + TILE_SIZE / 2, y2 + TILE_SIZE / 2);
00045 
00046     if (z2 - z1 > 1) {
00047       /* Slope up */
00048       return Yapf().PfGetSettings().road_slope_penalty;
00049     }
00050     return 0;
00051   }
00052 
00054   FORCEINLINE int OneTileCost(TileIndex tile, Trackdir trackdir)
00055   {
00056     int cost = 0;
00057     /* set base cost */
00058     if (IsDiagonalTrackdir(trackdir)) {
00059       cost += YAPF_TILE_LENGTH;
00060       switch (GetTileType(tile)) {
00061         case MP_ROAD:
00062           /* Increase the cost for level crossings */
00063           if (IsLevelCrossing(tile)) {
00064             cost += Yapf().PfGetSettings().road_crossing_penalty;
00065           }
00066           break;
00067 
00068         case MP_STATION: {
00069           const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
00070           if (IsDriveThroughStopTile(tile)) {
00071             /* Increase the cost for drive-through road stops */
00072             cost += Yapf().PfGetSettings().road_stop_penalty;
00073             DiagDirection dir = TrackdirToExitdir(trackdir);
00074             if (!RoadStop::IsDriveThroughRoadStopContinuation(tile, tile - TileOffsByDiagDir(dir))) {
00075               /* When we're the first road stop in a 'queue' of them we increase
00076                * cost based on the fill percentage of the whole queue. */
00077               const RoadStop::Entry *entry = rs->GetEntry(dir);
00078               cost += entry->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength();
00079             }
00080           } else {
00081             /* Increase cost for filled road stops */
00082             cost += Yapf().PfGetSettings().road_stop_bay_occupied_penalty * (!rs->IsFreeBay(0) + !rs->IsFreeBay(1)) / 2;
00083           }
00084           break;
00085         }
00086 
00087         default:
00088           break;
00089       }
00090     } else {
00091       /* non-diagonal trackdir */
00092       cost = YAPF_TILE_CORNER_LENGTH + Yapf().PfGetSettings().road_curve_penalty;
00093     }
00094     return cost;
00095   }
00096 
00097 public:
00103   FORCEINLINE bool PfCalcCost(Node& n, const TrackFollower *tf)
00104   {
00105     int segment_cost = 0;
00106     /* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */
00107     TileIndex tile = n.m_key.m_tile;
00108     Trackdir trackdir = n.m_key.m_td;
00109     while (true) {
00110       /* base tile cost depending on distance between edges */
00111       segment_cost += Yapf().OneTileCost(tile, trackdir);
00112 
00113       const RoadVehicle *v = Yapf().GetVehicle();
00114       /* we have reached the vehicle's destination - segment should end here to avoid target skipping */
00115       if (Yapf().PfDetectDestinationTile(tile, trackdir)) break;
00116 
00117       /* stop if we have just entered the depot */
00118       if (IsRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) {
00119         /* next time we will reverse and leave the depot */
00120         break;
00121       }
00122 
00123       /* if there are no reachable trackdirs on new tile, we have end of road */
00124       TrackFollower F(Yapf().GetVehicle());
00125       if (!F.Follow(tile, trackdir)) break;
00126 
00127       /* if there are more trackdirs available & reachable, we are at the end of segment */
00128       if (KillFirstBit(F.m_new_td_bits) != TRACKDIR_BIT_NONE) break;
00129 
00130       Trackdir new_td = (Trackdir)FindFirstBit2x64(F.m_new_td_bits);
00131 
00132       /* stop if RV is on simple loop with no junctions */
00133       if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) return false;
00134 
00135       /* if we skipped some tunnel tiles, add their cost */
00136       segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH;
00137 
00138       /* add hilly terrain penalty */
00139       segment_cost += Yapf().SlopeCost(tile, F.m_new_tile, trackdir);
00140 
00141       /* add min/max speed penalties */
00142       int min_speed = 0;
00143       int max_veh_speed = v->GetDisplayMaxSpeed();
00144       int max_speed = F.GetSpeedLimit(&min_speed);
00145       if (max_speed < max_veh_speed) segment_cost += 1 * (max_veh_speed - max_speed);
00146       if (min_speed > max_veh_speed) segment_cost += 10 * (min_speed - max_veh_speed);
00147 
00148       /* move to the next tile */
00149       tile = F.m_new_tile;
00150       trackdir = new_td;
00151     };
00152 
00153     /* save end of segment back to the node */
00154     n.m_segment_last_tile = tile;
00155     n.m_segment_last_td = trackdir;
00156 
00157     /* save also tile cost */
00158     int parent_cost = (n.m_parent != NULL) ? n.m_parent->m_cost : 0;
00159     n.m_cost = parent_cost + segment_cost;
00160     return true;
00161   }
00162 };
00163 
00164 
00165 template <class Types>
00166 class CYapfDestinationAnyDepotRoadT
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 
00175   Tpf& Yapf()
00176   {
00177     return *static_cast<Tpf*>(this);
00178   }
00179 
00181   FORCEINLINE bool PfDetectDestination(Node& n)
00182   {
00183     bool bDest = IsRoadDepotTile(n.m_segment_last_tile);
00184     return bDest;
00185   }
00186 
00187   FORCEINLINE bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
00188   {
00189     return IsRoadDepotTile(tile);
00190   }
00191 
00196   FORCEINLINE bool PfCalcEstimate(Node& n)
00197   {
00198     n.m_estimate = n.m_cost;
00199     return true;
00200   }
00201 };
00202 
00203 
00204 template <class Types>
00205 class CYapfDestinationTileRoadT
00206 {
00207 public:
00208   typedef typename Types::Tpf Tpf;                     
00209   typedef typename Types::TrackFollower TrackFollower;
00210   typedef typename Types::NodeList::Titem Node;        
00211   typedef typename Node::Key Key;                      
00212 
00213 protected:
00214   TileIndex    m_destTile;
00215   TrackdirBits m_destTrackdirs;
00216   StationID    m_dest_station;
00217   bool         m_bus;
00218   bool         m_non_artic;
00219 
00220 public:
00221   void SetDestination(const RoadVehicle *v)
00222   {
00223     if (v->current_order.IsType(OT_GOTO_STATION)) {
00224       m_dest_station  = v->current_order.GetDestination();
00225       m_bus           = v->IsBus();
00226       m_destTile      = CalcClosestStationTile(m_dest_station, v->tile, m_bus ? STATION_BUS : STATION_TRUCK);
00227       m_non_artic     = !v->HasArticulatedPart();
00228       m_destTrackdirs = INVALID_TRACKDIR_BIT;
00229     } else {
00230       m_dest_station  = INVALID_STATION;
00231       m_destTile      = v->dest_tile;
00232       m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, v->compatible_roadtypes));
00233     }
00234   }
00235 
00236 protected:
00238   Tpf& Yapf()
00239   {
00240     return *static_cast<Tpf*>(this);
00241   }
00242 
00243 public:
00245   FORCEINLINE bool PfDetectDestination(Node& n)
00246   {
00247     return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td);
00248   }
00249 
00250   FORCEINLINE bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
00251   {
00252     if (m_dest_station != INVALID_STATION) {
00253       return IsTileType(tile, MP_STATION) &&
00254         GetStationIndex(tile) == m_dest_station &&
00255         (m_bus ? IsBusStop(tile) : IsTruckStop(tile)) &&
00256         (m_non_artic || IsDriveThroughStopTile(tile));
00257     }
00258 
00259     return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
00260   }
00261 
00266   inline bool PfCalcEstimate(Node& n)
00267   {
00268     static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
00269     static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
00270     if (PfDetectDestination(n)) {
00271       n.m_estimate = n.m_cost;
00272       return true;
00273     }
00274 
00275     TileIndex tile = n.m_segment_last_tile;
00276     DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
00277     int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
00278     int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
00279     int x2 = 2 * TileX(m_destTile);
00280     int y2 = 2 * TileY(m_destTile);
00281     int dx = abs(x1 - x2);
00282     int dy = abs(y1 - y2);
00283     int dmin = min(dx, dy);
00284     int dxy = abs(dx - dy);
00285     int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
00286     n.m_estimate = n.m_cost + d;
00287     assert(n.m_estimate >= n.m_parent->m_estimate);
00288     return true;
00289   }
00290 };
00291 
00292 
00293 
00294 template <class Types>
00295 class CYapfFollowRoadT
00296 {
00297 public:
00298   typedef typename Types::Tpf Tpf;                     
00299   typedef typename Types::TrackFollower TrackFollower;
00300   typedef typename Types::NodeList::Titem Node;        
00301   typedef typename Node::Key Key;                      
00302 
00303 protected:
00305   FORCEINLINE Tpf& Yapf()
00306   {
00307     return *static_cast<Tpf*>(this);
00308   }
00309 
00310 public:
00311 
00317   inline void PfFollowNode(Node& old_node)
00318   {
00319     TrackFollower F(Yapf().GetVehicle());
00320     if (F.Follow(old_node.m_segment_last_tile, old_node.m_segment_last_td)) {
00321       Yapf().AddMultipleNodes(&old_node, F);
00322     }
00323   }
00324 
00326   FORCEINLINE char TransportTypeChar() const
00327   {
00328     return 'r';
00329   }
00330 
00331   static Trackdir stChooseRoadTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, bool &path_found)
00332   {
00333     Tpf pf;
00334     return pf.ChooseRoadTrack(v, tile, enterdir, path_found);
00335   }
00336 
00337   FORCEINLINE Trackdir ChooseRoadTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, bool &path_found)
00338   {
00339     /* Handle special case - when next tile is destination tile.
00340      * However, when going to a station the (initial) destination
00341      * tile might not be a station, but a junction, in which case
00342      * this method forces the vehicle to jump in circles. */
00343     if (tile == v->dest_tile && !v->current_order.IsType(OT_GOTO_STATION)) {
00344       /* choose diagonal trackdir reachable from enterdir */
00345       return DiagDirToDiagTrackdir(enterdir);
00346     }
00347     /* our source tile will be the next vehicle tile (should be the given one) */
00348     TileIndex src_tile = tile;
00349     /* get available trackdirs on the start tile */
00350     TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes));
00351     /* select reachable trackdirs only */
00352     src_trackdirs &= DiagdirReachesTrackdirs(enterdir);
00353 
00354     /* set origin and destination nodes */
00355     Yapf().SetOrigin(src_tile, src_trackdirs);
00356     Yapf().SetDestination(v);
00357 
00358     /* find the best path */
00359     path_found = Yapf().FindPath(v);
00360 
00361     /* if path not found - return INVALID_TRACKDIR */
00362     Trackdir next_trackdir = INVALID_TRACKDIR;
00363     Node *pNode = Yapf().GetBestNode();
00364     if (pNode != NULL) {
00365       /* path was found or at least suggested
00366        * walk through the path back to its origin */
00367       while (pNode->m_parent != NULL) {
00368         pNode = pNode->m_parent;
00369       }
00370       /* return trackdir from the best origin node (one of start nodes) */
00371       Node& best_next_node = *pNode;
00372       assert(best_next_node.GetTile() == tile);
00373       next_trackdir = best_next_node.GetTrackdir();
00374     }
00375     return next_trackdir;
00376   }
00377 
00378   static uint stDistanceToTile(const RoadVehicle *v, TileIndex tile)
00379   {
00380     Tpf pf;
00381     return pf.DistanceToTile(v, tile);
00382   }
00383 
00384   FORCEINLINE uint DistanceToTile(const RoadVehicle *v, TileIndex dst_tile)
00385   {
00386     /* handle special case - when current tile is the destination tile */
00387     if (dst_tile == v->tile) {
00388       /* distance is zero in this case */
00389       return 0;
00390     }
00391 
00392     if (!SetOriginFromVehiclePos(v)) return UINT_MAX;
00393 
00394     /* get available trackdirs on the destination tile */
00395     Yapf().SetDestination(v);
00396 
00397     /* if path not found - return distance = UINT_MAX */
00398     uint dist = UINT_MAX;
00399 
00400     /* find the best path */
00401     if (!Yapf().FindPath(v)) return dist;
00402 
00403     Node *pNode = Yapf().GetBestNode();
00404     if (pNode != NULL) {
00405       /* path was found
00406        * get the path cost estimate */
00407       dist = pNode->GetCostEstimate();
00408     }
00409 
00410     return dist;
00411   }
00412 
00414   FORCEINLINE bool SetOriginFromVehiclePos(const RoadVehicle *v)
00415   {
00416     /* set origin (tile, trackdir) */
00417     TileIndex src_tile = v->tile;
00418     Trackdir src_td = v->GetVehicleTrackdir();
00419     if ((TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(src_td)) == 0) {
00420       /* sometimes the roadveh is not on the road (it resides on non-existing track)
00421        * how should we handle that situation? */
00422       return false;
00423     }
00424     Yapf().SetOrigin(src_tile, TrackdirToTrackdirBits(src_td));
00425     return true;
00426   }
00427 
00428   static bool stFindNearestDepot(const RoadVehicle *v, TileIndex tile, Trackdir td, int max_distance, TileIndex *depot_tile)
00429   {
00430     Tpf pf;
00431     return pf.FindNearestDepot(v, tile, td, max_distance, depot_tile);
00432   }
00433 
00434   FORCEINLINE bool FindNearestDepot(const RoadVehicle *v, TileIndex tile, Trackdir td, int max_distance, TileIndex *depot_tile)
00435   {
00436     /* set origin and destination nodes */
00437     Yapf().SetOrigin(tile, TrackdirToTrackdirBits(td));
00438 
00439     /* find the best path */
00440     bool bFound = Yapf().FindPath(v);
00441     if (!bFound) return false;
00442 
00443     /* some path found
00444      * get found depot tile */
00445     Node *n = Yapf().GetBestNode();
00446 
00447     if (max_distance > 0 && n->m_cost > max_distance * YAPF_TILE_LENGTH) return false;
00448 
00449     *depot_tile = n->m_segment_last_tile;
00450     return true;
00451   }
00452 };
00453 
00454 template <class Tpf_, class Tnode_list, template <class Types> class Tdestination>
00455 struct CYapfRoad_TypesT
00456 {
00457   typedef CYapfRoad_TypesT<Tpf_, Tnode_list, Tdestination>  Types;
00458 
00459   typedef Tpf_                              Tpf;
00460   typedef CFollowTrackRoad                  TrackFollower;
00461   typedef Tnode_list                        NodeList;
00462   typedef RoadVehicle                       VehicleType;
00463   typedef CYapfBaseT<Types>                 PfBase;
00464   typedef CYapfFollowRoadT<Types>           PfFollow;
00465   typedef CYapfOriginTileT<Types>           PfOrigin;
00466   typedef Tdestination<Types>               PfDestination;
00467   typedef CYapfSegmentCostCacheNoneT<Types> PfCache;
00468   typedef CYapfCostRoadT<Types>             PfCost;
00469 };
00470 
00471 struct CYapfRoad1         : CYapfT<CYapfRoad_TypesT<CYapfRoad1        , CRoadNodeListTrackDir, CYapfDestinationTileRoadT    > > {};
00472 struct CYapfRoad2         : CYapfT<CYapfRoad_TypesT<CYapfRoad2        , CRoadNodeListExitDir , CYapfDestinationTileRoadT    > > {};
00473 
00474 struct CYapfRoadAnyDepot1 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot1, CRoadNodeListTrackDir, CYapfDestinationAnyDepotRoadT> > {};
00475 struct CYapfRoadAnyDepot2 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot2, CRoadNodeListExitDir , CYapfDestinationAnyDepotRoadT> > {};
00476 
00477 
00478 Trackdir YapfRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirs, bool &path_found)
00479 {
00480   /* default is YAPF type 2 */
00481   typedef Trackdir (*PfnChooseRoadTrack)(const RoadVehicle*, TileIndex, DiagDirection, bool &path_found);
00482   PfnChooseRoadTrack pfnChooseRoadTrack = &CYapfRoad2::stChooseRoadTrack; // default: ExitDir, allow 90-deg
00483 
00484   /* check if non-default YAPF type should be used */
00485   if (_settings_game.pf.yapf.disable_node_optimization) {
00486     pfnChooseRoadTrack = &CYapfRoad1::stChooseRoadTrack; // Trackdir, allow 90-deg
00487   }
00488 
00489   Trackdir td_ret = pfnChooseRoadTrack(v, tile, enterdir, path_found);
00490   return (td_ret != INVALID_TRACKDIR) ? td_ret : (Trackdir)FindFirstBit2x64(trackdirs);
00491 }
00492 
00493 FindDepotData YapfRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_distance)
00494 {
00495   TileIndex tile = v->tile;
00496   Trackdir trackdir = v->GetVehicleTrackdir();
00497   if ((TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(trackdir)) == 0) {
00498     return FindDepotData();
00499   }
00500 
00501   /* default is YAPF type 2 */
00502   typedef bool (*PfnFindNearestDepot)(const RoadVehicle*, TileIndex, Trackdir, int, TileIndex*);
00503   PfnFindNearestDepot pfnFindNearestDepot = &CYapfRoadAnyDepot2::stFindNearestDepot;
00504 
00505   /* check if non-default YAPF type should be used */
00506   if (_settings_game.pf.yapf.disable_node_optimization) {
00507     pfnFindNearestDepot = &CYapfRoadAnyDepot1::stFindNearestDepot; // Trackdir, allow 90-deg
00508   }
00509 
00510   FindDepotData fdd;
00511   bool ret = pfnFindNearestDepot(v, tile, trackdir, max_distance, &fdd.tile);
00512   fdd.best_length = ret ? max_distance / 2 : UINT_MAX; // some fake distance or NOT_FOUND
00513   return fdd;
00514 }

Generated on Thu Dec 23 23:41:29 2010 for OpenTTD by  doxygen 1.6.1