yapf_cargo.cpp

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 #include "../../stdafx.h"
00013 #include "../../cargodest_base.h"
00014 #include "../../station_base.h"
00015 #include "../../town.h"
00016 #include "yapf.hpp"
00017 
00018 
00020 struct CYapfRouteLinkNodeKeyT {
00021   RouteLink *m_link;
00022 
00024   FORCEINLINE void Set(RouteLink *link)
00025   {
00026     this->m_link = link;
00027   }
00028 
00030   FORCEINLINE int CalcHash() const
00031   {
00032     return (int)(size_t)this->m_link >> 4;
00033   }
00034 
00035   FORCEINLINE bool operator == (const CYapfRouteLinkNodeKeyT& other) const
00036   {
00037     return this->m_link == other.m_link;
00038   }
00039 
00040   void Dump(DumpTarget &dmp) const
00041   {
00042     dmp.WriteLine("m_link = %u", this->m_link->GetDestination());
00043   }
00044 };
00045 
00047 struct CYapfRouteLinkNodeT : public CYapfNodeT<CYapfRouteLinkNodeKeyT, CYapfRouteLinkNodeT> {
00048   typedef CYapfNodeT<CYapfRouteLinkNodeKeyT, CYapfRouteLinkNodeT> Base;
00049 
00050   uint m_num_transfers; 
00051 
00053   FORCEINLINE void Set(CYapfRouteLinkNodeT *parent, RouteLink *link)
00054   {
00055     Base::Set(parent, false);
00056     this->m_key.Set(link);
00057     this->m_num_transfers = (parent != NULL) ? parent->m_num_transfers : 0;
00058   }
00059 
00061   FORCEINLINE RouteLink *GetRouteLink() const { return this->m_key.m_link; }
00062 
00064   FORCEINLINE int GetNumberOfTransfers() const { return this->m_num_transfers; }
00065 };
00066 
00067 typedef CNodeList_HashTableT<CYapfRouteLinkNodeT, 8, 10, 2048> CRouteLinkNodeList;
00068 
00070 struct CFollowRouteLinkT {
00071   CargoID m_cid;
00072   RouteLink *m_old_link;
00073   RouteLinkList *m_new_links;
00074 
00075   CFollowRouteLinkT(CargoID cid) : m_cid(cid) {}
00076 
00078   inline bool Follow(RouteLink *from)
00079   {
00080     this->m_old_link = from;
00081 
00082     Station *st = Station::Get(from->GetDestination());
00083     m_new_links = &st->goods[this->m_cid].routes;
00084     return !this->m_new_links->empty();
00085   }
00086 };
00087 
00089 template <class Types>
00090 class CYapfCostRouteLinkT {
00091   typedef typename Types::Tpf Tpf;                     
00092   typedef typename Types::TrackFollower Follower;      
00093   typedef typename Types::NodeList::Titem Node;        
00094 
00095   static const int PENALTY_DIVISOR      = 16;          
00096   static const int LOCAL_PENALTY_FACTOR = 16;          
00097   static const int RF_DISTANCE_FACTOR   = 2;           
00098   static const int RF_TIME_FACTOR       = 3;           
00099 
00101   FORCEINLINE Tpf& Yapf() { return *static_cast<Tpf*>(this); }
00102   FORCEINLINE const Tpf& Yapf() const { return *static_cast<const Tpf*>(this); }
00103 
00105   FORCEINLINE bool ValidLink(Node &n, const RouteLink *link, const RouteLink *parent) const
00106   {
00107     /* If the parent link has an owner, and the owner is different to
00108      * the new owner, discard the node. Otherwise cargo could switch
00109      * companies at oil rigs, which would mess up payment. */
00110     if (parent->GetOwner() != INVALID_OWNER && link->GetOwner() != parent->GetOwner()) return false;
00111 
00112     /* Check for no loading/no unloading when transferring. */
00113     if (link->GetOriginOrderId() != parent->GetDestOrderId() || (Order::Get(link->GetOriginOrderId())->GetUnloadType() & OUFB_UNLOAD) != 0) {
00114       /* Can't transfer if the current order prohibits loading. */
00115       if ((Order::Get(link->GetOriginOrderId())->GetLoadType() & OLFB_NO_LOAD) != 0) return false;
00116 
00117       /* Can't transfer if the last order prohibits unloading. */
00118       if (parent->GetDestOrderId() != INVALID_ORDER && (Order::Get(parent->GetDestOrderId())->GetUnloadType() & OUFB_NO_UNLOAD) != 0) return false;
00119 
00120       /* Increase transfer counter and stop if max number of transfers is exceeded. */
00121       if (++n.m_num_transfers > Yapf().PfGetSettings().route_max_transfers) return false;
00122     }
00123 
00124     return true;
00125   }
00126 
00128   FORCEINLINE int RouteLinkCost(const RouteLink *link, const RouteLink *parent) const
00129   {
00130     int cost = 0;
00131 
00132     /* Distance cost. */
00133     const Station *from = Station::Get(parent->GetDestination());
00134     const Station *to = Station::Get(link->GetDestination());
00135     cost = DistanceManhattan(from->xy, to->xy) * this->Yapf().PfGetSettings().route_distance_factor;
00136 
00137     /* Modulate the distance by a vehicle-type specific factor to
00138      * simulate the different costs. Cost is doubled if the cargo
00139      * wants to go cheap. */
00140     assert_compile(lengthof(_settings_game.pf.yapf.route_mode_cost_factor) == VEH_AIRCRAFT + 1);
00141     byte dfactor = this->Yapf().PfGetSettings().route_mode_cost_factor[link->GetVehicleType()];
00142     if (HasBit(this->Yapf().GetFlags(), RF_WANT_CHEAP)) dfactor *= RF_DISTANCE_FACTOR;
00143     cost *= dfactor;
00144 
00145     /* Factor for the time penalties based on whether the cargo wants to go fast. */
00146     uint time_factor = HasBit(this->Yapf().GetFlags(), RF_WANT_FAST) ? RF_TIME_FACTOR : 1;
00147 
00148     /* Transfer penalty when switching vehicles or forced unloading. */
00149     if (link->GetOriginOrderId() != parent->GetDestOrderId() || (Order::Get(link->GetOriginOrderId())->GetUnloadType() & OUFB_UNLOAD) != 0) {
00150       cost += this->Yapf().PfGetSettings().route_transfer_cost;
00151 
00152       /* Penalty for time since the last vehicle arrived. */
00153       cost += link->GetWaitTime() * this->Yapf().PfGetSettings().route_station_last_veh_factor * time_factor / PENALTY_DIVISOR;
00154 
00155       /* Penalty for cargo waiting on our link. */
00156       cost += (from->goods[this->Yapf().GetCargoID()].cargo.CountForNextHop(link->GetOriginOrderId()) * this->Yapf().PfGetSettings().route_station_waiting_factor) / PENALTY_DIVISOR;
00157     }
00158 
00159     /* Penalty for travel time. */
00160     cost += (link->GetTravelTime() * this->Yapf().PfGetSettings().route_travel_time_factor * time_factor) / PENALTY_DIVISOR;
00161 
00162     return cost;
00163   }
00164 
00165 public:
00167   inline bool PfCalcCost(Node& n, const Follower *follow)
00168   {
00169     int segment_cost = 0;
00170 
00171     if (this->Yapf().PfDetectDestination(n)) {
00172       Station *st = Station::Get(n.m_parent->GetRouteLink()->GetDestination());
00173       /* Discard node if the station doesn't accept the cargo type. */
00174       if (!HasBit(st->goods[follow->m_cid].acceptance_pickup, GoodsEntry::ACCEPTANCE)) return false;
00175       /* Destination node, get delivery cost. Parent has the station. */
00176       segment_cost += this->Yapf().DeliveryCost(st);
00177       /* If this link comes from an origin station, penalize it to encourage
00178        * delivery using other stations. */
00179       if (n.m_parent->GetRouteLink()->GetDestOrderId() == INVALID_ORDER) segment_cost *= LOCAL_PENALTY_FACTOR;
00180     } else {
00181       RouteLink *link = n.GetRouteLink();
00182       RouteLink *parent = n.m_parent->GetRouteLink();
00183 
00184       /* Check if the link is a valid connection. */
00185       if (!this->ValidLink(n, link, parent)) return false;
00186 
00187       /* Cost of the single route link. */
00188       segment_cost += this->RouteLinkCost(link, parent);
00189     }
00190 
00191     /* Apply it. */
00192     n.m_cost = n.m_parent->m_cost + segment_cost;
00193     return n.m_cost <= this->Yapf().GetMaxCost();
00194   }
00195 };
00196 
00198 template <class Types>
00199 class CYapfOriginRouteLinkT {
00200   typedef typename Types::Tpf Tpf;                     
00201   typedef typename Types::NodeList::Titem Node;        
00202 
00203   CargoID   m_cid;
00204   TileIndex m_src;
00205   OrderID   m_order;
00206   byte      m_flags;
00207   SmallVector<RouteLink, 2> m_origin;
00208 
00210   FORCEINLINE Tpf& Yapf() { return *static_cast<Tpf*>(this); }
00211 
00212 public:
00214   FORCEINLINE CargoID GetCargoID() const
00215   {
00216     return this->m_cid;
00217   }
00218 
00220   FORCEINLINE byte GetFlags() const
00221   {
00222     return this->m_flags;
00223   }
00224 
00226   void SetOrigin(CargoID cid, TileIndex src, const StationList *stations, bool cargo_creation, OrderID order, byte flags)
00227   {
00228     this->m_cid = cid;
00229     this->m_src = src;
00230     this->m_order = order;
00231     this->m_flags = flags;
00232     /* Create fake links for the origin stations. */
00233     for (const Station * const *st = stations->Begin(); st != stations->End(); st++) {
00234       if (cargo_creation) {
00235         /* Exclusive rights in effect? Only serve those stations. */
00236         if ((*st)->town->exclusive_counter > 0 && (*st)->town->exclusivity != (*st)->owner) continue;
00237         /* Selectively servicing stations, and not this one. */
00238         if (_settings_game.order.selectgoods && (*st)->goods[cid].last_speed == 0) continue;
00239       }
00240 
00241       *this->m_origin.Append() = RouteLink((*st)->index, INVALID_ORDER, this->m_order);
00242     }
00243   }
00244 
00246   void PfSetStartupNodes()
00247   {
00248     for (RouteLink *link = this->m_origin.Begin(); link != this->m_origin.End(); link++) {
00249       Node &n = this->Yapf().CreateNewNode();
00250       n.Set(NULL, link);
00251       /* Prefer stations closer to the source tile. */
00252       n.m_cost = DistanceSquare(this->m_src, Station::Get(link->GetDestination())->xy) * this->Yapf().PfGetSettings().route_distance_factor;
00253       this->Yapf().AddStartupNode(n);
00254     }
00255   }
00256 };
00257 
00259 template <class Types>
00260 class CYapfDestinationRouteLinkT {
00261   typedef typename Types::Tpf Tpf;                     
00262   typedef typename Types::NodeList::Titem Node;        
00263 
00264   TileArea m_dest;
00265   int m_max_cost;            
00266 
00268   FORCEINLINE Tpf& Yapf() { return *static_cast<Tpf*>(this); }
00269 
00270 public:
00272   FORCEINLINE int GetMaxCost() const
00273   {
00274     return this->m_max_cost;
00275   }
00276 
00278   void SetDestination(const TileArea &dest, uint max_cost)
00279   {
00280     this->m_dest = dest;
00281     this->m_max_cost = max_cost;
00282   }
00283 
00285   FORCEINLINE int DeliveryCost(Station *st)
00286   {
00287     int x = TileX(this->m_dest.tile);
00288     int y = TileY(this->m_dest.tile);
00289 
00290     /* Inside the station area? Delivery costs "nothing". */
00291     if (st->rect.PtInExtendedRect(x, y)) return 0;
00292 
00293     int dist_x = x < st->rect.left ? x - st->rect.left : x - st->rect.right;
00294     int dist_y = y < st->rect.top  ? y - st->rect.top  : y - st->rect.bottom;
00295 
00296     return (dist_x * dist_x + dist_y * dist_y) * this->Yapf().PfGetSettings().route_distance_factor;
00297   }
00298 
00300   FORCEINLINE bool PfDetectDestination(StationID st_id) const
00301   {
00302     const Station *st = Station::Get(st_id);
00303     return st->rect.AreaInExtendedRect(this->m_dest, st->GetCatchmentRadius());
00304   }
00305 
00307   FORCEINLINE bool PfDetectDestination(const Node& n) const
00308   {
00309     return n.GetRouteLink() == NULL;
00310   }
00311 
00313   FORCEINLINE bool PfCalcEstimate(Node& n)
00314   {
00315     if (this->PfDetectDestination(n)) {
00316       n.m_estimate = n.m_cost;
00317       return true;
00318     }
00319 
00320     /* Estimate based on Manhattan distance to destination. */
00321     Station *from = Station::Get(n.GetRouteLink()->GetDestination());
00322     int d = DistanceManhattan(from->xy, this->m_dest.tile) * this->Yapf().PfGetSettings().route_distance_factor;
00323 
00324     n.m_estimate = n.m_cost + d;
00325     assert(n.m_estimate >= n.m_parent->m_estimate);
00326     return true;
00327   }
00328 };
00329 
00331 template <class Types>
00332 class CYapfFollowRouteLinkT {
00333   typedef typename Types::Tpf Tpf;                     
00334   typedef typename Types::TrackFollower Follower;      
00335   typedef typename Types::NodeList::Titem Node;        
00336 
00338   FORCEINLINE Tpf& Yapf() { return *static_cast<Tpf*>(this); }
00339 
00340 public:
00342   inline void PfFollowNode(Node& old_node)
00343   {
00344     Follower f(this->Yapf().GetCargoID());
00345 
00346     if (this->Yapf().PfDetectDestination(old_node.GetRouteLink()->GetDestination()) && (old_node.GetRouteLink()->GetDestOrderId() == INVALID_ORDER || (Order::Get(old_node.GetRouteLink()->GetDestOrderId())->GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
00347       /* Possible destination? Add sentinel node for final delivery. */
00348       Node &n = this->Yapf().CreateNewNode();
00349       n.Set(&old_node, NULL);
00350       this->Yapf().AddNewNode(n, f);
00351     }
00352 
00353     if (f.Follow(old_node.GetRouteLink())) {
00354       for (RouteLinkList::iterator link = f.m_new_links->begin(); link != f.m_new_links->end(); ++link) {
00355         /* Add new node. */
00356         Node &n = this->Yapf().CreateNewNode();
00357         n.Set(&old_node, *link);
00358         this->Yapf().AddNewNode(n, f);
00359       }
00360     }
00361   }
00362 
00364   FORCEINLINE char TransportTypeChar() const
00365   {
00366     return 'c';
00367   }
00368 
00370   static RouteLink *ChooseRouteLink(CargoID cid, const StationList *stations, TileIndex src, const TileArea &dest, StationID *start_station, StationID *next_unload, byte flags, bool *found, OrderID order, int max_cost)
00371   {
00372     /* Initialize pathfinder instance. */
00373     Tpf pf;
00374     pf.SetOrigin(cid, src, stations, start_station != NULL, order, flags);
00375     pf.SetDestination(dest, max_cost);
00376 
00377     *next_unload = INVALID_STATION;
00378 
00379     /* Do it. Exit if we didn't find a path. */
00380     bool res = pf.FindPath(NULL);
00381     if (found != NULL) *found = res;
00382     if (!res) return NULL;
00383 
00384     /* Walk back to find the start node. */
00385     Node *node = pf.GetBestNode();
00386     while (node->m_parent->m_parent != NULL) {
00387       /* Transfer? Then save transfer station as next unload station. */
00388       if (node->GetRouteLink() == NULL || (node->GetRouteLink()->GetOriginOrderId() != node->m_parent->GetRouteLink()->GetDestOrderId())) {
00389         *next_unload = node->m_parent->GetRouteLink()->GetDestination();
00390       }
00391 
00392       node = node->m_parent;
00393     }
00394 
00395     /* Save result. */
00396     if (start_station != NULL) {
00397       *start_station = node->m_parent->GetRouteLink()->GetDestination();
00398       /* Path starts and ends at the same station, do local delivery. */
00399       if (*start_station == pf.GetBestNode()->m_parent->GetRouteLink()->GetDestination()) return NULL;
00400     }
00401     return node->GetRouteLink();
00402   }
00403 };
00404 
00406 template <class Tpf_>
00407 struct CYapfRouteLink_TypesT {
00408   typedef CYapfRouteLink_TypesT<Tpf_> Types;
00409 
00410   typedef Tpf_               Tpf;           
00411   typedef CFollowRouteLinkT  TrackFollower; 
00412   typedef CRouteLinkNodeList NodeList;      
00413   typedef Vehicle            VehicleType;   
00414 
00415   typedef CYapfBaseT<Types>                 PfBase;        
00416   typedef CYapfFollowRouteLinkT<Types>      PfFollow;      
00417   typedef CYapfOriginRouteLinkT<Types>      PfOrigin;      
00418   typedef CYapfDestinationRouteLinkT<Types> PfDestination; 
00419   typedef CYapfSegmentCostCacheNoneT<Types> PfCache;       
00420   typedef CYapfCostRouteLinkT<Types>        PfCost;        
00421 };
00422 
00423 struct CYapfRouteLink : CYapfT<CYapfRouteLink_TypesT<CYapfRouteLink> > {};
00424 
00425 
00439 RouteLink *YapfChooseRouteLink(CargoID cid, const StationList *stations, TileIndex src, const TileArea &dest, StationID *start_station, StationID *next_unload, byte flags, bool *found, OrderID order, int max_cost)
00440 {
00441   return CYapfRouteLink::ChooseRouteLink(cid, stations, src, dest, start_station, next_unload, flags, found, order, max_cost);
00442 }

Generated on Fri Jun 3 05:18:55 2011 for OpenTTD by  doxygen 1.6.1