OpenTTD Source 20260109-master-g241b5fcdfe
demands.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "demands.h"
12#include "../core/math_func.hpp"
13#include <queue>
14
15#include "../safeguards.h"
16
17typedef std::queue<NodeID> NodeList;
18
22class Scaler {
23public:
24 void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
25};
26
30class SymmetricScaler : public Scaler {
31public:
40
45 inline void AddNode(const Node &node)
46 {
47 this->supply_sum += node.base.supply;
48 }
49
54 inline void SetDemandPerNode(uint num_demands)
55 {
56 this->demand_per_node = std::max(this->supply_sum / num_demands, 1U);
57 }
58
66 inline uint EffectiveSupply(const Node &from, const Node &to)
67 {
68 return std::max(from.base.supply * std::max(1U, to.base.supply) * this->mod_size / 100 / this->demand_per_node, 1U);
69 }
70
78 inline bool HasDemandLeft(const Node &to)
79 {
80 return (to.base.supply == 0 || to.undelivered_supply > 0) && to.base.demand > 0;
81 }
82
83 void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
84
85private:
86 uint mod_size;
89};
90
94class AsymmetricScaler : public Scaler {
95public:
100 inline void AddNode(const Node &)
101 {
102 }
103
108 inline void SetDemandPerNode(uint)
109 {
110 }
111
117 inline uint EffectiveSupply(const Node &from, const Node &)
118 {
119 return from.base.supply;
120 }
121
127 inline bool HasDemandLeft(const Node &to) { return to.base.demand > 0; }
128};
129
138void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
139{
140 if (job[from_id].base.demand > 0) {
141 uint demand_back = demand_forw * this->mod_size / 100;
142 uint undelivered = job[to_id].undelivered_supply;
143 if (demand_back > undelivered) {
144 demand_back = undelivered;
145 demand_forw = std::max(1U, demand_back * 100 / this->mod_size);
146 }
147 this->Scaler::SetDemands(job, to_id, from_id, demand_back);
148 }
149
150 this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
151}
152
161inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
162{
163 job[from_id].DeliverSupply(to_id, demand_forw);
164}
165
171template <class Tscaler>
173{
174 NodeList supplies;
175 NodeList demands;
176 uint num_supplies = 0;
177 uint num_demands = 0;
178
179 for (NodeID node = 0; node < job.Size(); node++) {
180 scaler.AddNode(job[node]);
181 if (job[node].base.supply > 0) {
182 supplies.push(node);
183 num_supplies++;
184 }
185 if (job[node].base.demand > 0) {
186 demands.push(node);
187 num_demands++;
188 }
189 }
190
191 if (num_supplies == 0 || num_demands == 0) return;
192
193 /* Mean acceptance attributed to each node. If the distribution is
194 * symmetric this is relative to remote supply, otherwise it is
195 * relative to remote demand. */
196 scaler.SetDemandPerNode(num_demands);
197 uint chance = 0;
198
199 while (!supplies.empty() && !demands.empty()) {
200 NodeID from_id = supplies.front();
201 supplies.pop();
202
203 for (uint i = 0; i < num_demands; ++i) {
204 assert(!demands.empty());
205 NodeID to_id = demands.front();
206 demands.pop();
207 if (from_id == to_id) {
208 /* Only one node with supply and demand left */
209 if (demands.empty() && supplies.empty()) return;
210
211 demands.push(to_id);
212 continue;
213 }
214
215 int32_t supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
216 assert(supply > 0);
217
218 constexpr int32_t divisor_scale = 16;
219
220 int32_t scaled_distance = this->base_distance;
221 if (this->mod_dist > 0) {
222 const int32_t distance = DistanceMaxPlusManhattan(job[from_id].base.xy, job[to_id].base.xy);
223 /* Scale distance around base_distance by (mod_dist * (100 / 1024)).
224 * mod_dist may be > 1024, so clamp result to be non-negative */
225 scaled_distance = std::max(0, this->base_distance + (((distance - this->base_distance) * this->mod_dist) / 1024));
226 }
227
228 /* Scale the accuracy by distance around accuracy / 2 */
229 const int32_t divisor = divisor_scale + ((this->accuracy * scaled_distance * divisor_scale) / (this->base_distance * 2));
230 assert(divisor >= divisor_scale);
231
232 uint demand_forw = 0;
233 if (divisor <= (supply * divisor_scale)) {
234 /* At first only distribute demand if
235 * effective supply / accuracy divisor >= 1
236 * Others are too small or too far away to be considered. */
237 demand_forw = (supply * divisor_scale) / divisor;
238 } else if (++chance > this->accuracy * num_demands * num_supplies) {
239 /* After some trying, if there is still supply left, distribute
240 * demand also to other nodes. */
241 demand_forw = 1;
242 }
243
244 demand_forw = std::min(demand_forw, job[from_id].undelivered_supply);
245
246 scaler.SetDemands(job, from_id, to_id, demand_forw);
247
248 if (scaler.HasDemandLeft(job[to_id])) {
249 demands.push(to_id);
250 } else {
251 num_demands--;
252 }
253
254 if (job[from_id].undelivered_supply == 0) break;
255 }
256
257 if (job[from_id].undelivered_supply != 0) {
258 supplies.push(from_id);
259 } else {
260 num_supplies--;
261 }
262 }
263}
264
270 base_distance(IntSqrt(DistanceMaxPlusManhattan(TileXY(0,0), TileXY(Map::MaxX(), Map::MaxY()))))
271{
272 const LinkGraphSettings &settings = job.Settings();
273 CargoType cargo = job.Cargo();
274
275 this->accuracy = settings.accuracy;
276 this->mod_dist = settings.demand_distance;
277 if (this->mod_dist > 100) {
278 /* Increase effect of mod_dist > 100.
279 * Quadratic:
280 * 100 --> 100
281 * 150 --> 308
282 * 200 --> 933
283 * 255 --> 2102
284 */
285 int over100 = this->mod_dist - 100;
286 this->mod_dist = 100 + ((over100 * over100) / 12);
287 }
288
289 switch (settings.GetDistributionType(cargo)) {
290 case DT_SYMMETRIC:
291 this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
292 break;
293 case DT_ASYMMETRIC:
294 this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
295 break;
296 default:
297 /* Nothing to do. */
298 break;
299 }
300}
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
A scaler for asymmetric distribution.
Definition demands.cpp:94
void AddNode(const Node &)
Nothing to do here.
Definition demands.cpp:100
uint EffectiveSupply(const Node &from, const Node &)
Get the effective supply of one node towards another one.
Definition demands.cpp:117
void SetDemandPerNode(uint)
Nothing to do here.
Definition demands.cpp:108
bool HasDemandLeft(const Node &to)
Check if there is any acceptance left for this node.
Definition demands.cpp:127
void CalcDemand(LinkGraphJob &job, Tscaler scaler)
Do the actual demand calculation, called from constructor.
Definition demands.cpp:172
DemandCalculator(LinkGraphJob &job)
Create the DemandCalculator and immediately do the calculation.
Definition demands.cpp:269
int32_t base_distance
Base distance for scaling purposes.
Definition demands.h:24
int32_t mod_dist
Distance modifier, determines how much demands decrease with distance.
Definition demands.h:25
int32_t accuracy
Accuracy of the calculation.
Definition demands.h:26
Class for calculation jobs to be run on link graphs.
const LinkGraphSettings & Settings() const
Get the link graph settings for this component.
CargoType Cargo() const
Get the cargo of the underlying link graph.
NodeID Size() const
Get the size of the underlying link graph.
Hash table based node list multi-container class.
Definition nodelist.hpp:21
Scale various things according to symmetric/asymmetric distribution.
Definition demands.cpp:22
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:161
Scaler for symmetric distribution.
Definition demands.cpp:30
uint mod_size
Size modifier. Determines how much demands increase with the supply of the remote station.
Definition demands.cpp:86
uint EffectiveSupply(const Node &from, const Node &to)
Get the effective supply of one node towards another one.
Definition demands.cpp:66
uint demand_per_node
Mean demand associated with each node.
Definition demands.cpp:88
void SetDemandPerNode(uint num_demands)
Calculate the mean demand per node using the sum of supplies.
Definition demands.cpp:54
void AddNode(const Node &node)
Count a node's supply into the sum of supplies.
Definition demands.cpp:45
bool HasDemandLeft(const Node &to)
Check if there is any acceptance left for this node.
Definition demands.cpp:78
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:138
uint supply_sum
Sum of all supplies in the component.
Definition demands.cpp:87
SymmetricScaler(uint mod_size)
Constructor.
Definition demands.cpp:37
uint32_t IntSqrt(uint32_t num)
Compute the integer square root.
Definition math_func.cpp:42
Declaration of demand calculating link graph handler.
fluid_settings_t * settings
FluidSynth settings handle.
@ DT_ASYMMETRIC
Asymmetric distribution. Usually cargo will only travel in one direction.
@ DT_SYMMETRIC
Symmetric distribution. The same amount of cargo travels in each direction between each pair of nodes...
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:206
static TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:385
uint8_t accuracy
accuracy when calculating things on the link graph. low accuracy => low running time
uint8_t demand_distance
influence of distance between stations on the demand function
Node of the link graph.
Definition linkgraph.h:90
uint supply
Supply at the station.
Definition linkgraph.h:91
uint demand
Acceptance at the station.
Definition linkgraph.h:92
Size related data of the map.
Definition map_func.h:206