OpenTTD Source: src/linkgraph/demands.cpp Source File

OpenTTD Source 15.0-beta1

demands.cpp
Go to the documentation of this file.
1
3#include "../stdafx.h"
4#include "demands.h"
5#include "../core/math_func.hpp"
6#include <queue>
7
8#include "../safeguards.h"
9
10typedef std::queue<NodeID> NodeList;
11
15 class Scaler {
16public:
17 void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
18};
19
23 class SymmetricScaler : public Scaler {
24public:
30 inline SymmetricScaler(uint mod_size) : mod_size(mod_size), supply_sum(0),
31 demand_per_node(0)
32 {}
33
38 inline void AddNode(const Node &node)
39 {
40 this->supply_sum += node.base.supply;
41 }
42
47 inline void SetDemandPerNode(uint num_demands)
48 {
49 this->demand_per_node = std::max(this->supply_sum / num_demands, 1U);
50 }
51
59 inline uint EffectiveSupply(const Node &from, const Node &to)
60 {
61 return std::max(from.base.supply * std::max(1U, to.base.supply) * this->mod_size / 100 / this->demand_per_node, 1U);
62 }
63
71 inline bool HasDemandLeft(const Node &to)
72 {
73 return (to.base.supply == 0 || to.undelivered_supply > 0) && to.base.demand > 0;
74 }
75
76 void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
77
78private:
79 uint mod_size;
80 uint supply_sum;
81 uint demand_per_node;
82};
83
87 class AsymmetricScaler : public Scaler {
88public:
93 inline void AddNode(const Node &)
94 {
95 }
96
101 inline void SetDemandPerNode(uint)
102 {
103 }
104
110 inline uint EffectiveSupply(const Node &from, const Node &)
111 {
112 return from.base.supply;
113 }
114
120 inline bool HasDemandLeft(const Node &to) { return to.base.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].base.demand > 0) {
134 uint demand_back = demand_forw * this->mod_size / 100;
135 uint undelivered = job[to_id].undelivered_supply;
136 if (demand_back > undelivered) {
137 demand_back = undelivered;
138 demand_forw = std::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
164template<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].base.supply > 0) {
175 supplies.push(node);
176 num_supplies++;
177 }
178 if (job[node].base.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_t supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
209 assert(supply > 0);
210
211 constexpr int32_t divisor_scale = 16;
212
213 int32_t scaled_distance = this->base_distance;
214 if (this->mod_dist > 0) {
215 const int32_t distance = DistanceMaxPlusManhattan(job[from_id].base.xy, job[to_id].base.xy);
216 /* Scale distance around base_distance by (mod_dist * (100 / 1024)).
217 * mod_dist may be > 1024, so clamp result to be non-negative */
218 scaled_distance = std::max(0, this->base_distance + (((distance - this->base_distance) * this->mod_dist) / 1024));
219 }
220
221 /* Scale the accuracy by distance around accuracy / 2 */
222 const int32_t divisor = divisor_scale + ((this->accuracy * scaled_distance * divisor_scale) / (this->base_distance * 2));
223 assert(divisor >= divisor_scale);
224
225 uint demand_forw = 0;
226 if (divisor <= (supply * divisor_scale)) {
227 /* At first only distribute demand if
228 * effective supply / accuracy divisor >= 1
229 * Others are too small or too far away to be considered. */
230 demand_forw = (supply * divisor_scale) / divisor;
231 } else if (++chance > this->accuracy * num_demands * num_supplies) {
232 /* After some trying, if there is still supply left, distribute
233 * demand also to other nodes. */
234 demand_forw = 1;
235 }
236
237 demand_forw = std::min(demand_forw, job[from_id].undelivered_supply);
238
239 scaler.SetDemands(job, from_id, to_id, demand_forw);
240
241 if (scaler.HasDemandLeft(job[to_id])) {
242 demands.push(to_id);
243 } else {
244 num_demands--;
245 }
246
247 if (job[from_id].undelivered_supply == 0) break;
248 }
249
250 if (job[from_id].undelivered_supply != 0) {
251 supplies.push(from_id);
252 } else {
253 num_supplies--;
254 }
255 }
256}
257
262DemandCalculator::DemandCalculator(LinkGraphJob &job) :
263 base_distance(IntSqrt(DistanceMaxPlusManhattan(TileXY(0,0), TileXY(Map::MaxX(), Map::MaxY()))))
264{
265 const LinkGraphSettings &settings = job.Settings();
266 CargoID cargo = job.Cargo();
267
268 this->accuracy = settings.accuracy;
269 this->mod_dist = settings.demand_distance;
270 if (this->mod_dist > 100) {
271 /* Increase effect of mod_dist > 100.
272 * Quadratic:
273 * 100 --> 100
274 * 150 --> 308
275 * 200 --> 933
276 * 255 --> 2102
277 */
278 int over100 = this->mod_dist - 100;
279 this->mod_dist = 100 + ((over100 * over100) / 12);
280 }
281
282 switch (settings.GetDistributionType(cargo)) {
283 case DT_SYMMETRIC:
284 this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
285 break;
286 case DT_ASYMMETRIC:
287 this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
288 break;
289 default:
290 /* Nothing to do. */
291 break;
292 }
293}
CargoID
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
AsymmetricScaler
A scaler for asymmetric distribution.
Definition demands.cpp:87
AsymmetricScaler::AddNode
void AddNode(const Node &)
Nothing to do here.
Definition demands.cpp:93
AsymmetricScaler::EffectiveSupply
uint EffectiveSupply(const Node &from, const Node &)
Get the effective supply of one node towards another one.
Definition demands.cpp:110
AsymmetricScaler::SetDemandPerNode
void SetDemandPerNode(uint)
Nothing to do here.
Definition demands.cpp:101
AsymmetricScaler::HasDemandLeft
bool HasDemandLeft(const Node &to)
Check if there is any acceptance left for this node.
Definition demands.cpp:120
DemandCalculator::CalcDemand
void CalcDemand(LinkGraphJob &job, Tscaler scaler)
Do the actual demand calculation, called from constructor.
Definition demands.cpp:165
DemandCalculator::DemandCalculator
DemandCalculator(LinkGraphJob &job)
Create the DemandCalculator and immediately do the calculation.
Definition demands.cpp:262
DemandCalculator::base_distance
int32_t base_distance
Base distance for scaling purposes.
Definition demands.h:17
DemandCalculator::mod_dist
int32_t mod_dist
Distance modifier, determines how much demands decrease with distance.
Definition demands.h:18
DemandCalculator::accuracy
int32_t accuracy
Accuracy of the calculation.
Definition demands.h:19
LinkGraphJob
Class for calculation jobs to be run on link graphs.
Definition linkgraphjob.h:29
LinkGraphJob::Settings
const LinkGraphSettings & Settings() const
Get the link graph settings for this component.
Definition linkgraphjob.h:232
LinkGraphJob::Size
NodeID Size() const
Get the size of the underlying link graph.
Definition linkgraphjob.h:245
LinkGraphJob::Cargo
CargoID Cargo() const
Get the cargo of the underlying link graph.
Definition linkgraphjob.h:251
NodeList
Hash table based node list multi-container class.
Definition nodelist.hpp:21
Scaler
Scale various things according to symmetric/asymmetric distribution.
Definition demands.cpp:15
Scaler::SetDemands
void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw)
Set the demands between two nodes using the given base demand.
Definition demands.cpp:154
SymmetricScaler
Scaler for symmetric distribution.
Definition demands.cpp:23
SymmetricScaler::mod_size
uint mod_size
Size modifier. Determines how much demands increase with the supply of the remote station.
Definition demands.cpp:79
SymmetricScaler::EffectiveSupply
uint EffectiveSupply(const Node &from, const Node &to)
Get the effective supply of one node towards another one.
Definition demands.cpp:59
SymmetricScaler::demand_per_node
uint demand_per_node
Mean demand associated with each node.
Definition demands.cpp:81
SymmetricScaler::SetDemandPerNode
void SetDemandPerNode(uint num_demands)
Calculate the mean demand per node using the sum of supplies.
Definition demands.cpp:47
SymmetricScaler::AddNode
void AddNode(const Node &node)
Count a node's supply into the sum of supplies.
Definition demands.cpp:38
SymmetricScaler::HasDemandLeft
bool HasDemandLeft(const Node &to)
Check if there is any acceptance left for this node.
Definition demands.cpp:71
SymmetricScaler::SetDemands
void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw)
Set the demands between two nodes using the given base demand.
Definition demands.cpp:131
SymmetricScaler::supply_sum
uint supply_sum
Sum of all supplies in the component.
Definition demands.cpp:80
SymmetricScaler::SymmetricScaler
SymmetricScaler(uint mod_size)
Constructor.
Definition demands.cpp:30
IntSqrt
uint32_t IntSqrt(uint32_t num)
Compute the integer square root.
Definition math_func.cpp:42
demands.h
Declaration of demand calculating link graph handler.
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition fluidsynth.cpp:21
DT_ASYMMETRIC
@ DT_ASYMMETRIC
Asymmetric distribution. Usually cargo will only travel in one direction.
Definition linkgraph_type.h:26
DT_SYMMETRIC
@ DT_SYMMETRIC
Symmetric distribution. The same amount of cargo travels in each direction between each pair of nodes...
Definition linkgraph_type.h:28
DistanceMaxPlusManhattan
uint DistanceMaxPlusManhattan(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles plus the Manhattan distance,...
Definition map.cpp:194
TileXY
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:373
LinkGraphSettings
Definition settings_type.h:542
LinkGraphSettings::accuracy
uint8_t accuracy
accuracy when calculating things on the link graph. low accuracy => low running time
Definition settings_type.h:549
LinkGraphSettings::demand_distance
uint8_t demand_distance
influence of distance between stations on the demand function
Definition settings_type.h:551
LinkGraph::BaseNode
Node of the link graph.
Definition linkgraph.h:90
LinkGraph::BaseNode::supply
uint supply
Supply at the station.
Definition linkgraph.h:91
LinkGraph::BaseNode::demand
uint demand
Acceptance at the station.
Definition linkgraph.h:92
Map
Size related data of the map.
Definition map_func.h:206