OpenTTD Source  20240917-master-g9ab0a47812
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 
46 template <class Types>
47 class CYapfBaseT {
48 public:
49  typedef typename Types::Tpf Tpf;
50  typedef typename Types::TrackFollower TrackFollower;
51  typedef typename Types::NodeList NodeList;
52  typedef typename Types::VehicleType VehicleType;
53  typedef typename NodeList::Titem Node;
54  typedef typename Node::Key Key;
55 
56 
58 protected:
63  const VehicleType *m_veh;
64 
67 
68 public:
70 
71 public:
73  inline CYapfBaseT()
74  : m_pBestDestNode(nullptr)
75  , m_pBestIntermediateNode(nullptr)
76  , m_settings(&_settings_game.pf.yapf)
77  , m_max_search_nodes(PfGetSettings().max_search_nodes)
78  , m_veh(nullptr)
81  , m_num_steps(0)
82  {
83  }
84 
87 
88 protected:
90  inline Tpf &Yapf()
91  {
92  return *static_cast<Tpf *>(this);
93  }
94 
95 public:
97  inline const YAPFSettings &PfGetSettings() const
98  {
99  return *m_settings;
100  }
101 
111  inline bool FindPath(const VehicleType *v)
112  {
113  m_veh = v;
114 
115  Yapf().PfSetStartupNodes();
116 
117  for (;;) {
118  m_num_steps++;
119  Node *best_open_node = m_nodes.GetBestOpenNode();
120  if (best_open_node == nullptr) break;
121 
122  if (Yapf().PfDetectDestination(*best_open_node)) {
123  m_pBestDestNode = best_open_node;
124  break;
125  }
126 
127  Yapf().PfFollowNode(*best_open_node);
128  if (m_max_search_nodes != 0 && m_nodes.ClosedCount() >= m_max_search_nodes) break;
129 
130  m_nodes.PopOpenNode(best_open_node->GetKey());
131  m_nodes.InsertClosedNode(*best_open_node);
132  }
133 
134  const bool destination_found = (m_pBestDestNode != nullptr);
135 
136  if (_debug_yapf_level >= 3) {
137  const UnitID veh_idx = (m_veh != nullptr) ? m_veh->unitnumber : 0;
138  const char ttc = Yapf().TransportTypeChar();
139  const 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);
140  const int cost = destination_found ? m_pBestDestNode->m_cost : -1;
141  const int dist = destination_found ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1;
142 
143  Debug(yapf, 3, "[YAPF{}]{}{:4d} - {} rounds - {} open - {} closed - CHR {:4.1f}% - C {} D {}",
144  ttc, destination_found ? '-' : '!', veh_idx, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(), cache_hit_ratio, cost, dist
145  );
146  }
147 
148  return destination_found;
149  }
150 
155  inline Node *GetBestNode()
156  {
158  }
159 
164  inline Node &CreateNewNode()
165  {
166  Node &node = *m_nodes.CreateNewNode();
167  return node;
168  }
169 
171  inline void AddStartupNode(Node &n)
172  {
173  Yapf().PfNodeCacheFetch(n);
174  /* insert the new node only if it is not there */
175  if (m_nodes.FindOpenNode(n.m_key) == nullptr) {
176  m_nodes.InsertOpenNode(n);
177  } else {
178  /* if we are here, it means that node is already there - how it is possible?
179  * probably the train is in the position that both its ends point to the same tile/exit-dir
180  * very unlikely, but it happened */
181  }
182  }
183 
185  inline void AddMultipleNodes(Node *parent, const TrackFollower &tf)
186  {
187  bool is_choice = (KillFirstBit(tf.m_new_td_bits) != TRACKDIR_BIT_NONE);
188  for (TrackdirBits rtds = tf.m_new_td_bits; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
189  Trackdir td = (Trackdir)FindFirstBit(rtds);
190  Node &n = Yapf().CreateNewNode();
191  n.Set(parent, tf.m_new_tile, td, is_choice);
192  Yapf().AddNewNode(n, tf);
193  }
194  }
195 
205  {
206  bool intermediate_on_branch = false;
207  while (n != nullptr && (n->m_segment->m_end_segment_reason & ESRB_CHOICE_FOLLOWS) == 0) {
208  if (n == Yapf().m_pBestIntermediateNode) intermediate_on_branch = true;
209  n = n->m_parent;
210  }
211  if (intermediate_on_branch) Yapf().m_pBestIntermediateNode = n;
212  }
213 
218  void AddNewNode(Node &n, const TrackFollower &tf)
219  {
220  /* evaluate the node */
221  bool bCached = Yapf().PfNodeCacheFetch(n);
222  if (!bCached) {
224  } else {
226  }
227 
228  bool bValid = Yapf().PfCalcCost(n, &tf);
229 
230  if (bValid) bValid = Yapf().PfCalcEstimate(n);
231 
232  /* have the cost or estimate callbacks marked this node as invalid? */
233  if (!bValid) return;
234 
235  /* The new node can be set as the best intermediate node only once we're
236  * certain it will be finalized by being inserted into the open list. */
237  bool set_intermediate = m_max_search_nodes > 0 && (m_pBestIntermediateNode == nullptr || (m_pBestIntermediateNode->GetCostEstimate() - m_pBestIntermediateNode->GetCost()) > (n.GetCostEstimate() - n.GetCost()));
238 
239  /* check new node against open list */
240  Node *openNode = m_nodes.FindOpenNode(n.GetKey());
241  if (openNode != nullptr) {
242  /* another node exists with the same key in the open list
243  * is it better than new one? */
244  if (n.GetCostEstimate() < openNode->GetCostEstimate()) {
245  /* update the old node by value from new one */
246  m_nodes.PopOpenNode(n.GetKey());
247  *openNode = n;
248  /* add the updated old node back to open list */
249  m_nodes.InsertOpenNode(*openNode);
250  if (set_intermediate) m_pBestIntermediateNode = openNode;
251  }
252  return;
253  }
254 
255  /* check new node against closed list */
256  Node *closedNode = m_nodes.FindClosedNode(n.GetKey());
257  if (closedNode != nullptr) {
258  /* another node exists with the same key in the closed list
259  * is it better than new one? */
260  int node_est = n.GetCostEstimate();
261  int closed_est = closedNode->GetCostEstimate();
262  if (node_est < closed_est) {
263  /* If this assert occurs, you have probably problem in
264  * your Tderived::PfCalcCost() or Tderived::PfCalcEstimate().
265  * The problem could be:
266  * - PfCalcEstimate() gives too large numbers
267  * - PfCalcCost() gives too small numbers
268  * - You have used negative cost penalty in some cases (cost bonus) */
269  NOT_REACHED();
270  }
271  return;
272  }
273  /* the new node is really new
274  * add it to the open list */
275  m_nodes.InsertOpenNode(n);
276  if (set_intermediate) m_pBestIntermediateNode = &n;
277  }
278 
279  const VehicleType * GetVehicle() const
280  {
281  return m_veh;
282  }
283 
284  void DumpBase(DumpTarget &dmp) const
285  {
286  dmp.WriteStructT("m_nodes", &m_nodes);
287  dmp.WriteValue("m_num_steps", m_num_steps);
288  }
289 };
290 
291 #endif /* YAPF_BASE_HPP */
CYapfBaseT::PfGetSettings
const YAPFSettings & PfGetSettings() const
return current settings (can be custom - company based - but later)
Definition: yapf_base.hpp:97
UnitID
uint16_t 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:49
CYapfBaseT::PruneIntermediateNodeBranch
void PruneIntermediateNodeBranch(Node *n)
In some cases an intermediate node branch should be pruned.
Definition: yapf_base.hpp:204
YAPFSettings
Settings related to the yet another pathfinder.
Definition: settings_type.h:425
KillFirstBit
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
Definition: bitmath_func.hpp:250
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
CYapfBaseT::m_stats_cache_hits
int m_stats_cache_hits
stats - how many node's costs were reused from cache
Definition: yapf_base.hpp:66
CYapfBaseT::AddMultipleNodes
void AddMultipleNodes(Node *parent, const TrackFollower &tf)
add multiple nodes - direct children of the given node
Definition: yapf_base.hpp:185
DumpTarget::WriteStructT
void WriteStructT(const std::string &name, const S *s)
Dump nested object (or only its name if this instance is already known).
Definition: dbg_helpers.h:147
CYapfBaseT::m_settings
const YAPFSettings * m_settings
current settings (_settings_game.yapf)
Definition: yapf_base.hpp:61
TRACKDIR_BIT_NONE
@ TRACKDIR_BIT_NONE
No track build.
Definition: track_type.h:99
CYapfBaseT::CYapfBaseT
CYapfBaseT()
default constructor
Definition: yapf_base.hpp:73
CYapfBaseT::Yapf
Tpf & Yapf()
to access inherited path finder
Definition: yapf_base.hpp:90
DumpTarget
Class that represents the dump-into-string target.
Definition: dbg_helpers.h:95
_settings_game
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:57
CYapfBaseT::m_nodes
NodeList m_nodes
node list multi-container
Definition: yapf_base.hpp:57
CYapfBaseT::m_num_steps
int m_num_steps
this is there for debugging purposes (hope it doesn't hurt)
Definition: yapf_base.hpp:69
CYapfBaseT::VehicleType
Types::VehicleType VehicleType
the type of vehicle
Definition: yapf_base.hpp:52
CYapfBaseT::GetBestNode
Node * GetBestNode()
If path was found return the best node that has reached the destination.
Definition: yapf_base.hpp:155
CYapfBaseT::Key
Node::Key Key
key to hash tables
Definition: yapf_base.hpp:54
CYapfBaseT::AddNewNode
void AddNewNode(Node &n, const TrackFollower &tf)
AddNewNode() - called by Tderived::PfFollowNode() for each child node.
Definition: yapf_base.hpp:218
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:62
CYapfBaseT::m_pBestDestNode
Node * m_pBestDestNode
pointer to the destination node found at last round
Definition: yapf_base.hpp:59
CYapfBaseT::AddStartupNode
void AddStartupNode(Node &n)
Add new node (created by CreateNewNode and filled with data) into open list.
Definition: yapf_base.hpp:171
Trackdir
Trackdir
Enumeration for tracks and directions.
Definition: track_type.h:67
CYapfBaseT::m_stats_cost_calcs
int m_stats_cost_calcs
stats - how many node's costs were calculated
Definition: yapf_base.hpp:65
CYapfBaseT::FindPath
bool FindPath(const VehicleType *v)
Main pathfinder routine:
Definition: yapf_base.hpp:111
CYapfBaseT::CreateNewNode
Node & CreateNewNode()
Calls NodeList::CreateNewNode() - allocates new node that can be filled and used as argument for AddS...
Definition: yapf_base.hpp:164
CYapfBaseT::NodeList
Types::NodeList NodeList
our node list
Definition: yapf_base.hpp:51
CYapfBaseT::~CYapfBaseT
~CYapfBaseT()
default destructor
Definition: yapf_base.hpp:86
CYapfBaseT::m_pBestIntermediateNode
Node * m_pBestIntermediateNode
here should be node closest to the destination if path not found
Definition: yapf_base.hpp:60
VehicleType
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
CYapfBaseT::m_veh
const VehicleType * m_veh
vehicle that we are trying to drive
Definition: yapf_base.hpp:63
CYapfBaseT
CYapfBaseT - A-star type path finder base class.
Definition: yapf_base.hpp:47
TrackdirBits
TrackdirBits
Allow incrementing of Trackdir variables.
Definition: track_type.h:98
CYapfBaseT::Node
NodeList::Titem Node
this will be our node type
Definition: yapf_base.hpp:53
DumpTarget::WriteValue
void WriteValue(const std::string &name, int value)
Write 'name = value' with indent and new-line.
Definition: dbg_helpers.cpp:115
FindFirstBit
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
Definition: bitmath_func.hpp:213