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 = 20;          
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     return DistanceSquare(st->xy, this->m_dest.tile) * this->Yapf().PfGetSettings().route_distance_factor;
00288   }
00289 
00291   FORCEINLINE bool PfDetectDestination(StationID st_id) const
00292   {
00293     const Station *st = Station::Get(st_id);
00294     return st->rect.AreaInExtendedRect(this->m_dest, st->GetCatchmentRadius());
00295   }
00296 
00298   FORCEINLINE bool PfDetectDestination(const Node& n) const
00299   {
00300     return n.GetRouteLink() == NULL;
00301   }
00302 
00304   FORCEINLINE bool PfCalcEstimate(Node& n)
00305   {
00306     if (this->PfDetectDestination(n)) {
00307       n.m_estimate = n.m_cost;
00308       return true;
00309     }
00310 
00311     /* Estimate based on Manhattan distance to destination. */
00312     Station *from = Station::Get(n.GetRouteLink()->GetDestination());
00313     int d = DistanceManhattan(from->xy, this->m_dest.tile) * this->Yapf().PfGetSettings().route_distance_factor;
00314 
00315     n.m_estimate = n.m_cost + d;
00316     assert(n.m_estimate >= n.m_parent->m_estimate);
00317     return true;
00318   }
00319 };
00320 
00322 template <class Types>
00323 class CYapfFollowRouteLinkT {
00324   typedef typename Types::Tpf Tpf;                     
00325   typedef typename Types::TrackFollower Follower;      
00326   typedef typename Types::NodeList::Titem Node;        
00327 
00329   FORCEINLINE Tpf& Yapf() { return *static_cast<Tpf*>(this); }
00330 
00331 public:
00333   inline void PfFollowNode(Node& old_node)
00334   {
00335     Follower f(this->Yapf().GetCargoID());
00336 
00337     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) {
00338       /* Possible destination? Add sentinel node for final delivery. */
00339       Node &n = this->Yapf().CreateNewNode();
00340       n.Set(&old_node, NULL);
00341       this->Yapf().AddNewNode(n, f);
00342     }
00343 
00344     if (f.Follow(old_node.GetRouteLink())) {
00345       for (RouteLinkList::iterator link = f.m_new_links->begin(); link != f.m_new_links->end(); ++link) {
00346         /* Add new node. */
00347         Node &n = this->Yapf().CreateNewNode();
00348         n.Set(&old_node, *link);
00349         this->Yapf().AddNewNode(n, f);
00350       }
00351     }
00352   }
00353 
00355   FORCEINLINE char TransportTypeChar() const
00356   {
00357     return 'c';
00358   }
00359 
00361   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)
00362   {
00363     /* Initialize pathfinder instance. */
00364     Tpf pf;
00365     pf.SetOrigin(cid, src, stations, start_station != NULL, order, flags);
00366     pf.SetDestination(dest, max_cost);
00367 
00368     *next_unload = INVALID_STATION;
00369 
00370     /* Do it. Exit if we didn't find a path. */
00371     bool res = pf.FindPath(NULL);
00372     if (found != NULL) *found = res;
00373     if (!res) return NULL;
00374 
00375     /* Walk back to find the start node. */
00376     Node *node = pf.GetBestNode();
00377     while (node->m_parent->m_parent != NULL) {
00378       /* Transfer? Then save transfer station as next unload station. */
00379       if (node->GetRouteLink() == NULL || (node->GetRouteLink()->GetOriginOrderId() != node->m_parent->GetRouteLink()->GetDestOrderId())) {
00380         *next_unload = node->m_parent->GetRouteLink()->GetDestination();
00381       }
00382 
00383       node = node->m_parent;
00384     }
00385 
00386     /* Save result. */
00387     if (start_station != NULL) *start_station = node->m_parent->GetRouteLink()->GetDestination();
00388     return node->GetRouteLink();
00389   }
00390 };
00391 
00393 template <class Tpf_>
00394 struct CYapfRouteLink_TypesT {
00395   typedef CYapfRouteLink_TypesT<Tpf_> Types;
00396 
00397   typedef Tpf_               Tpf;           
00398   typedef CFollowRouteLinkT  TrackFollower; 
00399   typedef CRouteLinkNodeList NodeList;      
00400   typedef Vehicle            VehicleType;   
00401 
00402   typedef CYapfBaseT<Types>                 PfBase;        
00403   typedef CYapfFollowRouteLinkT<Types>      PfFollow;      
00404   typedef CYapfOriginRouteLinkT<Types>      PfOrigin;      
00405   typedef CYapfDestinationRouteLinkT<Types> PfDestination; 
00406   typedef CYapfSegmentCostCacheNoneT<Types> PfCache;       
00407   typedef CYapfCostRouteLinkT<Types>        PfCost;        
00408 };
00409 
00410 struct CYapfRouteLink : CYapfT<CYapfRouteLink_TypesT<CYapfRouteLink> > {};
00411 
00412 
00426 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)
00427 {
00428   return CYapfRouteLink::ChooseRouteLink(cid, stations, src, dest, start_station, next_unload, flags, found, order, max_cost);
00429 }

Generated on Mon May 9 05:18:58 2011 for OpenTTD by  doxygen 1.6.1