OpenTTD Source 20260421-master-gc2fbc6fdeb
yapf_base.hpp
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#ifndef YAPF_BASE_HPP
11#define YAPF_BASE_HPP
12
13#include "../../debug.h"
14#include "../../settings_type.h"
16#include "yapf_type.hpp"
17
47template <class Types>
49public:
50 typedef typename Types::Tpf Tpf;
51 typedef typename Types::TrackFollower TrackFollower;
52 typedef typename Types::NodeList NodeList;
53 typedef typename Types::VehicleType VehicleType;
54 typedef typename NodeList::Item Node;
55 typedef typename Node::Key Key;
56
58
65 using PfFollowNodeFunc = void(Node &old_node);
66
75 using PfCalcCostFunc = bool(Node &n, const TrackFollower *follower);
76
83 using PfCalcEstimateFunc = bool(Node &n);
84
90 using PfDetectDestinationFunc = bool(Node &n);
91
99
104 using TransportTypeCharFunc = char();
105
106protected:
107 Node *best_dest_node = nullptr;
111 const VehicleType *vehicle = nullptr;
112
115
116public:
117 int num_steps = 0;
118
119public:
122
125
126protected:
131 inline Tpf &Yapf()
132 {
133 return *static_cast<Tpf *>(this);
134 }
135
136public:
141 inline const YAPFSettings &PfGetSettings() const
142 {
143 return *this->settings;
144 }
145
156 inline bool FindPath(const VehicleType *v)
157 {
158 this->vehicle = v;
159
160 for (;;) {
161 this->num_steps++;
162 Node *best_open_node = this->nodes.GetBestOpenNode();
163 if (best_open_node == nullptr) break;
164
165 if (Yapf().PfDetectDestination(*best_open_node)) {
166 this->best_dest_node = best_open_node;
167 break;
168 }
169
170 Yapf().PfFollowNode(*best_open_node);
171 if (this->max_search_nodes != 0 && this->nodes.ClosedCount() >= this->max_search_nodes) break;
172
173 this->nodes.PopOpenNode(best_open_node->GetKey());
174 this->nodes.InsertClosedNode(*best_open_node);
175 }
176
177 const bool destination_found = (this->best_dest_node != nullptr);
178
179 if (_debug_yapf_level >= 3) {
180 const UnitID veh_idx = (this->vehicle != nullptr) ? this->vehicle->unitnumber : 0;
181 const char ttc = Yapf().TransportTypeChar();
182 const float cache_hit_ratio = (this->stats_cache_hits == 0) ? 0.0f : ((float)this->stats_cache_hits / (float)(this->stats_cache_hits + this->stats_cost_calcs) * 100.0f);
183 const int cost = destination_found ? this->best_dest_node->cost : -1;
184 const int dist = destination_found ? this->best_dest_node->estimate - this->best_dest_node->cost : -1;
185
186 Debug(yapf, 3, "[YAPF{}]{}{:4d} - {} rounds - {} open - {} closed - CHR {:4.1f}% - C {} D {}",
187 ttc, destination_found ? '-' : '!', veh_idx, this->num_steps, this->nodes.OpenCount(), this->nodes.ClosedCount(), cache_hit_ratio, cost, dist
188 );
189 }
190
191 return destination_found;
192 }
193
200 {
201 return (this->best_dest_node != nullptr) ? this->best_dest_node : this->best_intermediate_node;
202 }
203
210 {
211 Node &node = this->nodes.CreateNewNode();
212 return node;
213 }
214
219 inline void AddStartupNode(Node &n)
220 {
221 assert(n.parent == nullptr);
222 assert(this->num_steps == 0);
223
224 Yapf().PfNodeCacheFetch(n);
225 /* insert the new node only if it is not there */
226 if (this->nodes.FindOpenNode(n.key) == nullptr) {
227 this->nodes.InsertOpenNode(n);
228 }
229 }
230
236 inline void AddMultipleNodes(Node *parent, const TrackFollower &tf)
237 {
238 bool is_choice = (KillFirstBit(tf.new_td_bits) != TRACKDIR_BIT_NONE);
239 for (TrackdirBits rtds = tf.new_td_bits; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
240 Trackdir td = (Trackdir)FindFirstBit(rtds);
241 Node &n = Yapf().CreateNewNode();
242 n.Set(parent, tf.new_tile, td, is_choice);
243 Yapf().AddNewNode(n, tf);
244 }
245 }
246
253 void AddNewNode(Node &n, const TrackFollower &follower)
254 {
255 assert(n.parent != nullptr);
256
257 /* evaluate the node */
258 bool cached = Yapf().PfNodeCacheFetch(n);
259 if (!cached) {
260 this->stats_cost_calcs++;
261 } else {
262 this->stats_cache_hits++;
263 }
264
265 bool valid = Yapf().PfCalcCost(n, &follower);
266
267 if (valid) valid = Yapf().PfCalcEstimate(n);
268
269 /* have the cost or estimate callbacks marked this node as invalid? */
270 if (!valid) return;
271
272 /* The new node can be set as the best intermediate node only once we're
273 * certain it will be finalized by being inserted into the open list. */
274 bool set_intermediate = this->max_search_nodes > 0 && (this->best_intermediate_node == nullptr || (this->best_intermediate_node->GetCostEstimate() - this->best_intermediate_node->GetCost()) > (n.GetCostEstimate() - n.GetCost()));
275
276 /* check new node against open list */
277 Node *open_node = this->nodes.FindOpenNode(n.GetKey());
278 if (open_node != nullptr) {
279 /* another node exists with the same key in the open list
280 * is it better than new one? */
281 if (n.GetCostEstimate() < open_node->GetCostEstimate()) {
282 /* update the old node by value from new one */
283 this->nodes.PopOpenNode(n.GetKey());
284 *open_node = n;
285 /* add the updated old node back to open list */
286 this->nodes.InsertOpenNode(*open_node);
287 if (set_intermediate) this->best_intermediate_node = open_node;
288 }
289 return;
290 }
291
292 /* check new node against closed list */
293 Node *closed_node = this->nodes.FindClosedNode(n.GetKey());
294 if (closed_node != nullptr) {
295 /* another node exists with the same key in the closed list
296 * is it better than new one? */
297 int node_est = n.GetCostEstimate();
298 int closed_est = closed_node->GetCostEstimate();
299 if (node_est < closed_est) {
300 /* If this assert occurs, you have probably problem in
301 * your Tderived::PfCalcCost() or Tderived::PfCalcEstimate().
302 * The problem could be:
303 * - PfCalcEstimate() gives too large numbers
304 * - PfCalcCost() gives too small numbers
305 * - You have used negative cost penalty in some cases (cost bonus) */
306 NOT_REACHED();
307 }
308 return;
309 }
310 /* the new node is really new
311 * add it to the open list */
312 this->nodes.InsertOpenNode(n);
313 if (set_intermediate) this->best_intermediate_node = &n;
314 }
315
316 const VehicleType * GetVehicle() const
317 {
318 return this->vehicle;
319 }
320
321 void DumpBase(DumpTarget &dmp) const
322 {
323 dmp.WriteStructT("nodes", &this->nodes);
324 dmp.WriteValue("num_steps", this->num_steps);
325 }
326};
327
328#endif /* YAPF_BASE_HPP */
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
int num_steps
this is there for debugging purposes (hope it doesn't hurt)
void(Node &old_node) PfFollowNodeFunc
Called by YAPF to move from the given node to the next tile.
Definition yapf_base.hpp:65
Node::Key Key
key to hash tables
Definition yapf_base.hpp:55
void AddNewNode(Node &n, const TrackFollower &follower)
AddNewNode() - called by Tderived::PfFollowNode() for each child node.
CYapfBaseT()
default constructor
const VehicleType * vehicle
vehicle that we are trying to drive
Node * best_intermediate_node
here should be node closest to the destination if path not found
char() TransportTypeCharFunc
Return debug report character to identify the transportation type.
const YAPFSettings * settings
current settings (_settings_game.yapf)
int stats_cache_hits
stats - how many node's costs were reused from cache
void AddMultipleNodes(Node *parent, const TrackFollower &tf)
Add multiple nodes - direct children of the given node.
int stats_cost_calcs
stats - how many node's costs were calculated
int max_search_nodes
maximum number of nodes we are allowed to visit before we give up
bool(TileIndex tile, Trackdir td) PfDetectDestinationTileFunc
Called by YAPF to detect if node ends in the desired destination.
Definition yapf_base.hpp:98
bool(Node &n) PfDetectDestinationFunc
Called by YAPF to detect if node ends in the desired destination.
Definition yapf_base.hpp:90
bool(Node &n, const TrackFollower *follower) PfCalcCostFunc
Called by YAPF to calculate the cost from the origin to the given node.
Definition yapf_base.hpp:75
Node * GetBestNode()
If path was found return the best node that has reached the destination.
~CYapfBaseT()
default destructor
bool FindPath(const VehicleType *v)
Main pathfinder routine:
bool(Node &n) PfCalcEstimateFunc
Called by YAPF to calculate cost estimate.
Definition yapf_base.hpp:83
Node & CreateNewNode()
Calls NodeList::CreateNewNode() - allocates new node that can be filled and used as argument for AddS...
NodeList nodes
node list multi-container
Definition yapf_base.hpp:57
Types::Tpf Tpf
the pathfinder class (derived from THIS class)
Definition yapf_base.hpp:50
const YAPFSettings & PfGetSettings() const
Return current settings (can be custom - company based - but later).
Types::VehicleType VehicleType
the type of vehicle
Definition yapf_base.hpp:53
void AddStartupNode(Node &n)
Add new node (created by CreateNewNode and filled with data) into open list.
Node * best_dest_node
pointer to the destination node found at last round
NodeList::Item Node
this will be our node type
Definition yapf_base.hpp:54
Types::NodeList NodeList
our node list
Definition yapf_base.hpp:52
Tpf & Yapf()
Access the inherited path finder.
void InsertClosedNode(Titem &item)
Insert the given item into the closed nodes set.
Definition nodelist.hpp:157
Titem * FindClosedNode(const Key &key)
Find a closed node by its key.
Definition nodelist.hpp:168
void InsertOpenNode(Titem &item)
Insert given item as open node (into open_nodes and open_queue).
Definition nodelist.hpp:94
int OpenCount()
Get open node count.
Definition nodelist.hpp:44
int ClosedCount()
Get closed node count.
Definition nodelist.hpp:53
Titem & CreateNewNode()
Allocate new data item from items.
Definition nodelist.hpp:71
Titem * GetBestOpenNode()
Get the open node at the begin of the open queue.
Definition nodelist.hpp:108
Titem & PopOpenNode(const Key &key)
Find and remove an open node by key.
Definition nodelist.hpp:145
Titem * FindOpenNode(const Key &key)
Find an open node by key.
Definition nodelist.hpp:135
Functions to be used for debug printings.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Types related to global configuration settings.
void WriteStructT(std::string_view name, const S *s)
Dump nested object (or only its name if this instance is already known).
void WriteValue(std::string_view name, const auto &value)
Write 'name = value' with indent and new-line.
Settings related to the yet another pathfinder.
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:97
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
uint16_t UnitID
Type for the company global vehicle unit number.
VehicleType
Available vehicle types.
Types used by YAPF.