OpenTTD Source 20260208-master-g43af8e94d0
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
9
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:
99 inline void AddNode(const Node &)
100 {
101 }
102
106 inline void SetDemandPerNode(uint)
107 {
108 }
109
114 inline uint EffectiveSupply(const Node &from, const Node &)
115 {
116 return from.base.supply;
117 }
118
124 inline bool HasDemandLeft(const Node &to) { return to.base.demand > 0; }
125};
126
135void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
136{
137 if (job[from_id].base.demand > 0) {
138 uint demand_back = demand_forw * this->mod_size / 100;
139 uint undelivered = job[to_id].undelivered_supply;
140 if (demand_back > undelivered) {
141 demand_back = undelivered;
142 demand_forw = std::max(1U, demand_back * 100 / this->mod_size);
143 }
144 this->Scaler::SetDemands(job, to_id, from_id, demand_back);
145 }
146
147 this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
148}
149
158inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
159{
160 job[from_id].DeliverSupply(to_id, demand_forw);
161}
162
168template <class Tscaler>
170{
171 NodeList supplies;
172 NodeList demands;
173 uint num_supplies = 0;
174 uint num_demands = 0;
175
176 for (NodeID node = 0; node < job.Size(); node++) {
177 scaler.AddNode(job[node]);
178 if (job[node].base.supply > 0) {
179 supplies.push(node);
180 num_supplies++;
181 }
182 if (job[node].base.demand > 0) {
183 demands.push(node);
184 num_demands++;
185 }
186 }
187
188 if (num_supplies == 0 || num_demands == 0) return;
189
190 /* Mean acceptance attributed to each node. If the distribution is
191 * symmetric this is relative to remote supply, otherwise it is
192 * relative to remote demand. */
193 scaler.SetDemandPerNode(num_demands);
194 uint chance = 0;
195
196 while (!supplies.empty() && !demands.empty()) {
197 NodeID from_id = supplies.front();
198 supplies.pop();
199
200 for (uint i = 0; i < num_demands; ++i) {
201 assert(!demands.empty());
202 NodeID to_id = demands.front();
203 demands.pop();
204 if (from_id == to_id) {
205 /* Only one node with supply and demand left */
206 if (demands.empty() && supplies.empty()) return;
207
208 demands.push(to_id);
209 continue;
210 }
211
212 int32_t supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
213 assert(supply > 0);
214
215 constexpr int32_t divisor_scale = 16;
216
217 int32_t scaled_distance = this->base_distance;
218 if (this->mod_dist > 0) {
219 const int32_t distance = DistanceMaxPlusManhattan(job[from_id].base.xy, job[to_id].base.xy);
220 /* Scale distance around base_distance by (mod_dist * (100 / 1024)).
221 * mod_dist may be > 1024, so clamp result to be non-negative */
222 scaled_distance = std::max(0, this->base_distance + (((distance - this->base_distance) * this->mod_dist) / 1024));
223 }
224
225 /* Scale the accuracy by distance around accuracy / 2 */
226 const int32_t divisor = divisor_scale + ((this->accuracy * scaled_distance * divisor_scale) / (this->base_distance * 2));
227 assert(divisor >= divisor_scale);
228
229 uint demand_forw = 0;
230 if (divisor <= (supply * divisor_scale)) {
231 /* At first only distribute demand if
232 * effective supply / accuracy divisor >= 1
233 * Others are too small or too far away to be considered. */
234 demand_forw = (supply * divisor_scale) / divisor;
235 } else if (++chance > this->accuracy * num_demands * num_supplies) {
236 /* After some trying, if there is still supply left, distribute
237 * demand also to other nodes. */
238 demand_forw = 1;
239 }
240
241 demand_forw = std::min(demand_forw, job[from_id].undelivered_supply);
242
243 scaler.SetDemands(job, from_id, to_id, demand_forw);
244
245 if (scaler.HasDemandLeft(job[to_id])) {
246 demands.push(to_id);
247 } else {
248 num_demands--;
249 }
250
251 if (job[from_id].undelivered_supply == 0) break;
252 }
253
254 if (job[from_id].undelivered_supply != 0) {
255 supplies.push(from_id);
256 } else {
257 num_supplies--;
258 }
259 }
260}
261
268{
269 const LinkGraphSettings &settings = job.Settings();
270 CargoType cargo = job.Cargo();
271
272 this->accuracy = settings.accuracy;
273 this->mod_dist = settings.demand_distance;
274 if (this->mod_dist > 100) {
275 /* Increase effect of mod_dist > 100.
276 * Quadratic:
277 * 100 --> 100
278 * 150 --> 308
279 * 200 --> 933
280 * 255 --> 2102
281 */
282 int over100 = this->mod_dist - 100;
283 this->mod_dist = 100 + ((over100 * over100) / 12);
284 }
285
286 switch (settings.GetDistributionType(cargo)) {
287 case DT_SYMMETRIC:
288 this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
289 break;
290 case DT_ASYMMETRIC:
291 this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
292 break;
293 default:
294 /* Nothing to do. */
295 break;
296 }
297}
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:99
uint EffectiveSupply(const Node &from, const Node &)
Get the effective supply of one node towards another one.
Definition demands.cpp:114
void SetDemandPerNode(uint)
Nothing to do here.
Definition demands.cpp:106
bool HasDemandLeft(const Node &to)
Check if there is any acceptance left for this node.
Definition demands.cpp:124
void CalcDemand(LinkGraphJob &job, Tscaler scaler)
Do the actual demand calculation, called from constructor.
Definition demands.cpp:169
DemandCalculator(LinkGraphJob &job)
Create the DemandCalculator and immediately do the calculation.
Definition demands.cpp:266
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:158
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:135
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.
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:375
Integer math functions.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
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:196