demands.cpp

Go to the documentation of this file.
00001 
00003 #include "../stdafx.h"
00004 #include "../station_base.h"
00005 #include "../settings_type.h"
00006 #include "../newgrf_cargo.h"
00007 #include "../cargotype.h"
00008 #include "../core/math_func.hpp"
00009 #include "demands.h"
00010 #include <list>
00011 
00012 typedef std::list<NodeID> NodeList;
00013 
00022 void SymmetricScaler::SetDemands(LinkGraphJob *job, NodeID from_id, NodeID to_id, uint demand_forw)
00023 {
00024   if (job->Graph().GetNode(from_id).demand > 0) {
00025     uint demand_back = demand_forw * this->mod_size / 100;
00026     uint undelivered = job->GetNode(to_id).undelivered_supply;
00027     if (demand_back > undelivered) {
00028       demand_back = undelivered;
00029       demand_forw = max(1U, demand_back * 100 / this->mod_size);
00030     }
00031     this->Scaler::SetDemands(job, to_id, from_id, demand_back);
00032   }
00033 
00034   this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
00035 }
00036 
00045 inline void Scaler::SetDemands(LinkGraphJob *job, NodeID from_id, NodeID to_id, uint demand_forw)
00046 {
00047   EdgeAnnotation &forward = job->GetEdge(from_id, to_id);
00048   forward.demand += demand_forw;
00049   forward.unsatisfied_demand += demand_forw;
00050   job->GetNode(from_id).undelivered_supply -= demand_forw;
00051 }
00052 
00058 template<class Tscaler>
00059 void DemandCalculator::CalcDemand(LinkGraphJob *job, Tscaler scaler)
00060 {
00061   NodeList supplies;
00062   NodeList demands;
00063   uint num_supplies = 0;
00064   uint num_demands = 0;
00065 
00066   LinkGraph *graph = &job->Graph();
00067   for (NodeID node = 0; node < graph->GetSize(); node++) {
00068     Node &n = graph->GetNode(node);
00069     scaler.AddNode(n);
00070     if (n.supply > 0) {
00071       supplies.push_back(node);
00072       num_supplies++;
00073     }
00074     if (n.demand > 0) {
00075       demands.push_back(node);
00076       num_demands++;
00077     }
00078   }
00079 
00080   if (num_supplies == 0 || num_demands == 0) return;
00081 
00082   /* Mean acceptance attributed to each node. If the distribution is
00083    * symmetric this is relative to remote supply, otherwise it is
00084    * relative to remote demand. */
00085   scaler.SetDemandPerNode(num_demands);
00086   uint chance = 0;
00087 
00088   while (!supplies.empty() && !demands.empty()) {
00089     NodeID from_id = supplies.front();
00090     supplies.pop_front();
00091 
00092     Node &from = graph->GetNode(from_id);
00093     NodeAnnotation &from_anno = job->GetNode(from_id);
00094 
00095     for (uint i = 0; i < num_demands; ++i) {
00096       assert(!demands.empty());
00097       NodeID to_id = demands.front();
00098       demands.pop_front();
00099       if (from_id == to_id) {
00100         /* Only one node with supply and demand left */
00101         if (demands.empty() && supplies.empty()) return;
00102 
00103         demands.push_back(to_id);
00104         continue;
00105       }
00106       Node &to = graph->GetNode(to_id);
00107 
00108       int32 supply = scaler.EffectiveSupply(from, to);
00109       assert(supply > 0);
00110 
00111       /* Scale the distance by mod_dist around max_distance */
00112       int32 distance = this->max_distance - (this->max_distance -
00113           (int32)graph->GetEdge(from_id, to_id).distance) * this->mod_dist / 100;
00114 
00115       /* Scale the accuracy by distance around accuracy / 2 */
00116       int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 +
00117           this->accuracy * distance / this->max_distance + 1;
00118 
00119       assert(divisor > 0);
00120 
00121       uint demand_forw = 0;
00122       if (divisor <= supply) {
00123         /* At first only distribute demand if
00124          * effective supply / accuracy divisor >= 1
00125          * Others are too small or too far away to be considered. */
00126         demand_forw = supply / divisor;
00127       } else if (++chance > this->accuracy * num_demands * num_supplies) {
00128         /* After some trying, if there is still supply left, distribute
00129          * demand also to other nodes. */
00130         demand_forw = 1;
00131       }
00132 
00133       demand_forw = min(demand_forw, from_anno.undelivered_supply);
00134 
00135       scaler.SetDemands(job, from_id, to_id, demand_forw);
00136 
00137       if (scaler.HasDemandLeft(to, job->GetNode(to_id))) {
00138         demands.push_back(to_id);
00139       } else {
00140         num_demands--;
00141       }
00142 
00143       if (from_anno.undelivered_supply == 0) break;
00144     }
00145 
00146     if (from_anno.undelivered_supply != 0) {
00147       supplies.push_back(from_id);
00148     } else {
00149       num_supplies--;
00150     }
00151   }
00152 }
00153 
00158 DemandCalculator::DemandCalculator(LinkGraphJob *job) :
00159   max_distance(MapSizeX() + MapSizeY() - 2)
00160 {
00161   const LinkGraphSettings &settings = job->Settings();
00162   CargoID cargo = job->Graph().GetCargo();
00163 
00164   this->accuracy = settings.accuracy;
00165   this->mod_dist = settings.demand_distance;
00166   if (this->mod_dist > 100) {
00167     /* Increase effect of mod_dist > 100 */
00168     int over100 = this->mod_dist - 100;
00169     this->mod_dist = 100 + over100 * over100;
00170   }
00171 
00172   switch (settings.GetDistributionType(cargo)) {
00173     case DT_SYMMETRIC:
00174       this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
00175       break;
00176     case DT_ASYMMETRIC:
00177       this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
00178       break;
00179     default:
00180       NOT_REACHED();
00181       break;
00182   }
00183 }