Pass Node by pointer to CYapfRailBaseT::SignalCost
[openttd/fttd.git] / src / pathfinder / yapf / yapf_rail.cpp
blobef94fd5a58e262c39854640fa937da4341b4fc18
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
10 /** @file yapf_rail.cpp The rail pathfinding. */
12 #include "../../stdafx.h"
14 #include <bitset>
15 #include <vector>
17 #include "yapf.hpp"
18 #include "../railpos.h"
19 #include "../../viewport_func.h"
20 #include "../../newgrf_station.h"
21 #include "../../station_func.h"
22 #include "../../misc/array.hpp"
23 #include "../../misc/hashtable.hpp"
24 #include "../../map/slope.h"
25 #include "../../pbs.h"
26 #include "../../waypoint_base.h"
27 #include "../../date_func.h"
29 #define DEBUG_YAPF_CACHE 0
31 template <typename Tpf> void DumpState(Tpf &pf1, Tpf &pf2)
33 DumpTarget dmp1 ("yapf1.txt");
34 dmp1.WriteStructT("m_arr", pf1.GetArray());
35 dmp1.WriteLine("m_num_steps = %d", pf1.num_steps);
37 DumpTarget dmp2 ("yapf2.txt");
38 dmp2.WriteStructT("m_arr", pf2.GetArray());
39 dmp2.WriteLine("m_num_steps = %d", pf2.num_steps);
42 int _total_pf_time_us = 0;
45 /* Enum used in PfCalcCost() to see why was the segment closed. */
46 enum EndSegmentReason {
47 /* The following reasons can be saved into cached segment */
48 ESR_DEAD_END = 0, ///< track ends here
49 ESR_RAIL_TYPE, ///< the next tile has a different rail type than our tiles
50 ESR_INFINITE_LOOP, ///< infinite loop detected
51 ESR_SEGMENT_TOO_LONG, ///< the segment is too long (possible infinite loop)
52 ESR_CHOICE_FOLLOWS, ///< the next tile contains a choice (the track splits to more than one segments)
53 ESR_DEPOT, ///< stop in the depot (could be a target next time)
54 ESR_WAYPOINT, ///< waypoint encountered (could be a target next time)
55 ESR_STATION, ///< station encountered (could be a target next time)
56 ESR_SAFE_TILE, ///< safe waiting position found (could be a target)
58 /* The following reasons are used only internally by PfCalcCost().
59 * They should not be found in the cached segment. */
60 ESR_PATH_TOO_LONG, ///< the path is too long (searching for the nearest depot in the given radius)
61 ESR_FIRST_TWO_WAY_RED, ///< first signal was 2-way and it was red
62 ESR_LOOK_AHEAD_END, ///< we have just passed the last look-ahead signal
63 ESR_TARGET_REACHED, ///< we have just reached the destination
65 /* Special values */
66 ESR_NONE = 0xFF, ///< no reason to end the segment here
69 enum EndSegmentReasonBits {
70 ESRB_NONE = 0,
72 ESRB_DEAD_END = 1 << ESR_DEAD_END,
73 ESRB_RAIL_TYPE = 1 << ESR_RAIL_TYPE,
74 ESRB_INFINITE_LOOP = 1 << ESR_INFINITE_LOOP,
75 ESRB_SEGMENT_TOO_LONG = 1 << ESR_SEGMENT_TOO_LONG,
76 ESRB_CHOICE_FOLLOWS = 1 << ESR_CHOICE_FOLLOWS,
77 ESRB_DEPOT = 1 << ESR_DEPOT,
78 ESRB_WAYPOINT = 1 << ESR_WAYPOINT,
79 ESRB_STATION = 1 << ESR_STATION,
80 ESRB_SAFE_TILE = 1 << ESR_SAFE_TILE,
82 ESRB_PATH_TOO_LONG = 1 << ESR_PATH_TOO_LONG,
83 ESRB_FIRST_TWO_WAY_RED = 1 << ESR_FIRST_TWO_WAY_RED,
84 ESRB_LOOK_AHEAD_END = 1 << ESR_LOOK_AHEAD_END,
85 ESRB_TARGET_REACHED = 1 << ESR_TARGET_REACHED,
87 /* Additional (composite) values. */
89 /* What reasons mean that the target can be found and needs to be detected. */
90 ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
92 /* What reasons can be stored back into cached segment. */
93 ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
95 /* Reasons to abort pathfinding in this direction. */
96 ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED,
99 DECLARE_ENUM_AS_BIT_SET(EndSegmentReasonBits)
101 inline void WriteValueStr(EndSegmentReasonBits bits, FILE *f)
103 static const char * const end_segment_reason_names[] = {
104 "DEAD_END", "RAIL_TYPE", "INFINITE_LOOP", "SEGMENT_TOO_LONG", "CHOICE_FOLLOWS",
105 "DEPOT", "WAYPOINT", "STATION", "SAFE_TILE",
106 "PATH_TOO_LONG", "FIRST_TWO_WAY_RED", "LOOK_AHEAD_END", "TARGET_REACHED"
109 fprintf (f, "0x%04X (", bits);
110 ComposeNameT (f, bits, end_segment_reason_names, "UNK", ESRB_NONE, "NONE");
111 putc (')', f);
115 /** key for YAPF rail nodes */
116 typedef CYapfNodeKeyTrackDir<RailPathPos> CYapfRailKey;
118 /** key for cached segment cost for rail YAPF */
119 struct CYapfRailSegmentKey
121 uint32 m_value;
123 inline CYapfRailSegmentKey(const CYapfRailSegmentKey& src) : m_value(src.m_value) {}
125 inline CYapfRailSegmentKey(const CYapfRailKey& node_key)
127 Set(node_key);
130 inline void Set(const CYapfRailSegmentKey& src)
132 m_value = src.m_value;
135 inline void Set(const CYapfRailKey& node_key)
137 m_value = node_key.CalcHash();
140 inline int32 CalcHash() const
142 return m_value;
145 inline bool operator == (const CYapfRailSegmentKey& other) const
147 return m_value == other.m_value;
150 void Dump(DumpTarget &dmp) const
152 dmp.WriteTile("tile", (TileIndex)(m_value >> 4));
153 dmp.WriteEnumT("td", (Trackdir)(m_value & 0x0F));
157 /** cached segment cost for rail YAPF */
158 struct CYapfRailSegment : CHashTableEntryT <CYapfRailSegment>
160 typedef CYapfRailSegmentKey Key;
162 CYapfRailSegmentKey m_key;
163 RailPathPos m_last;
164 int m_cost;
165 RailPathPos m_last_signal;
166 EndSegmentReasonBits m_end_segment_reason;
168 inline CYapfRailSegment(const CYapfRailSegmentKey& key)
169 : m_key(key)
170 , m_last()
171 , m_cost(-1)
172 , m_last_signal()
173 , m_end_segment_reason(ESRB_NONE)
176 inline const Key& GetKey() const
178 return m_key;
181 void Dump(DumpTarget &dmp) const
183 dmp.WriteStructT("m_key", &m_key);
184 dmp.WriteTile("m_last.tile", m_last.tile);
185 dmp.WriteEnumT("m_last.td", m_last.td);
186 dmp.WriteLine("m_cost = %d", m_cost);
187 dmp.WriteTile("m_last_signal.tile", m_last_signal.tile);
188 dmp.WriteEnumT("m_last_signal.td", m_last_signal.td);
189 dmp.WriteEnumT("m_end_segment_reason", m_end_segment_reason);
193 /** Yapf Node for rail YAPF */
194 struct CYapfRailNodeTrackDir
195 : CYapfNodeT<CYapfRailKey, CYapfRailNodeTrackDir>
197 typedef CYapfNodeT<CYapfRailKey, CYapfRailNodeTrackDir> base;
198 typedef CYapfRailSegment CachedData;
200 enum {
201 FLAG_TARGET_SEEN,
202 FLAG_CHOICE_SEEN,
203 FLAG_LAST_SIGNAL_WAS_RED,
204 NFLAGS
207 CYapfRailSegment *m_segment;
208 uint16 m_num_signals_passed;
209 std::bitset<NFLAGS> flags;
210 SignalType m_last_red_signal_type;
211 SignalType m_last_signal_type;
213 inline void Set(CYapfRailNodeTrackDir *parent, const RailPathPos &pos, bool is_choice)
215 base::Set(parent, pos);
216 m_segment = NULL;
217 if (parent == NULL) {
218 m_num_signals_passed = 0;
219 flags = 0;
220 m_last_red_signal_type = SIGTYPE_NORMAL;
221 /* We use PBS as initial signal type because if we are in
222 * a PBS section and need to route, i.e. we're at a safe
223 * waiting point of a station, we need to account for the
224 * reservation costs. If we are in a normal block then we
225 * should be alone in there and as such the reservation
226 * costs should be 0 anyway. If there would be another
227 * train in the block, i.e. passing signals at danger
228 * then avoiding that train with help of the reservation
229 * costs is not a bad thing, actually it would probably
230 * be a good thing to do. */
231 m_last_signal_type = SIGTYPE_PBS;
232 } else {
233 m_num_signals_passed = parent->m_num_signals_passed;
234 flags = parent->flags;
235 m_last_red_signal_type = parent->m_last_red_signal_type;
236 m_last_signal_type = parent->m_last_signal_type;
238 flags.set (FLAG_CHOICE_SEEN, is_choice);
241 inline const RailPathPos& GetLastPos() const
243 assert(m_segment != NULL);
244 return m_segment->m_last;
247 void Dump(DumpTarget &dmp) const
249 base::Dump(dmp);
250 dmp.WriteStructT("m_segment", m_segment);
251 dmp.WriteLine("m_num_signals_passed = %d", m_num_signals_passed);
252 dmp.WriteLine("m_target_seen = %s", flags.test(FLAG_TARGET_SEEN) ? "Yes" : "No");
253 dmp.WriteLine("m_choice_seen = %s", flags.test(FLAG_CHOICE_SEEN) ? "Yes" : "No");
254 dmp.WriteLine("m_last_signal_was_red = %s", flags.test(FLAG_LAST_SIGNAL_WAS_RED) ? "Yes" : "No");
255 dmp.WriteEnumT("m_last_red_signal_type", m_last_red_signal_type);
259 /* Default Astar type */
260 typedef Astar<CYapfRailNodeTrackDir, 8, 10> AstarRailTrackDir;
264 * Base class for segment cost cache providers. Contains global counter
265 * of track layout changes and static notification function called whenever
266 * the track layout changes. It is implemented as base class because it needs
267 * to be shared between all rail YAPF types (one shared counter, one notification
268 * function.
270 struct CSegmentCostCacheBase
272 static int s_rail_change_counter;
274 static void NotifyTrackLayoutChange(TileIndex tile, Track track)
276 s_rail_change_counter++;
280 /** if any track changes, this counter is incremented - that will invalidate segment cost cache */
281 int CSegmentCostCacheBase::s_rail_change_counter = 0;
283 void YapfNotifyTrackLayoutChange(TileIndex tile, Track track)
285 CSegmentCostCacheBase::NotifyTrackLayoutChange(tile, track);
290 * CSegmentCostCacheT - template class providing hash-map and storage (heap)
291 * of Tsegment structures. Each rail node contains pointer to the segment
292 * that contains cached (or non-cached) segment cost information. Nodes can
293 * differ by key type, but they use the same segment type. Segment key should
294 * be always the same (TileIndex + DiagDirection) that represent the beginning
295 * of the segment (origin tile and exit-dir from this tile).
296 * Different CYapfCachedCostT types can share the same type of CSegmentCostCacheT.
297 * Look at CYapfRailSegment for the segment example
299 template <class Tsegment>
300 struct CSegmentCostCacheT
301 : public CSegmentCostCacheBase
303 static const int C_HASH_BITS = 14;
305 typedef CHashTableT<Tsegment, C_HASH_BITS> HashTable;
306 typedef SmallArray<Tsegment> Heap;
307 typedef typename Tsegment::Key Key; ///< key to hash table
309 HashTable m_map;
310 Heap m_heap;
312 inline CSegmentCostCacheT() {}
314 /** flush (clear) the cache */
315 inline void Flush()
317 m_map.Clear();
318 m_heap.Clear();
321 inline bool Get(const Key& key, Tsegment **item)
323 *item = m_map.Find(key);
324 if (*item != NULL) return true;
325 *item = new (m_heap.Append()) Tsegment(key);
326 m_map.Push(**item);
327 return false;
332 template <class TAstar>
333 class CYapfRailBaseT : public TAstar
335 public:
336 typedef typename TAstar::Node Node; ///< this will be our node type
337 typedef typename Node::Key Key; ///< key to hash tables
338 typedef typename Node::CachedData CachedData;
339 typedef typename CachedData::Key CacheKey;
340 typedef CSegmentCostCacheT<CachedData> Cache;
341 typedef SmallArray<CachedData> LocalCache;
343 protected:
344 const YAPFSettings *const m_settings; ///< current settings (_settings_game.yapf)
345 const Train *const m_veh; ///< vehicle that we are trying to drive
346 const RailTypes m_compatible_railtypes;
347 const bool mask_reserved_tracks;
348 bool m_treat_first_red_two_way_signal_as_eol; ///< in some cases (leaving station) we need to handle first two-way signal differently
349 public:
350 bool m_stopped_on_first_two_way_signal;
352 protected:
353 bool m_disable_cache;
354 Cache& m_global_cache;
355 LocalCache m_local_cache;
358 * @note maximum cost doesn't work with caching enabled
359 * @todo fix maximum cost failing with caching (e.g. FS#2900)
361 int m_max_cost;
362 std::vector<int> m_sig_look_ahead_costs;
364 int m_stats_cost_calcs; ///< stats - how many node's costs were calculated
365 int m_stats_cache_hits; ///< stats - how many node's costs were reused from cache
367 CPerformanceTimer m_perf_cost; ///< stats - total CPU time of this run
368 CPerformanceTimer m_perf_slope_cost; ///< stats - slope calculation CPU time
369 CPerformanceTimer m_perf_ts_cost; ///< stats - GetTrackStatus() CPU time
370 CPerformanceTimer m_perf_other_cost; ///< stats - other CPU time
372 CFollowTrackRail tf; ///< track follower to be used by Follow
373 CFollowTrackRail tf_local; ///< track follower to be used by CalcSegment
375 static const int s_max_segment_cost = 10000;
377 inline static Cache& stGetGlobalCache()
379 static int last_rail_change_counter = 0;
380 static Date last_date = 0;
381 static Cache C;
383 /* some statistics */
384 if (last_date != _date) {
385 last_date = _date;
386 DEBUG(yapf, 2, "Pf time today: %5d ms", _total_pf_time_us / 1000);
387 _total_pf_time_us = 0;
390 /* delete the cache sometimes... */
391 if (last_rail_change_counter != Cache::s_rail_change_counter) {
392 last_rail_change_counter = Cache::s_rail_change_counter;
393 C.Flush();
396 return C;
399 CYapfRailBaseT (const Train *v, bool allow_90deg, bool override_rail_type, bool mask_reserved_tracks)
400 : m_settings(&_settings_game.pf.yapf)
401 , m_veh(v)
402 , m_compatible_railtypes(v->compatible_railtypes | (override_rail_type ? GetRailTypeInfo(v->railtype)->compatible_railtypes : RAILTYPES_NONE))
403 , mask_reserved_tracks(mask_reserved_tracks)
404 , m_treat_first_red_two_way_signal_as_eol(true)
405 , m_stopped_on_first_two_way_signal(false)
406 , m_disable_cache(false)
407 , m_global_cache(stGetGlobalCache())
408 , m_max_cost(0)
409 , m_sig_look_ahead_costs(m_settings->rail_look_ahead_max_signals)
410 , m_stats_cost_calcs(0)
411 , m_stats_cache_hits(0)
412 , tf (v, allow_90deg, mask_reserved_tracks ? m_compatible_railtypes : v->compatible_railtypes)
413 , tf_local (v, allow_90deg, m_compatible_railtypes, &m_perf_ts_cost)
415 /* pre-compute look-ahead penalties into array */
416 int p0 = m_settings->rail_look_ahead_signal_p0;
417 int p1 = m_settings->rail_look_ahead_signal_p1;
418 int p2 = m_settings->rail_look_ahead_signal_p2;
419 for (uint i = 0; i < m_settings->rail_look_ahead_max_signals; i++) {
420 m_sig_look_ahead_costs[i] = p0 + i * (p1 + i * p2);
424 public:
425 inline bool Allow90degTurns (void) const
427 return tf.m_allow_90deg;
430 inline bool CanUseGlobalCache(Node& n) const
432 return !m_disable_cache
433 && (n.m_parent != NULL)
434 && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.size());
437 protected:
439 * Called by YAPF to attach cached or local segment cost data to the given node.
440 * @return true if globally cached data were used or false if local data was used
442 inline bool AttachSegmentToNode(Node *n)
444 CacheKey key(n->GetKey());
445 if (!CanUseGlobalCache(*n)) {
446 n->m_segment = new (m_local_cache.Append()) CachedData(key);
447 n->m_segment->m_last = n->GetPos();
448 return false;
450 bool found = m_global_cache.Get(key, &n->m_segment);
451 if (!found) n->m_segment->m_last = n->GetPos();
452 return found;
455 public:
456 /** Create and add a new node */
457 inline void AddStartupNode (const RailPathPos &pos, int cost = 0)
459 Node *node = TAstar::CreateNewNode (NULL, pos, false);
460 node->m_cost = cost;
461 AttachSegmentToNode(node);
462 TAstar::InsertInitialNode(node);
465 /** set origin */
466 void SetOrigin(const RailPathPos &pos)
468 AddStartupNode (pos);
471 /** set origin */
472 void SetOrigin(const RailPathPos &pos, const RailPathPos &rev, int reverse_penalty, bool treat_first_red_two_way_signal_as_eol)
474 m_treat_first_red_two_way_signal_as_eol = treat_first_red_two_way_signal_as_eol;
476 AddStartupNode (pos);
477 AddStartupNode (rev, reverse_penalty);
480 inline void SetMaxCost (int max_cost)
482 m_max_cost = max_cost;
485 inline void DisableCache(bool disable)
487 m_disable_cache = disable;
491 * In some cases an intermediate node branch should be pruned.
492 * The most prominent case is when a red EOL signal is encountered, but
493 * there was a segment change (e.g. a rail type change) before that. If
494 * the branch would not be pruned, the rail type change location would
495 * remain the best intermediate node, and thus the vehicle would still
496 * go towards the red EOL signal.
498 void PruneIntermediateNodeBranch()
500 while (TAstar::best_intermediate != NULL && (TAstar::best_intermediate->m_segment->m_end_segment_reason & ESRB_CHOICE_FOLLOWS) == 0) {
501 TAstar::best_intermediate = TAstar::best_intermediate->m_parent;
505 struct NodeData {
506 int parent_cost;
507 int entry_cost;
508 int segment_cost;
509 int extra_cost;
510 RailPathPos pos;
511 RailPathPos last_signal;
512 EndSegmentReasonBits end_reason;
515 /** Return the transition cost from one tile to another. */
516 inline int TransitionCost (const RailPathPos &pos1, const RailPathPos &pos2) const
518 assert(IsValidTrackdir(pos1.td));
519 assert(IsValidTrackdir(pos2.td));
521 if (pos1.in_wormhole() || !IsRailwayTile(pos1.tile)) {
522 assert(IsDiagonalTrackdir(pos1.td));
523 if (pos2.in_wormhole() || !IsRailwayTile(pos2.tile)) {
524 /* compare only the tracks, as depots cause reversing */
525 assert(TrackdirToTrack(pos1.td) == TrackdirToTrack(pos2.td));
526 return 0;
527 } else {
528 return (pos1.td == pos2.td) ? 0 : m_settings->rail_curve45_penalty;
530 } else {
531 if (pos2.in_wormhole() || !IsRailwayTile(pos2.tile)) {
532 assert(IsDiagonalTrackdir(pos2.td));
533 return (pos1.td == pos2.td) ? 0 : m_settings->rail_curve45_penalty;
537 /* both tiles are railway tiles */
539 int cost = 0;
540 if ((TrackdirToTrackdirBits(pos2.td) & (TrackdirBits)TrackdirCrossesTrackdirs(pos1.td)) != 0) {
541 /* 90-deg curve penalty */
542 cost += m_settings->rail_curve90_penalty;
543 } else if (pos2.td != NextTrackdir(pos1.td)) {
544 /* 45-deg curve penalty */
545 cost += m_settings->rail_curve45_penalty;
548 DiagDirection exitdir = TrackdirToExitdir(pos1.td);
549 bool t1 = KillFirstBit(GetTrackBits(pos1.tile) & DiagdirReachesTracks(ReverseDiagDir(exitdir))) != TRACK_BIT_NONE;
550 bool t2 = KillFirstBit(GetTrackBits(pos2.tile) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE;
551 if (t1 && t2) cost += m_settings->rail_doubleslip_penalty;
553 return cost;
556 /** Return one tile cost (base cost + level crossing penalty). */
557 inline int OneTileCost (const RailPathPos &pos) const
559 int cost = 0;
560 /* set base cost */
561 if (IsDiagonalTrackdir(pos.td)) {
562 cost += YAPF_TILE_LENGTH;
563 if (IsLevelCrossingTile(pos.tile)) {
564 /* Increase the cost for level crossings */
565 cost += m_settings->rail_crossing_penalty;
567 } else {
568 /* non-diagonal trackdir */
569 cost = YAPF_TILE_CORNER_LENGTH;
571 return cost;
574 /** Return slope cost for a tile. */
575 inline int SlopeCost (const RailPathPos &pos)
577 CPerfStart perf_cost(m_perf_slope_cost);
579 if (pos.in_wormhole() || !IsDiagonalTrackdir(pos.td)) return 0;
581 /* Only rail tracks and bridgeheads can have sloped rail. */
582 if (!IsRailwayTile(pos.tile)) return 0;
584 bool uphill;
585 if (IsTileSubtype(pos.tile, TT_BRIDGE)) {
586 /* it is bridge ramp, check if we are entering the bridge */
587 DiagDirection dir = GetTunnelBridgeDirection(pos.tile);
588 if (dir != TrackdirToExitdir(pos.td)) return 0; // no, we are leaving it, no penalty
589 /* we are entering the bridge */
590 Slope tile_slope = GetTileSlope(pos.tile);
591 Axis axis = DiagDirToAxis(dir);
592 uphill = !HasBridgeFlatRamp(tile_slope, axis);
593 } else {
594 /* not bridge ramp */
595 Slope tile_slope = GetTileSlope(pos.tile);
596 uphill = IsUphillTrackdir(tile_slope, pos.td); // slopes uphill => apply penalty
599 return uphill ? m_settings->rail_slope_penalty : 0;
602 /** Check for a reserved station platform. */
603 static inline bool IsAnyStationTileReserved (const RailPathPos &pos, int skipped)
605 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos.td)));
606 for (TileIndex tile = pos.tile; skipped >= 0; skipped--, tile += diff) {
607 if (HasStationReservation(tile)) return true;
609 return false;
612 /** The cost for reserved tiles, including skipped ones. */
613 inline int ReservationCost(Node& n, const RailPathPos &pos, int skipped) const
615 if (n.m_num_signals_passed >= m_sig_look_ahead_costs.size() / 2) return 0;
616 if (!IsPbsSignal(n.m_last_signal_type)) return 0;
618 if (!pos.in_wormhole() && IsRailStationTile(pos.tile) && IsAnyStationTileReserved(pos, skipped)) {
619 return m_settings->rail_pbs_station_penalty * (skipped + 1);
620 } else if (TrackOverlapsTracks(GetReservedTrackbits(pos.tile), TrackdirToTrack(pos.td))) {
621 int cost = m_settings->rail_pbs_cross_penalty;
622 if (!IsDiagonalTrackdir(pos.td)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH;
623 return cost * (skipped + 1);
625 return 0;
628 /** Penalty for platform shorter/longer than needed. */
629 inline int PlatformLengthPenalty (int platform_length) const
631 int cost = 0;
632 const Train *v = m_veh;
633 assert(v->gcache.cached_total_length != 0);
634 int missing_platform_length = CeilDiv(v->gcache.cached_total_length, TILE_SIZE) - platform_length;
635 if (missing_platform_length < 0) {
636 /* apply penalty for longer platform than needed */
637 cost += m_settings->rail_longer_platform_penalty + m_settings->rail_longer_platform_per_tile_penalty * -missing_platform_length;
638 } else if (missing_platform_length > 0) {
639 /* apply penalty for shorter platform than needed */
640 cost += m_settings->rail_shorter_platform_penalty + m_settings->rail_shorter_platform_per_tile_penalty * missing_platform_length;
642 return cost;
645 /* Compute cost and modify node state for a signal. */
646 inline int SignalCost(Node *n, const RailPathPos &pos, NodeData *data);
648 /* Compute cost and modify node state for a position. */
649 inline void HandleNodeTile (Node *n, const CFollowTrackRail *tf, NodeData *segment, TileIndex prev);
651 /* Check for possible reasons to end a segment at the next tile. */
652 inline bool HandleNodeNextTile (Node *n, CFollowTrackRail *tf, NodeData *segment, RailType rail_type);
654 /** Compute all costs for a newly-allocated (not cached) segment. */
655 inline EndSegmentReasonBits CalcSegment (Node *n, const CFollowTrackRail *tf);
657 /* Fill in a node from cached data. */
658 inline EndSegmentReasonBits RestoreCachedNode (Node *n);
660 /* Add special extra cost when the segment reaches our target. */
661 inline void AddTargetCost (Node *n, bool is_station);
663 /** Struct to store a position in a path (node and path position). */
664 struct NodePos {
665 RailPathPos pos; ///< position (tile and trackdir)
666 Node *node; ///< node where the position is
669 /* Find the earliest safe position on a path. */
670 Node *FindSafePositionOnPath (Node *node, NodePos *res);
672 /* Try to reserve the path up to a given position. */
673 bool TryReservePath (TileIndex origin, const NodePos *res);
676 /** Compute cost and modify node state for a signal. */
677 template <class TAstar>
678 inline int CYapfRailBaseT<TAstar>::SignalCost(Node *n, const RailPathPos &pos, NodeData *data)
680 int cost = 0;
681 /* if there is one-way signal in the opposite direction, then it is not our way */
682 CPerfStart perf_cost(m_perf_other_cost);
684 if (pos.has_signal_along()) {
685 SignalState sig_state = pos.get_signal_state();
686 SignalType sig_type = pos.get_signal_type();
688 n->m_last_signal_type = sig_type;
690 /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */
691 int look_ahead_cost = (n->m_num_signals_passed < m_sig_look_ahead_costs.size()) ? m_sig_look_ahead_costs[n->m_num_signals_passed] : 0;
692 if (sig_state != SIGNAL_STATE_RED) {
693 /* green signal */
694 n->flags.reset (n->FLAG_LAST_SIGNAL_WAS_RED);
695 /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */
696 if (look_ahead_cost < 0) {
697 /* add its negation to the cost */
698 cost -= look_ahead_cost;
700 } else {
701 /* we have a red signal in our direction
702 * was it first signal which is two-way? */
703 if (!IsPbsSignal(sig_type)
704 && m_settings->rail_firstred_twoway_eol
705 && m_treat_first_red_two_way_signal_as_eol
706 && n->flags.test(n->FLAG_CHOICE_SEEN)
707 && pos.has_signal_against()
708 && n->m_num_signals_passed == 0) {
709 /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
710 PruneIntermediateNodeBranch();
711 data->end_reason |= ESRB_DEAD_END;
712 m_stopped_on_first_two_way_signal = true;
713 return -1;
715 n->m_last_red_signal_type = sig_type;
716 n->flags.set (n->FLAG_LAST_SIGNAL_WAS_RED);
718 /* look-ahead signal penalty */
719 if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
720 /* add the look ahead penalty only if it is positive */
721 cost += look_ahead_cost;
724 /* special signal penalties */
725 if (n->m_num_signals_passed == 0) {
726 switch (sig_type) {
727 case SIGTYPE_COMBO:
728 case SIGTYPE_EXIT: cost += m_settings->rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
729 case SIGTYPE_NORMAL:
730 case SIGTYPE_ENTRY: cost += m_settings->rail_firstred_penalty; break;
731 default: break;
736 n->m_num_signals_passed++;
737 data->last_signal = pos;
738 } else if (pos.has_signal_against()) {
739 if (pos.get_signal_type() != SIGTYPE_PBS) {
740 /* one-way signal in opposite direction */
741 data->end_reason |= ESRB_DEAD_END;
742 } else {
743 cost += n->m_num_signals_passed < m_settings->rail_look_ahead_max_signals ? m_settings->rail_pbs_signal_back_penalty : 0;
747 return cost;
750 /** Compute cost and modify node state for a position. */
751 template <class TAstar>
752 inline void CYapfRailBaseT<TAstar>::HandleNodeTile (Node *n, const CFollowTrackRail *tf, NodeData *segment, TileIndex prev)
754 /* All other tile costs will be calculated here. */
755 segment->segment_cost += OneTileCost(segment->pos);
757 /* If we skipped some tunnel/bridge/station tiles, add their base cost */
758 segment->segment_cost += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
760 /* Slope cost. */
761 segment->segment_cost += SlopeCost(segment->pos);
763 /* Signal cost (routine can modify segment data). */
764 segment->segment_cost += SignalCost(n, segment->pos, segment);
766 /* Reserved tiles. */
767 segment->segment_cost += ReservationCost(*n, segment->pos, tf->m_tiles_skipped);
769 /* Tests for 'potential target' reasons to close the segment. */
770 if (segment->pos.tile == prev) {
771 /* Penalty for reversing in a depot. */
772 assert(!segment->pos.in_wormhole());
773 assert(IsRailDepotTile(segment->pos.tile));
774 assert(segment->pos.td == DiagDirToDiagTrackdir(GetGroundDepotDirection(segment->pos.tile)));
775 segment->segment_cost += m_settings->rail_depot_reverse_penalty;
776 /* We will end in this pass (depot is possible target) */
777 segment->end_reason |= ESRB_DEPOT;
779 } else if (!segment->pos.in_wormhole() && IsRailWaypointTile(segment->pos.tile)) {
780 const Train *v = m_veh;
781 if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
782 GetStationIndex(segment->pos.tile) == v->current_order.GetDestination() &&
783 !Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) {
784 /* This waypoint is our destination; maybe this isn't an unreserved
785 * one, so check that and if so see that as the last signal being
786 * red. This way waypoints near stations should work better. */
787 CFollowTrackRail ft(v);
788 ft.SetPos(segment->pos);
790 bool add_extra_cost;
791 for (;;) {
792 if (!ft.FollowNext()) {
793 /* end of line */
794 add_extra_cost = !IsWaitingPositionFree(v, ft.m_old, _settings_game.pf.forbid_90_deg);
795 break;
798 assert(ft.m_old.tile != ft.m_new.tile);
799 if (!ft.m_new.is_single()) {
800 /* We encountered a junction; it's going to be too complex to
801 * handle this perfectly, so just bail out. There is no simple
802 * free path, so try the other possibilities. */
803 add_extra_cost = true;
804 break;
807 /* If this is a safe waiting position we're done searching for it */
808 PBSPositionState state = CheckWaitingPosition (v, ft.m_new, _settings_game.pf.forbid_90_deg);
809 if (state != PBS_UNSAFE) {
810 add_extra_cost = state == PBS_BUSY;
811 break;
815 /* In the case this platform is (possibly) occupied we add penalty so the
816 * other platforms of this waypoint are evaluated as well, i.e. we assume
817 * that there is a red signal in the waypoint when it's occupied. */
818 if (add_extra_cost) segment->extra_cost += m_settings->rail_lastred_penalty;
820 /* Waypoint is also a good reason to finish. */
821 segment->end_reason |= ESRB_WAYPOINT;
823 } else if (tf->m_flag == tf->TF_STATION) {
824 /* Station penalties. */
825 uint platform_length = tf->m_tiles_skipped + 1;
826 /* We don't know yet if the station is our target or not. Act like
827 * if it is pass-through station (not our destination). */
828 segment->segment_cost += m_settings->rail_station_penalty * platform_length;
829 /* We will end in this pass (station is possible target) */
830 segment->end_reason |= ESRB_STATION;
832 } else if (mask_reserved_tracks) {
833 /* Searching for a safe tile? */
834 if (segment->pos.has_signal_along() && !IsPbsSignal(segment->pos.get_signal_type())) {
835 segment->end_reason |= ESRB_SAFE_TILE;
839 /* Apply max speed penalty only when inside the look-ahead radius.
840 * Otherwise it would cause desync in MP. */
841 if (n->m_num_signals_passed < m_sig_look_ahead_costs.size())
843 int max_speed = tf->GetSpeedLimit();
844 int max_veh_speed = m_veh->GetDisplayMaxSpeed();
845 if (max_speed < max_veh_speed) {
846 segment->extra_cost += YAPF_TILE_LENGTH * (max_veh_speed - max_speed) * (1 + tf->m_tiles_skipped) / max_veh_speed;
850 /* Finish if we already exceeded the maximum path cost (i.e. when
851 * searching for the nearest depot). */
852 if (m_max_cost > 0 && (segment->parent_cost + segment->entry_cost + segment->segment_cost) > m_max_cost) {
853 segment->end_reason |= ESRB_PATH_TOO_LONG;
857 /** Check for possible reasons to end a segment at the next tile. */
858 template <class TAstar>
859 inline bool CYapfRailBaseT<TAstar>::HandleNodeNextTile (Node *n, CFollowTrackRail *tf, NodeData *segment, RailType rail_type)
861 if (!tf->Follow(segment->pos)) {
862 assert(tf->m_err != CFollowTrackRail::EC_NONE);
863 /* Can't move to the next tile (EOL?). */
864 if (tf->m_err == CFollowTrackRail::EC_RAIL_TYPE) {
865 segment->end_reason |= ESRB_RAIL_TYPE;
866 } else {
867 segment->end_reason |= ESRB_DEAD_END;
870 if (mask_reserved_tracks && !HasOnewaySignalBlockingPos(segment->pos)) {
871 segment->end_reason |= ESRB_SAFE_TILE;
873 return false;
876 /* Check if the next tile is not a choice. */
877 if (!tf->m_new.is_single()) {
878 /* More than one segment will follow. Close this one. */
879 segment->end_reason |= ESRB_CHOICE_FOLLOWS;
880 return false;
883 /* Gather the next tile/trackdir. */
885 if (mask_reserved_tracks) {
886 if (tf->m_new.has_signal_along() && IsPbsSignal(tf->m_new.get_signal_type())) {
887 /* Possible safe tile. */
888 segment->end_reason |= ESRB_SAFE_TILE;
889 } else if (tf->m_new.has_signal_against() && tf->m_new.get_signal_type() == SIGTYPE_PBS_ONEWAY) {
890 /* Possible safe tile, but not so good as it's the back of a signal... */
891 segment->end_reason |= ESRB_SAFE_TILE | ESRB_DEAD_END;
892 segment->extra_cost += m_settings->rail_lastred_exit_penalty;
896 /* Check the next tile for the rail type. */
897 if (tf->m_new.in_wormhole()) {
898 RailType next_rail_type = tf->m_new.get_railtype();
899 assert(next_rail_type == rail_type);
900 } else if (tf->m_new.get_railtype() != rail_type) {
901 /* Segment must consist from the same rail_type tiles. */
902 segment->end_reason |= ESRB_RAIL_TYPE;
903 return false;
906 /* Avoid infinite looping. */
907 if (tf->m_new == n->GetPos()) {
908 segment->end_reason |= ESRB_INFINITE_LOOP;
909 return false;
912 if (segment->segment_cost > s_max_segment_cost) {
913 /* Potentially in the infinite loop (or only very long segment?). We should
914 * not force it to finish prematurely unless we are on a regular tile. */
915 if (!tf->m_new.in_wormhole() && IsNormalRailTile(tf->m_new.tile)) {
916 segment->end_reason |= ESRB_SEGMENT_TOO_LONG;
917 return false;
921 /* Any other reason bit set? */
922 return segment->end_reason == ESRB_NONE;
925 /** Compute all costs for a newly-allocated (not cached) segment. */
926 template <class TAstar>
927 inline EndSegmentReasonBits CYapfRailBaseT<TAstar>::CalcSegment (Node *n, const CFollowTrackRail *tf)
929 /* Each node cost contains 2 or 3 main components:
930 * 1. Transition cost - cost of the move from previous node (tile):
931 * - curve cost (or zero for straight move)
932 * 2. Tile cost:
933 * - base tile cost
934 * - YAPF_TILE_LENGTH for diagonal tiles
935 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
936 * - tile penalties
937 * - tile slope penalty (upward slopes)
938 * - red signal penalty
939 * - level crossing penalty
940 * - speed-limit penalty (bridges)
941 * - station platform penalty
942 * - penalty for reversing in the depot
943 * - etc.
944 * 3. Extra cost (applies to the last node only)
945 * - last red signal penalty
946 * - penalty for too long or too short platform on the destination station
949 /* Segment: one or more tiles connected by contiguous tracks of the same type.
950 * Each segment cost includes 'Tile cost' for all its tiles (including the first
951 * and last), and the 'Transition cost' between its tiles. The first transition
952 * cost of segment entry (move from the 'parent' node) is not included!
955 /* start at n and walk to the end of segment */
956 NodeData segment;
957 segment.parent_cost = n->m_parent->m_cost;
958 segment.entry_cost = TransitionCost (n->m_parent->GetLastPos(), n->GetPos());
959 segment.segment_cost = 0;
960 segment.extra_cost = 0;
961 segment.pos = n->GetPos();
962 segment.last_signal = RailPathPos();
963 segment.end_reason = ESRB_NONE;
965 TileIndex prev = n->m_parent->GetLastPos().tile;
967 RailType rail_type = segment.pos.get_railtype();
969 for (;;) {
970 HandleNodeTile (n, tf, &segment, prev);
972 /* Move to the next tile/trackdir. */
973 tf = &tf_local;
974 assert(tf_local.m_veh_owner == m_veh->owner);
975 assert(tf_local.m_railtypes == m_compatible_railtypes);
976 assert(tf_local.m_pPerf == &m_perf_ts_cost);
978 if (!HandleNodeNextTile (n, &tf_local, &segment, rail_type)) break;
980 /* Transition cost (cost of the move from previous tile) */
981 segment.segment_cost += TransitionCost (segment.pos, tf_local.m_new);
983 /* For the next loop set new prev and cur tile info. */
984 prev = segment.pos.tile;
985 segment.pos = tf_local.m_new;
986 } // for (;;)
988 /* Write back the segment information so it can be reused the next time. */
989 n->m_segment->m_last = segment.pos;
990 n->m_segment->m_cost = segment.segment_cost;
991 n->m_segment->m_last_signal = segment.last_signal;
992 n->m_segment->m_end_segment_reason = segment.end_reason & ESRB_CACHED_MASK;
994 /* total node cost */
995 n->m_cost = segment.parent_cost + segment.entry_cost + segment.segment_cost + segment.extra_cost;
996 return segment.end_reason;
999 /** Fill in a node from cached data. */
1000 template <class TAstar>
1001 inline EndSegmentReasonBits CYapfRailBaseT<TAstar>::RestoreCachedNode (Node *n)
1003 /* total node cost */
1004 n->m_cost = n->m_parent->m_cost + TransitionCost (n->m_parent->GetLastPos(), n->GetPos()) + n->m_segment->m_cost;
1005 /* We will need also some information about the last signal (if it was red). */
1006 if (n->m_segment->m_last_signal.is_valid_tile()) {
1007 assert(n->m_segment->m_last_signal.has_signal_along());
1008 SignalState sig_state = n->m_segment->m_last_signal.get_signal_state();
1009 bool is_red = (sig_state == SIGNAL_STATE_RED);
1010 n->flags.set (n->FLAG_LAST_SIGNAL_WAS_RED, is_red);
1011 if (is_red) {
1012 n->m_last_red_signal_type = n->m_segment->m_last_signal.get_signal_type();
1015 /* No further calculation needed. */
1016 return n->m_segment->m_end_segment_reason;
1019 /** Add special extra cost when the segment reaches our target. */
1020 template <class TAstar>
1021 inline void CYapfRailBaseT<TAstar>::AddTargetCost (Node *n, bool is_station)
1023 n->flags.set (n->FLAG_TARGET_SEEN);
1025 /* Last-red and last-red-exit penalties. */
1026 if (n->flags.test (n->FLAG_LAST_SIGNAL_WAS_RED)) {
1027 if (n->m_last_red_signal_type == SIGTYPE_EXIT) {
1028 /* last signal was red pre-signal-exit */
1029 n->m_cost += m_settings->rail_lastred_exit_penalty;
1030 } else if (!IsPbsSignal(n->m_last_red_signal_type)) {
1031 /* Last signal was red, but not exit or path signal. */
1032 n->m_cost += m_settings->rail_lastred_penalty;
1036 /* Station platform-length penalty. */
1037 if (is_station) {
1038 const BaseStation *st = BaseStation::GetByTile(n->GetLastPos().tile);
1039 assert(st != NULL);
1040 uint platform_length = st->GetPlatformLength(n->GetLastPos().tile, ReverseDiagDir(TrackdirToExitdir(n->GetLastPos().td)));
1041 /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
1042 n->m_cost -= m_settings->rail_station_penalty * platform_length;
1043 /* Add penalty for the inappropriate platform length. */
1044 n->m_cost += PlatformLengthPenalty(platform_length);
1049 * Find the earliest safe position on a path.
1050 * @param node Node to start searching back from (usually the best found node).
1051 * @param res Where to store the safe position found.
1052 * @return The first node in the path after the initial node.
1054 template <class TAstar>
1055 inline typename TAstar::Node *CYapfRailBaseT<TAstar>::FindSafePositionOnPath (Node *node, NodePos *res)
1057 /* We will never pass more than two signals, no need to check for a safe tile. */
1058 assert (node->m_parent != NULL);
1059 while (node->m_parent->m_num_signals_passed >= 2) {
1060 node = node->m_parent;
1061 /* If the parent node has passed at least 2 signals, it
1062 * cannot be a root node, because root nodes are single-tile
1063 * nodes and can therefore have only one signal, if any. */
1064 assert (node->m_parent != NULL);
1067 /* Default safe position if no other, earlier one is found. */
1068 res->node = node;
1069 res->pos = node->GetLastPos();
1071 /* Walk through the path back to the origin. */
1072 CFollowTrackRail ft (m_veh, Allow90degTurns(), m_compatible_railtypes);
1074 for (;;) {
1075 Node *parent = node->m_parent;
1076 assert (parent != NULL);
1078 /* Search node for a safe position. */
1079 ft.SetPos (node->GetPos());
1080 for (;;) {
1081 if (IsSafeWaitingPosition (m_veh, ft.m_new, !Allow90degTurns())) {
1082 /* Found a safe position in this node. */
1083 res->node = node;
1084 res->pos = ft.m_new;
1085 break;
1088 if (ft.m_new == node->GetLastPos()) break; // no safe position found in node
1090 bool follow = ft.FollowNext();
1091 assert (follow);
1092 assert (ft.m_new.is_single());
1095 /* Stop at node after initial node. */
1096 if (parent->m_parent == NULL) return node;
1097 node = parent;
1102 * Try to reserve a single track/platform
1103 * @param pos The position to reserve (last tile for platforms)
1104 * @param origin If pos is a platform, consider the platform to begin right after this tile
1105 * @return Whether reservation succeeded
1107 static bool ReserveSingleTrack (const RailPathPos &pos, TileIndex origin = INVALID_TILE)
1109 if (pos.in_wormhole() || !IsRailStationTile(pos.tile)) {
1110 return TryReserveRailTrack(pos);
1113 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos.td)));
1114 TileIndex t = pos.tile;
1116 do {
1117 if (HasStationReservation(t)) {
1118 /* Platform could not be reserved, undo. */
1119 diff = -diff;
1120 while (t != pos.tile) {
1121 t = TILE_ADD(t, diff);
1122 SetRailStationReservation(t, false);
1124 return false;
1127 SetRailStationReservation(t, true);
1128 MarkTileDirtyByTile(t);
1129 t = TILE_ADD(t, diff);
1130 } while (IsCompatibleTrainStationTile(t, pos.tile) && t != origin);
1132 TriggerStationRandomisation(NULL, pos.tile, SRT_PATH_RESERVATION);
1134 return true;
1138 * Unreserve a single track/platform
1139 * @param pos The position to reserve (last tile for platforms)
1140 * @param origin If pos is a platform, consider the platform to begin right after this tile
1142 static void UnreserveSingleTrack (const RailPathPos &pos, TileIndex origin = INVALID_TILE)
1144 if (pos.in_wormhole() || !IsRailStationTile(pos.tile)) {
1145 UnreserveRailTrack(pos);
1146 return;
1149 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos.td)));
1150 TileIndex t = pos.tile;
1151 while (IsCompatibleTrainStationTile(t, pos.tile) && t != origin) {
1152 assert(HasStationReservation(t));
1153 SetRailStationReservation(t, false);
1154 t = TILE_ADD(t, diff);
1159 * Try to reserve the path up to a given position.
1160 * @param origin Starting tile for the reservation.
1161 * @param res Target tile for the reservation.
1162 * @return Whether reservation succeeded.
1164 template <class TAstar>
1165 bool CYapfRailBaseT<TAstar>::TryReservePath (TileIndex origin, const NodePos *res)
1167 /* Don't bother if the target is reserved. */
1168 if (!IsWaitingPositionFree(m_veh, res->pos)) return false;
1170 CFollowTrackRail ft (m_veh, Allow90degTurns(), m_compatible_railtypes);
1172 for (Node *node = res->node; node->m_parent != NULL; node = node->m_parent) {
1173 ft.SetPos (node->GetPos());
1174 for (;;) {
1175 if (!ReserveSingleTrack (ft.m_new, origin)) {
1176 /* Reservation failed, undo. */
1177 Node *failed_node = node;
1178 RailPathPos res_fail = ft.m_new;
1179 for (node = res->node; node != failed_node; node = node->m_parent) {
1180 ft.SetPos (node->GetPos());
1181 for (;;) {
1182 UnreserveSingleTrack (ft.m_new, origin);
1183 if (ft.m_new == res->pos) break;
1184 if (ft.m_new == node->GetLastPos()) break;
1185 ft.FollowNext();
1188 ft.SetPos (failed_node->GetPos());
1189 while (ft.m_new != res_fail) {
1190 assert (ft.m_new != res->pos);
1191 assert (ft.m_new != node->GetLastPos());
1192 UnreserveSingleTrack (ft.m_new, origin);
1193 ft.FollowNext();
1195 return false;
1198 if (ft.m_new == res->pos) break;
1199 if (ft.m_new == node->GetLastPos()) break;
1200 bool follow = ft.FollowNext();
1201 assert (follow);
1202 assert (ft.m_new.is_single());
1206 if (CanUseGlobalCache(*res->node)) {
1207 YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
1210 return true;
1214 template <class TAstar>
1215 struct CYapfRailOrderT : CYapfRailBaseT <TAstar>
1217 public:
1218 typedef CYapfRailBaseT <TAstar> Base;
1219 typedef typename TAstar::Node Node;
1221 private:
1222 TileIndex m_dest_tile;
1223 StationID m_dest_station_id;
1225 public:
1226 CYapfRailOrderT (const Train *v, bool allow_90deg)
1227 : Base (v, allow_90deg, false, false)
1229 switch (v->current_order.GetType()) {
1230 case OT_GOTO_WAYPOINT:
1231 if (!Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) {
1232 /* In case of 'complex' waypoints we need to do a look
1233 * ahead. This look ahead messes a bit about, which
1234 * means that it 'corrupts' the cache. To prevent this
1235 * we disable caching when we're looking for a complex
1236 * waypoint. */
1237 Base::DisableCache(true);
1239 /* FALL THROUGH */
1240 case OT_GOTO_STATION:
1241 m_dest_station_id = v->current_order.GetDestination();
1242 m_dest_tile = BaseStation::Get(m_dest_station_id)->GetClosestTile(v->tile, v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT);
1243 break;
1245 default:
1246 m_dest_station_id = INVALID_STATION;
1247 m_dest_tile = v->dest_tile;
1248 break;
1252 /** Check if a position is a destination. */
1253 inline bool IsDestination (const RailPathPos &pos) const
1255 if (m_dest_station_id != INVALID_STATION) {
1256 return !pos.in_wormhole() && HasStationTileRail(pos.tile)
1257 && (GetStationIndex(pos.tile) == m_dest_station_id)
1258 && (GetRailStationTrack(pos.tile) == TrackdirToTrack(pos.td));
1259 } else {
1260 return !pos.in_wormhole() && (pos.tile == m_dest_tile);
1264 /** Estimate the cost from a node to the destination. */
1265 inline void CalcEstimate (Node *n) const
1267 n->m_estimate = n->m_cost + YapfCalcEstimate (n->GetLastPos(), m_dest_tile);
1268 assert (n->m_estimate >= n->m_parent->m_estimate);
1272 template <class TAstar>
1273 struct CYapfAnyDepotRailT : CYapfRailBaseT <TAstar>
1275 typedef CYapfRailBaseT <TAstar> Base;
1276 typedef typename TAstar::Node Node;
1278 CYapfAnyDepotRailT (const Train *v, bool allow_90deg)
1279 : Base (v, allow_90deg, false, false)
1283 /** Check if a position is a destination. */
1284 inline bool IsDestination (const RailPathPos &pos) const
1286 return !pos.in_wormhole() && IsRailDepotTile(pos.tile);
1289 /** Estimate the cost from a node to the destination. */
1290 inline void CalcEstimate (Node *n) const
1292 n->m_estimate = n->m_cost;
1296 template <class TAstar>
1297 struct CYapfAnySafeTileRailT : CYapfRailBaseT <TAstar>
1299 typedef CYapfRailBaseT <TAstar> Base;
1300 typedef typename TAstar::Node Node;
1302 CYapfAnySafeTileRailT (const Train *v, bool allow_90deg, bool override_railtype)
1303 : Base (v, allow_90deg, override_railtype, true)
1307 /** Check if a position is a destination. */
1308 inline bool IsDestination (const RailPathPos &pos) const
1310 return IsFreeSafeWaitingPosition (Base::m_veh, pos, Base::Allow90degTurns());
1313 /** Estimate the cost from a node to the destination. */
1314 inline void CalcEstimate (Node *n) const
1316 n->m_estimate = n->m_cost;
1321 template <class Base>
1322 struct CYapfRailT : public Base
1324 typedef typename Base::Node Node; ///< this will be our node type
1326 CYapfRailT (const Train *v, bool allow_90deg)
1327 : Base (v, allow_90deg)
1331 CYapfRailT (const Train *v, bool allow_90deg, bool override_rail_type)
1332 : Base (v, allow_90deg, override_rail_type)
1336 /** Called by the A-star underlying class to find the neighbours of a node. */
1337 inline void Follow (Node *old_node)
1339 if (!Base::tf.Follow(old_node->GetLastPos())) return;
1340 if (Base::mask_reserved_tracks && !Base::tf.MaskReservedTracks()) return;
1342 bool is_choice = !Base::tf.m_new.is_single();
1343 RailPathPos pos = Base::tf.m_new;
1344 for (TrackdirBits rtds = Base::tf.m_new.trackdirs; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
1345 pos.set_trackdir (FindFirstTrackdir(rtds));
1346 Node *n = Base::CreateNewNode(old_node, pos, is_choice);
1348 /* evaluate the node */
1349 bool cached = Base::AttachSegmentToNode(n);
1350 if (!cached) {
1351 Base::m_stats_cost_calcs++;
1352 } else {
1353 Base::m_stats_cache_hits++;
1356 CPerfStart perf_cost(Base::m_perf_cost);
1358 EndSegmentReasonBits end_reason;
1359 if (cached) {
1360 end_reason = Base::RestoreCachedNode (n);
1361 } else {
1362 end_reason = Base::CalcSegment (n, &this->tf);
1365 assert (((end_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) || !Base::IsDestination(n->GetLastPos()));
1367 if (((end_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) &&
1368 Base::IsDestination(n->GetLastPos())) {
1369 /* Special costs for the case we have reached our target. */
1370 Base::AddTargetCost (n, (end_reason & ESRB_STATION) != ESRB_NONE);
1371 perf_cost.Stop();
1372 n->m_estimate = n->m_cost;
1373 this->FoundTarget(n);
1375 } else if ((end_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) {
1376 /* Reason to not continue. Stop this PF branch. */
1377 continue;
1379 } else {
1380 perf_cost.Stop();
1381 Base::CalcEstimate(n);
1382 this->InsertNode(n);
1387 /** call the node follower */
1388 static inline void Follow (CYapfRailT *pf, Node *n)
1390 pf->Follow(n);
1394 * Main pathfinder routine:
1395 * - set startup node(s)
1396 * - main loop that stops if:
1397 * - the destination was found
1398 * - or the open list is empty (no route to destination).
1399 * - or the maximum amount of loops reached - m_max_search_nodes (default = 10000)
1400 * @return true if the path was found
1402 inline bool FindPath (void)
1404 #ifndef NO_DEBUG_MESSAGES
1405 CPerformanceTimer perf;
1406 perf.Start();
1407 #endif /* !NO_DEBUG_MESSAGES */
1409 bool bDestFound = Base::FindPath (Follow, Base::m_settings->max_search_nodes);
1411 #ifndef NO_DEBUG_MESSAGES
1412 perf.Stop();
1413 if (_debug_yapf_level >= 2) {
1414 int t = perf.Get(1000000);
1415 _total_pf_time_us += t;
1417 if (_debug_yapf_level >= 3) {
1418 UnitID veh_idx = (Base::m_veh != NULL) ? Base::m_veh->unitnumber : 0;
1419 float cache_hit_ratio = (Base::m_stats_cache_hits == 0) ? 0.0f : ((float)Base::m_stats_cache_hits / (float)(Base::m_stats_cache_hits + Base::m_stats_cost_calcs) * 100.0f);
1420 int cost = bDestFound ? Base::best->m_cost : -1;
1421 int dist = bDestFound ? Base::best->m_estimate - Base::best->m_cost : -1;
1423 DEBUG(yapf, 3, "[YAPFt]%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) -- ",
1424 bDestFound ? '-' : '!', veh_idx, t, Base::num_steps, Base::OpenCount(), Base::ClosedCount(),
1425 cache_hit_ratio, cost, dist, Base::m_perf_cost.Get(1000000), Base::m_perf_slope_cost.Get(1000000),
1426 Base::m_perf_ts_cost.Get(1000000), Base::m_perf_other_cost.Get(1000000)
1430 #endif /* !NO_DEBUG_MESSAGES */
1431 return bDestFound;
1434 /** Pathfind, then return the trackdir that leads into the shortest path. */
1435 Trackdir ChooseRailTrack (const RailPathPos &origin, bool reserve_track, PFResult *target)
1437 if (target != NULL) target->pos.tile = INVALID_TILE;
1439 /* set origin node */
1440 Base::SetOrigin(origin);
1442 /* find the best path */
1443 bool path_found = FindPath();
1445 /* if path not found - return INVALID_TRACKDIR */
1446 Trackdir next_trackdir = INVALID_TRACKDIR;
1447 Node *pNode = Base::GetBestNode();
1448 if (pNode != NULL) {
1449 if (reserve_track && path_found) {
1450 typename Base::NodePos res;
1451 Node *best_next_node = Base::FindSafePositionOnPath (pNode, &res);
1452 if (target != NULL) target->pos = res.pos;
1453 /* return trackdir from the best origin node (one of start nodes) */
1454 next_trackdir = best_next_node->GetPos().td;
1456 assert (best_next_node->m_parent->GetPos() == origin);
1457 assert (best_next_node->m_parent->GetLastPos() == origin);
1458 bool okay = Base::TryReservePath (origin.tile, &res);
1459 if (target != NULL) target->okay = okay;
1460 } else {
1461 while (pNode->m_parent->m_parent != NULL) {
1462 pNode = pNode->m_parent;
1464 assert (pNode->m_parent->GetPos() == origin);
1465 assert (pNode->m_parent->GetLastPos() == origin);
1466 /* return trackdir from the best origin node (one of start nodes) */
1467 next_trackdir = pNode->GetPos().td;
1471 /* Treat the path as found if stopped on the first two way signal(s). */
1472 if (target != NULL) target->found = path_found | Base::m_stopped_on_first_two_way_signal;
1473 return next_trackdir;
1476 /** Pathfind, then return whether return whether to use the second origin. */
1477 bool CheckReverseTrain (const RailPathPos &pos1, const RailPathPos &pos2, int reverse_penalty)
1479 /* set origin nodes */
1480 Base::SetOrigin (pos1, pos2, reverse_penalty, false);
1482 /* find the best path */
1483 if (!FindPath()) return false;
1485 /* path found; walk through the path back to the origin */
1486 Node *pNode = Base::GetBestNode();
1487 while (pNode->m_parent != NULL) {
1488 pNode = pNode->m_parent;
1491 /* check if it was reversed origin */
1492 return pNode->m_cost != 0;
1495 /** Pathfind, then store nearest target. */
1496 bool FindNearestTargetTwoWay (const RailPathPos &pos1, const RailPathPos &pos2, int max_penalty, int reverse_penalty, TileIndex *target_tile, bool *reversed)
1498 /* set origin node */
1499 Base::SetOrigin (pos1, pos2, reverse_penalty, true);
1500 Base::SetMaxCost(max_penalty);
1502 /* find the best path */
1503 if (!FindPath()) return false;
1505 /* path found; get found target tile */
1506 Node *n = Base::GetBestNode();
1507 *target_tile = n->GetLastPos().tile;
1509 /* walk through the path back to the origin */
1510 while (n->m_parent != NULL) {
1511 n = n->m_parent;
1514 /* if the origin node is our front vehicle tile/Trackdir then we didn't reverse
1515 * but we can also look at the cost (== 0 -> not reversed, == reverse_penalty -> reversed) */
1516 *reversed = (n->m_cost != 0);
1518 return true;
1521 /** Pathfind, then optionally try to reserve the path found. */
1522 bool FindNearestSafeTile (const RailPathPos &pos, bool reserve)
1524 /* Set origin. */
1525 Base::SetOrigin(pos);
1527 if (!FindPath()) return false;
1529 if (!reserve) return true;
1531 /* Found a destination, search for a reservation target. */
1532 Node *pNode = Base::GetBestNode();
1533 typename Base::NodePos res;
1534 pNode = Base::FindSafePositionOnPath(pNode, &res)->m_parent;
1535 assert (pNode->GetPos() == pos);
1536 assert (pNode->GetLastPos() == pos);
1538 return Base::TryReservePath (pos.tile, &res);
1543 typedef CYapfRailT <CYapfRailOrderT <AstarRailTrackDir> > CYapfRail;
1545 Trackdir YapfTrainChooseTrack(const Train *v, const RailPathPos &origin, bool reserve_track, PFResult *target)
1547 /* create pathfinder instance */
1548 CYapfRail pf1 (v, !_settings_game.pf.forbid_90_deg);
1549 #if !DEBUG_YAPF_CACHE
1550 Trackdir result1 = pf1.ChooseRailTrack(origin, reserve_track, target);
1552 #else
1553 Trackdir result1 = pf1.ChooseRailTrack(origin, false, NULL);
1554 CYapfRail pf2 (v, !_settings_game.pf.forbid_90_deg);
1555 pf2.DisableCache(true);
1556 Trackdir result2 = pf2.ChooseRailTrack(origin, reserve_track, target);
1557 if (result1 != result2) {
1558 DEBUG(yapf, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1, result2);
1559 DumpState(pf1, pf2);
1561 #endif
1563 return result1;
1566 bool YapfTrainCheckReverse(const Train *v)
1568 const Train *last_veh = v->Last();
1570 /* tiles where front and back are */
1571 RailPathPos pos = v->GetPos();
1572 RailPathPos rev = last_veh->GetReversePos();
1574 int reverse_penalty = 0;
1576 if (pos.in_wormhole()) {
1577 /* front in tunnel / on bridge */
1578 assert(TrackdirToExitdir(pos.td) == ReverseDiagDir(GetTunnelBridgeDirection(pos.wormhole)));
1580 /* Current position of the train in the wormhole */
1581 TileIndex cur_tile = TileVirtXY(v->x_pos, v->y_pos);
1583 /* Add distance to drive in the wormhole as penalty for the forward path, i.e. bonus for the reverse path
1584 * Note: Negative penalties are ok for the start tile. */
1585 reverse_penalty -= DistanceManhattan(cur_tile, pos.tile) * YAPF_TILE_LENGTH;
1588 if (rev.in_wormhole()) {
1589 /* back in tunnel / on bridge */
1590 assert(TrackdirToExitdir(rev.td) == ReverseDiagDir(GetTunnelBridgeDirection(rev.wormhole)));
1592 /* Current position of the last wagon in the wormhole */
1593 TileIndex cur_tile = TileVirtXY(last_veh->x_pos, last_veh->y_pos);
1595 /* Add distance to drive in the wormhole as penalty for the revere path. */
1596 reverse_penalty += DistanceManhattan(cur_tile, rev.tile) * YAPF_TILE_LENGTH;
1599 /* slightly hackish: If the pathfinders finds a path, the cost of the first node is tested to distinguish between forward- and reverse-path. */
1600 if (reverse_penalty == 0) reverse_penalty = 1;
1602 CYapfRail pf1 (v, !_settings_game.pf.forbid_90_deg);
1603 bool result1 = pf1.CheckReverseTrain(pos, rev, reverse_penalty);
1605 #if DEBUG_YAPF_CACHE
1606 CYapfRail pf2 (v, !_settings_game.pf.forbid_90_deg);
1607 pf2.DisableCache(true);
1608 bool result2 = pf2.CheckReverseTrain(pos, rev, reverse_penalty);
1609 if (result1 != result2) {
1610 DEBUG(yapf, 0, "CACHE ERROR: CheckReverseTrain() = [%s, %s]", result1 ? "T" : "F", result2 ? "T" : "F");
1611 DumpState(pf1, pf2);
1613 #endif
1615 return result1;
1619 typedef CYapfRailT <CYapfAnyDepotRailT <AstarRailTrackDir> > CYapfAnyDepotRail;
1621 bool YapfTrainFindNearestDepot(const Train *v, uint max_penalty, FindDepotData *res)
1623 RailPathPos origin;
1624 FollowTrainReservation(v, &origin);
1625 RailPathPos rev = v->Last()->GetReversePos();
1627 CYapfAnyDepotRail pf1 (v, !_settings_game.pf.forbid_90_deg);
1629 * With caching enabled it simply cannot get a reliable result when you
1630 * have limited the distance a train may travel. This means that the
1631 * cached result does not match uncached result in all cases and that
1632 * causes desyncs. So disable caching when finding for a depot that is
1633 * nearby. This only happens with automatic servicing of vehicles,
1634 * so it will only impact performance when you do not manually set
1635 * depot orders and you do not disable automatic servicing.
1637 if (max_penalty != 0) pf1.DisableCache(true);
1638 bool result1 = pf1.FindNearestTargetTwoWay (origin, rev, max_penalty, YAPF_INFINITE_PENALTY, &res->tile, &res->reverse);
1640 #if DEBUG_YAPF_CACHE
1641 CYapfAnyDepotRail pf2 (v, !_settings_game.pf.forbid_90_deg);
1642 TileIndex depot_tile2 = INVALID_TILE;
1643 bool reversed2 = false;
1644 pf2.DisableCache(true);
1645 bool result2 = pf2.FindNearestTargetTwoWay (origin, rev, max_penalty, YAPF_INFINITE_PENALTY, &depot_tile2, &reversed2);
1646 if (result1 != result2 || (result1 && (res->tile != depot_tile2 || res->reverse != reversed2))) {
1647 DEBUG(yapf, 0, "CACHE ERROR: FindNearestDepotTwoWay() = [%s, %s]", result1 ? "T" : "F", result2 ? "T" : "F");
1648 DumpState(pf1, pf2);
1650 #endif
1652 return result1;
1656 typedef CYapfRailT <CYapfAnySafeTileRailT <AstarRailTrackDir> > CYapfAnySafeTileRail;
1658 bool YapfTrainFindNearestSafeTile(const Train *v, const RailPathPos &pos, bool override_railtype)
1660 /* Create pathfinder instance */
1661 CYapfAnySafeTileRail pf1 (v, !_settings_game.pf.forbid_90_deg, override_railtype);
1662 #if !DEBUG_YAPF_CACHE
1663 bool result1 = pf1.FindNearestSafeTile(pos, true);
1665 #else
1666 bool result2 = pf1.FindNearestSafeTile(pos, false);
1667 CYapfAnySafeTileRail pf2 (v, !_settings_game.pf.forbid_90_deg, override_railtype);
1668 pf2.DisableCache(true);
1669 bool result1 = pf2.FindNearestSafeTile(pos, true);
1670 if (result1 != result2) {
1671 DEBUG(yapf, 0, "CACHE ERROR: FindSafeTile() = [%s, %s]", result2 ? "T" : "F", result1 ? "T" : "F");
1672 DumpState(pf1, pf2);
1674 #endif
1676 return result1;