OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

demands.cpp
Go to the documentation of this file.
1
3 #include "../stdafx.h"
4 #include "demands.h"
5 #include <queue>
6
7 #include "../safeguards.h"
8
9 typedef std::queue<NodeID> NodeList;
10
14 class Scaler {
15 public:
16 void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
17 };
18
22 class SymmetricScaler : public Scaler {
23 public:
29 inline SymmetricScaler(uint mod_size) : mod_size(mod_size), supply_sum(0),
30 demand_per_node(0)
31 {}
32
37 inline void AddNode(const Node &node)
38 {
39 this->supply_sum += node.Supply();
40 }
41
46 inline void SetDemandPerNode(uint num_demands)
47 {
48 this->demand_per_node = max(this->supply_sum / num_demands, 1U);
49 }
50
58 inline uint EffectiveSupply(const Node &from, const Node &to)
59 {
60 return max(from.Supply() * max(1U, to.Supply()) * this->mod_size / 100 / this->demand_per_node, 1U);
61 }
62
70 inline bool HasDemandLeft(const Node &to)
71 {
72 return (to.Supply() == 0 || to.UndeliveredSupply() > 0) && to.Demand() > 0;
73 }
74
75 void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
76
77 private:
78 uint mod_size;
79 uint supply_sum;
80 uint demand_per_node;
81 };
82
86 class AsymmetricScaler : public Scaler {
87 public:
92 inline void AddNode(const Node &)
93 {
94 }
95
100 inline void SetDemandPerNode(uint)
101 {
102 }
103
109 inline uint EffectiveSupply(const Node &from, const Node &)
110 {
111 return from.Supply();
112 }
113
120 inline bool HasDemandLeft(const Node &to) { return to.Demand() > 0; }
121 };
122
131 void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
132 {
133 if (job[from_id].Demand() > 0) {
134 uint demand_back = demand_forw * this->mod_size / 100;
135 uint undelivered = job[to_id].UndeliveredSupply();
136 if (demand_back > undelivered) {
137 demand_back = undelivered;
138 demand_forw = max(1U, demand_back * 100 / this->mod_size);
139 }
140 this->Scaler::SetDemands(job, to_id, from_id, demand_back);
141 }
142
143 this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
144 }
145
154 inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
155 {
156 job[from_id].DeliverSupply(to_id, demand_forw);
157 }
158
164 template<class Tscaler>
165 void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler)
166 {
167 NodeList supplies;
168 NodeList demands;
169 uint num_supplies = 0;
170 uint num_demands = 0;
171
172 for (NodeID node = 0; node < job.Size(); node++) {
173 scaler.AddNode(job[node]);
174 if (job[node].Supply() > 0) {
175 supplies.push(node);
176 num_supplies++;
177 }
178 if (job[node].Demand() > 0) {
179 demands.push(node);
180 num_demands++;
181 }
182 }
183
184 if (num_supplies == 0 || num_demands == 0) return;
185
186 /* Mean acceptance attributed to each node. If the distribution is
187 * symmetric this is relative to remote supply, otherwise it is
188 * relative to remote demand. */
189 scaler.SetDemandPerNode(num_demands);
190 uint chance = 0;
191
192 while (!supplies.empty() && !demands.empty()) {
193 NodeID from_id = supplies.front();
194 supplies.pop();
195
196 for (uint i = 0; i < num_demands; ++i) {
197 assert(!demands.empty());
198 NodeID to_id = demands.front();
199 demands.pop();
200 if (from_id == to_id) {
201 /* Only one node with supply and demand left */
202 if (demands.empty() && supplies.empty()) return;
203
204 demands.push(to_id);
205 continue;
206 }
207
208 int32 supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
209 assert(supply > 0);
210
211 /* Scale the distance by mod_dist around max_distance */
212 int32 distance = this->max_distance - (this->max_distance -
213 (int32)DistanceMaxPlusManhattan(job[from_id].XY(), job[to_id].XY())) *
214 this->mod_dist / 100;
215
216 /* Scale the accuracy by distance around accuracy / 2 */
217 int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 +
218 this->accuracy * distance / this->max_distance + 1;
219
220 assert(divisor > 0);
221
222 uint demand_forw = 0;
223 if (divisor <= supply) {
224 /* At first only distribute demand if
225 * effective supply / accuracy divisor >= 1
226 * Others are too small or too far away to be considered. */
227 demand_forw = supply / divisor;
228 } else if (++chance > this->accuracy * num_demands * num_supplies) {
229 /* After some trying, if there is still supply left, distribute
230 * demand also to other nodes. */
231 demand_forw = 1;
232 }
233
234 demand_forw = min(demand_forw, job[from_id].UndeliveredSupply());
235
236 scaler.SetDemands(job, from_id, to_id, demand_forw);
237
238 if (scaler.HasDemandLeft(job[to_id])) {
239 demands.push(to_id);
240 } else {
241 num_demands--;
242 }
243
244 if (job[from_id].UndeliveredSupply() == 0) break;
245 }
246
247 if (job[from_id].UndeliveredSupply() != 0) {
248 supplies.push(from_id);
249 } else {
250 num_supplies--;
251 }
252 }
253 }
254
259 DemandCalculator::DemandCalculator(LinkGraphJob &job) :
260 max_distance(DistanceMaxPlusManhattan(TileXY(0,0), TileXY(MapMaxX(), MapMaxY())))
261 {
262 const LinkGraphSettings &settings = job.Settings();
263 CargoID cargo = job.Cargo();
264
265 this->accuracy = settings.accuracy;
266 this->mod_dist = settings.demand_distance;
267 if (this->mod_dist > 100) {
268 /* Increase effect of mod_dist > 100 */
269 int over100 = this->mod_dist - 100;
270 this->mod_dist = 100 + over100 * over100;
271 }
272
273 switch (settings.GetDistributionType(cargo)) {
274 case DT_SYMMETRIC:
275 this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
276 break;
277 case DT_ASYMMETRIC:
278 this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
279 break;
280 default:
281 /* Nothing to do. */
282 break;
283 }
284 }