OpenTTD Source: src/pathfinder/yapf/yapf_base.hpp Source File

OpenTTD Source 1.11.0-RC1

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 <http://www.gnu.org/licenses/>.
6 */
7
10 #ifndef YAPF_BASE_HPP
11 #define YAPF_BASE_HPP
12
13 #include "../../debug.h"
14 #include "../../settings_type.h"
15
16 extern int _total_pf_time_us;
17
48 template <class Types>
49 class CYapfBaseT {
50 public:
51 typedef typename Types::Tpf Tpf;
52 typedef typename Types::TrackFollower TrackFollower;
53 typedef typename Types::NodeList NodeList;
54 typedef typename Types::VehicleType VehicleType;
55 typedef typename NodeList::Titem Node;
56 typedef typename Node::Key Key;
57
58
59 NodeList m_nodes;
60 protected:
61 Node *m_pBestDestNode;
62 Node *m_pBestIntermediateNode;
63 const YAPFSettings *m_settings;
64 int m_max_search_nodes;
65 const VehicleType *m_veh;
66
67 int m_stats_cost_calcs;
68 int m_stats_cache_hits;
69
70 public:
71 CPerformanceTimer m_perf_cost;
72 CPerformanceTimer m_perf_slope_cost;
73 CPerformanceTimer m_perf_ts_cost;
74 CPerformanceTimer m_perf_other_cost;
75
76 public:
77 int m_num_steps;
78
79 public:
81 inline CYapfBaseT()
82 : m_pBestDestNode(nullptr)
83 , m_pBestIntermediateNode(nullptr)
84 , m_settings(&_settings_game.pf.yapf)
85 , m_max_search_nodes(PfGetSettings().max_search_nodes)
86 , m_veh(nullptr)
87 , m_stats_cost_calcs(0)
88 , m_stats_cache_hits(0)
89 , m_num_steps(0)
90 {
91 }
92
94 ~CYapfBaseT() {}
95
96 protected:
98 inline Tpf& Yapf()
99 {
100 return *static_cast<Tpf *>(this);
101 }
102
103 public:
105 inline const YAPFSettings& PfGetSettings() const
106 {
107 return *m_settings;
108 }
109
119 inline bool FindPath(const VehicleType *v)
120 {
121 m_veh = v;
122
123 CPerformanceTimer perf;
124 perf.Start();
125
126 Yapf().PfSetStartupNodes();
127 bool bDestFound = true;
128
129 for (;;) {
130 m_num_steps++;
131 Node *n = m_nodes.GetBestOpenNode();
132 if (n == nullptr) {
133 break;
134 }
135
136 /* if the best open node was worse than the best path found, we can finish */
137 if (m_pBestDestNode != nullptr && m_pBestDestNode->GetCost() < n->GetCostEstimate()) {
138 break;
139 }
140
141 Yapf().PfFollowNode(*n);
142 if (m_max_search_nodes == 0 || m_nodes.ClosedCount() < m_max_search_nodes) {
143 m_nodes.PopOpenNode(n->GetKey());
144 m_nodes.InsertClosedNode(*n);
145 } else {
146 bDestFound = false;
147 break;
148 }
149 }
150
151 bDestFound &= (m_pBestDestNode != nullptr);
152
153 perf.Stop();
154 if (_debug_yapf_level >= 2) {
155 int t = perf.Get(1000000);
156 _total_pf_time_us += t;
157
158 if (_debug_yapf_level >= 3) {
159 UnitID veh_idx = (m_veh != nullptr) ? m_veh->unitnumber : 0;
160 char ttc = Yapf().TransportTypeChar();
161 float cache_hit_ratio = (m_stats_cache_hits == 0) ? 0.0f : ((float)m_stats_cache_hits / (float)(m_stats_cache_hits + m_stats_cost_calcs) * 100.0f);
162 int cost = bDestFound ? m_pBestDestNode->m_cost : -1;
163 int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1;
164
165 DEBUG(yapf, 3, "[YAPF%c]%c%4d- %d us - %d rounds - %d open - %d closed - CHR %4.1f%% - C %d D %d - c%d(sc%d, ts%d, o%d) -- ",
166 ttc, bDestFound ? '-' : '!', veh_idx, t, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(),
167 cache_hit_ratio, cost, dist, m_perf_cost.Get(1000000), m_perf_slope_cost.Get(1000000),
168 m_perf_ts_cost.Get(1000000), m_perf_other_cost.Get(1000000)
169 );
170 }
171 }
172 return bDestFound;
173 }
174
179 inline Node *GetBestNode()
180 {
181 return (m_pBestDestNode != nullptr) ? m_pBestDestNode : m_pBestIntermediateNode;
182 }
183
188 inline Node& CreateNewNode()
189 {
190 Node &node = *m_nodes.CreateNewNode();
191 return node;
192 }
193
195 inline void AddStartupNode(Node &n)
196 {
197 Yapf().PfNodeCacheFetch(n);
198 /* insert the new node only if it is not there */
199 if (m_nodes.FindOpenNode(n.m_key) == nullptr) {
200 m_nodes.InsertOpenNode(n);
201 } else {
202 /* if we are here, it means that node is already there - how it is possible?
203 * probably the train is in the position that both its ends point to the same tile/exit-dir
204 * very unlikely, but it happened */
205 }
206 }
207
209 inline void AddMultipleNodes(Node *parent, const TrackFollower &tf)
210 {
211 bool is_choice = (KillFirstBit(tf.m_new_td_bits) != TRACKDIR_BIT_NONE);
212 for (TrackdirBits rtds = tf.m_new_td_bits; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
213 Trackdir td = (Trackdir)FindFirstBit2x64(rtds);
214 Node &n = Yapf().CreateNewNode();
215 n.Set(parent, tf.m_new_tile, td, is_choice);
216 Yapf().AddNewNode(n, tf);
217 }
218 }
219
228 void PruneIntermediateNodeBranch()
229 {
230 while (Yapf().m_pBestIntermediateNode != nullptr && (Yapf().m_pBestIntermediateNode->m_segment->m_end_segment_reason & ESRB_CHOICE_FOLLOWS) == 0) {
231 Yapf().m_pBestIntermediateNode = Yapf().m_pBestIntermediateNode->m_parent;
232 }
233 }
234
239 void AddNewNode(Node &n, const TrackFollower &tf)
240 {
241 /* evaluate the node */
242 bool bCached = Yapf().PfNodeCacheFetch(n);
243 if (!bCached) {
244 m_stats_cost_calcs++;
245 } else {
246 m_stats_cache_hits++;
247 }
248
249 bool bValid = Yapf().PfCalcCost(n, &tf);
250
251 if (bCached) {
252 Yapf().PfNodeCacheFlush(n);
253 }
254
255 if (bValid) bValid = Yapf().PfCalcEstimate(n);
256
257 /* have the cost or estimate callbacks marked this node as invalid? */
258 if (!bValid) return;
259
260 /* detect the destination */
261 bool bDestination = Yapf().PfDetectDestination(n);
262 if (bDestination) {
263 if (m_pBestDestNode == nullptr || n < *m_pBestDestNode) {
264 m_pBestDestNode = &n;
265 }
266 m_nodes.FoundBestNode(n);
267 return;
268 }
269
270 if (m_max_search_nodes > 0 && (m_pBestIntermediateNode == nullptr || (m_pBestIntermediateNode->GetCostEstimate() - m_pBestIntermediateNode->GetCost()) > (n.GetCostEstimate() - n.GetCost()))) {
271 m_pBestIntermediateNode = &n;
272 }
273
274 /* check new node against open list */
275 Node *openNode = m_nodes.FindOpenNode(n.GetKey());
276 if (openNode != nullptr) {
277 /* another node exists with the same key in the open list
278 * is it better than new one? */
279 if (n.GetCostEstimate() < openNode->GetCostEstimate()) {
280 /* update the old node by value from new one */
281 m_nodes.PopOpenNode(n.GetKey());
282 *openNode = n;
283 /* add the updated old node back to open list */
284 m_nodes.InsertOpenNode(*openNode);
285 }
286 return;
287 }
288
289 /* check new node against closed list */
290 Node *closedNode = m_nodes.FindClosedNode(n.GetKey());
291 if (closedNode != nullptr) {
292 /* another node exists with the same key in the closed list
293 * is it better than new one? */
294 int node_est = n.GetCostEstimate();
295 int closed_est = closedNode->GetCostEstimate();
296 if (node_est < closed_est) {
297 /* If this assert occurs, you have probably problem in
298 * your Tderived::PfCalcCost() or Tderived::PfCalcEstimate().
299 * The problem could be:
300 * - PfCalcEstimate() gives too large numbers
301 * - PfCalcCost() gives too small numbers
302 * - You have used negative cost penalty in some cases (cost bonus) */
303 NOT_REACHED();
304 }
305 return;
306 }
307 /* the new node is really new
308 * add it to the open list */
309 m_nodes.InsertOpenNode(n);
310 }
311
312 const VehicleType * GetVehicle() const
313 {
314 return m_veh;
315 }
316
317 void DumpBase(DumpTarget &dmp) const
318 {
319 dmp.WriteStructT("m_nodes", &m_nodes);
320 dmp.WriteLine("m_num_steps = %d", m_num_steps);
321 }
322
323 /* methods that should be implemented at derived class Types::Tpf (derived from CYapfBaseT) */
324
325 #if 0
326
327 inline void PfSetStartupNodes()
328 {
329 /* example: */
330 Node &n1 = *base::m_nodes.CreateNewNode();
331 .
332 . // setup node members here
333 .
334 base::m_nodes.InsertOpenNode(n1);
335 }
336
338 inline void PfFollowNode(Node &org)
339 {
340 for (each follower of node org) {
341 Node &n = *base::m_nodes.CreateNewNode();
342 .
343 . // setup node members here
344 .
345 n.m_parent = &org; // set node's parent to allow back tracking
346 AddNewNode(n);
347 }
348 }
349
351 inline bool PfCalcCost(Node &n)
352 {
353 /* evaluate last step cost */
354 int cost = ...;
355 /* set the node cost as sum of parent's cost and last step cost */
356 n.m_cost = n.m_parent->m_cost + cost;
357 return true; // true if node is valid follower (i.e. no obstacle was found)
358 }
359
361 inline bool PfCalcEstimate(Node &n)
362 {
363 /* evaluate the distance to our destination */
364 int distance = ...;
365 /* set estimate as sum of cost from origin + distance to the target */
366 n.m_estimate = n.m_cost + distance;
367 return true; // true if node is valid (i.e. not too far away :)
368 }
369
371 inline bool PfDetectDestination(Node &n)
372 {
373 bool bDest = (n.m_key.m_x == m_x2) && (n.m_key.m_y == m_y2);
374 return bDest;
375 }
376 #endif
377 };
378
379 #endif /* YAPF_BASE_HPP */
CYapfBaseT::m_perf_ts_cost
CPerformanceTimer m_perf_ts_cost
stats - GetTrackStatus() CPU time
Definition: yapf_base.hpp:73
CYapfBaseT::PfGetSettings
const YAPFSettings & PfGetSettings() const
return current settings (can be custom - company based - but later)
Definition: yapf_base.hpp:105
TrackdirBits
TrackdirBits
Enumeration of bitmasks for the TrackDirs.
Definition: track_type.h:101
LinkGraph::Node
Updatable node class.
Definition: linkgraph.h:373
UnitID
uint16 UnitID
Type for the company global vehicle unit number.
Definition: transport_type.h:16
CYapfBaseT::Tpf
Types::Tpf Tpf
the pathfinder class (derived from THIS class)
Definition: yapf_base.hpp:51
KillFirstBit
static T KillFirstBit(T value)
Clear the first bit in an integer.
Definition: bitmath_func.hpp:239
DumpTarget::WriteLine
void CDECL WriteLine(const char *format,...) WARN_FORMAT(2
Write a line with indent at the beginning and <LF> at the end.
Definition: dbg_helpers.cpp:119
YAPFSettings
Settings related to the yet another pathfinder.
Definition: settings_type.h:386
CYapfBaseT::m_stats_cache_hits
int m_stats_cache_hits
stats - how many node's costs were reused from cache
Definition: yapf_base.hpp:68
CYapfBaseT::AddMultipleNodes
void AddMultipleNodes(Node *parent, const TrackFollower &tf)
add multiple nodes - direct children of the given node
Definition: yapf_base.hpp:209
DEBUG
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
CYapfBaseT::m_settings
const YAPFSettings * m_settings
current settings (_settings_game.yapf)
Definition: yapf_base.hpp:63
CYapfBaseT::m_perf_slope_cost
CPerformanceTimer m_perf_slope_cost
stats - slope calculation CPU time
Definition: yapf_base.hpp:72
CYapfBaseT::CYapfBaseT
CYapfBaseT()
default constructor
Definition: yapf_base.hpp:81
FindFirstBit2x64
static uint8 FindFirstBit2x64(const int value)
Finds the position of the first non-zero bit in an integer.
Definition: bitmath_func.hpp:216
CYapfBaseT::Yapf
Tpf & Yapf()
to access inherited path finder
Definition: yapf_base.hpp:98
CYapfBaseT::PruneIntermediateNodeBranch
void PruneIntermediateNodeBranch()
In some cases an intermediate node branch should be pruned.
Definition: yapf_base.hpp:228
DumpTarget
Class that represents the dump-into-string target.
Definition: dbg_helpers.h:94
_settings_game
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:80
CYapfBaseT::m_nodes
NodeList m_nodes
node list multi-container
Definition: yapf_base.hpp:59
CYapfBaseT::m_num_steps
int m_num_steps
this is there for debugging purposes (hope it doesn't hurt)
Definition: yapf_base.hpp:77
CYapfBaseT::VehicleType
Types::VehicleType VehicleType
the type of vehicle
Definition: yapf_base.hpp:54
CPerformanceTimer
Definition: pf_performance_timer.hpp:15
CYapfBaseT::m_perf_other_cost
CPerformanceTimer m_perf_other_cost
stats - other CPU time
Definition: yapf_base.hpp:74
VehicleType
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
CYapfBaseT::GetBestNode
Node * GetBestNode()
If path was found return the best node that has reached the destination.
Definition: yapf_base.hpp:179
CYapfBaseT::Key
Node::Key Key
key to hash tables
Definition: yapf_base.hpp:56
CYapfBaseT::AddNewNode
void AddNewNode(Node &n, const TrackFollower &tf)
AddNewNode() - called by Tderived::PfFollowNode() for each child node.
Definition: yapf_base.hpp:239
DumpTarget::WriteStructT
void WriteStructT(const char *name, const S *s)
Dump nested object (or only its name if this instance is already known).
Definition: dbg_helpers.h:152
CYapfBaseT::m_max_search_nodes
int m_max_search_nodes
maximum number of nodes we are allowed to visit before we give up
Definition: yapf_base.hpp:64
TRACKDIR_BIT_NONE
@ TRACKDIR_BIT_NONE
No track build.
Definition: track_type.h:102
CYapfBaseT::m_pBestDestNode
Node * m_pBestDestNode
pointer to the destination node found at last round
Definition: yapf_base.hpp:61
CYapfBaseT::AddStartupNode
void AddStartupNode(Node &n)
Add new node (created by CreateNewNode and filled with data) into open list.
Definition: yapf_base.hpp:195
CYapfBaseT::m_stats_cost_calcs
int m_stats_cost_calcs
stats - how many node's costs were calculated
Definition: yapf_base.hpp:67
CYapfBaseT::FindPath
bool FindPath(const VehicleType *v)
Main pathfinder routine:
Definition: yapf_base.hpp:119
CYapfBaseT::CreateNewNode
Node & CreateNewNode()
Calls NodeList::CreateNewNode() - allocates new node that can be filled and used as argument for AddS...
Definition: yapf_base.hpp:188
CYapfBaseT::NodeList
Types::NodeList NodeList
our node list
Definition: yapf_base.hpp:53
CYapfBaseT::~CYapfBaseT
~CYapfBaseT()
default destructor
Definition: yapf_base.hpp:94
CYapfBaseT::m_pBestIntermediateNode
Node * m_pBestIntermediateNode
here should be node closest to the destination if path not found
Definition: yapf_base.hpp:62
CYapfBaseT::m_veh
const VehicleType * m_veh
vehicle that we are trying to drive
Definition: yapf_base.hpp:65
Trackdir
Trackdir
Enumeration for tracks and directions.
Definition: track_type.h:70
CYapfBaseT
CYapfBaseT - A-star type path finder base class.
Definition: yapf_base.hpp:49
CYapfBaseT::m_perf_cost
CPerformanceTimer m_perf_cost
stats - total CPU time of this run
Definition: yapf_base.hpp:71
CYapfBaseT::Node
NodeList::Titem Node
this will be our node type
Definition: yapf_base.hpp:55