OpenTTD Source 20260218-master-g2123fca5ea
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
115 inline uint EffectiveSupply(const Node &from, const Node &)
116 {
117 return from.base.supply;
118 }
119
126 inline bool HasDemandLeft(const Node &to) { return to.base.demand > 0; }
127};
128
137void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
138{
139 if (job[from_id].base.demand > 0) {
140 uint demand_back = demand_forw * this->mod_size / 100;
141 uint undelivered = job[to_id].undelivered_supply;
142 if (demand_back > undelivered) {
143 demand_back = undelivered;
144 demand_forw = std::max(1U, demand_back * 100 / this->mod_size);
145 }
146 this->Scaler::SetDemands(job, to_id, from_id, demand_back);
147 }
148
149 this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
150}
151
160inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
161{
162 job[from_id].DeliverSupply(to_id, demand_forw);
163}
164
170template <class Tscaler>
172{
173 NodeList supplies;
174 NodeList demands;
175 uint num_supplies = 0;
176 uint num_demands = 0;
177
178 for (NodeID node = 0; node < job.Size(); node++) {
179 scaler.AddNode(job[node]);
180 if (job[node].base.supply > 0) {
181 supplies.push(node);
182 num_supplies++;
183 }
184 if (job[node].base.demand > 0) {
185 demands.push(node);
186 num_demands++;
187 }
188 }
189
190 if (num_supplies == 0 || num_demands == 0) return;
191
192 /* Mean acceptance attributed to each node. If the distribution is
193 * symmetric this is relative to remote supply, otherwise it is
194 * relative to remote demand. */
195 scaler.SetDemandPerNode(num_demands);
196 uint chance = 0;
197
198 while (!supplies.empty() && !demands.empty()) {
199 NodeID from_id = supplies.front();
200 supplies.pop();
201
202 for (uint i = 0; i < num_demands; ++i) {
203 assert(!demands.empty());
204 NodeID to_id = demands.front();
205 demands.pop();
206 if (from_id == to_id) {
207 /* Only one node with supply and demand left */
208 if (demands.empty() && supplies.empty()) return;
209
210 demands.push(to_id);
211 continue;
212 }
213
214 int32_t supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
215 assert(supply > 0);
216
217 constexpr int32_t divisor_scale = 16;
218
219 int32_t scaled_distance = this->base_distance;
220 if (this->mod_dist > 0) {
221 const int32_t distance = DistanceMaxPlusManhattan(job[from_id].base.xy, job[to_id].base.xy);
222 /* Scale distance around base_distance by (mod_dist * (100 / 1024)).
223 * mod_dist may be > 1024, so clamp result to be non-negative */
224 scaled_distance = std::max(0, this->base_distance + (((distance - this->base_distance) * this->mod_dist) / 1024));
225 }
226
227 /* Scale the accuracy by distance around accuracy / 2 */
228 const int32_t divisor = divisor_scale + ((this->accuracy * scaled_distance * divisor_scale) / (this->base_distance * 2));
229 assert(divisor >= divisor_scale);
230
231 uint demand_forw = 0;
232 if (divisor <= (supply * divisor_scale)) {
233 /* At first only distribute demand if
234 * effective supply / accuracy divisor >= 1
235 * Others are too small or too far away to be considered. */
236 demand_forw = (supply * divisor_scale) / divisor;
237 } else if (++chance > this->accuracy * num_demands * num_supplies) {
238 /* After some trying, if there is still supply left, distribute
239 * demand also to other nodes. */
240 demand_forw = 1;
241 }
242
243 demand_forw = std::min(demand_forw, job[from_id].undelivered_supply);
244
245 scaler.SetDemands(job, from_id, to_id, demand_forw);
246
247 if (scaler.HasDemandLeft(job[to_id])) {
248 demands.push(to_id);
249 } else {
250 num_demands--;
251 }
252
253 if (job[from_id].undelivered_supply == 0) break;
254 }
255
256 if (job[from_id].undelivered_supply != 0) {
257 supplies.push(from_id);
258 } else {
259 num_supplies--;
260 }
261 }
262}
263
270{
271 const LinkGraphSettings &settings = job.Settings();
272 CargoType cargo = job.Cargo();
273
274 this->accuracy = settings.accuracy;
275 this->mod_dist = settings.demand_distance;
276 if (this->mod_dist > 100) {
277 /* Increase effect of mod_dist > 100.
278 * Quadratic:
279 * 100 --> 100
280 * 150 --> 308
281 * 200 --> 933
282 * 255 --> 2102
283 */
284 int over100 = this->mod_dist - 100;
285 this->mod_dist = 100 + ((over100 * over100) / 12);
286 }
287
288 switch (settings.GetDistributionType(cargo)) {
289 case DT_SYMMETRIC:
290 this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
291 break;
292 case DT_ASYMMETRIC:
293 this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
294 break;
295 default:
296 /* Nothing to do. */
297 break;
298 }
299}
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:115
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:126
void CalcDemand(LinkGraphJob &job, Tscaler scaler)
Do the actual demand calculation, called from constructor.
Definition demands.cpp:171
DemandCalculator(LinkGraphJob &job)
Create the DemandCalculator and immediately do the calculation.
Definition demands.cpp:268
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:160
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:137
uint supply_sum
Sum of all supplies in the component.
Definition demands.cpp:87
SymmetricScaler(uint mod_size)
Constructor.
Definition demands.cpp:37
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:378
uint32_t IntSqrt(uint32_t num)
Compute the integer square root.
Definition math_func.cpp:42
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:95
uint demand
Acceptance at the station.
Definition linkgraph.h:96
Size related data of the map.
Definition map_func.h:198