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/>.
10 /** @file yapf_rail.cpp The rail pathfinding. */
12 #include "../../stdafx.h"
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
66 ESR_NONE
= 0xFF, ///< no reason to end the segment here
69 enum EndSegmentReasonBits
{
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");
115 /** key for YAPF rail nodes */
116 typedef CYapfNodeKeyTrackDir
<RailPathPos
> CYapfRailKey
;
118 /** key for cached segment cost for rail YAPF */
119 struct CYapfRailSegmentKey
123 inline CYapfRailSegmentKey(const CYapfRailSegmentKey
& src
) : m_value(src
.m_value
) {}
125 inline CYapfRailSegmentKey(const CYapfRailKey
& 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
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
;
165 RailPathPos m_last_signal
;
166 EndSegmentReasonBits m_end_segment_reason
;
168 inline CYapfRailSegment(const CYapfRailSegmentKey
& key
)
173 , m_end_segment_reason(ESRB_NONE
)
176 inline const Key
& GetKey() const
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
;
203 FLAG_LAST_SIGNAL_WAS_RED
,
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
);
217 if (parent
== NULL
) {
218 m_num_signals_passed
= 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
;
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
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
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
312 inline CSegmentCostCacheT() {}
314 /** flush (clear) the cache */
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
);
332 template <class TAstar
>
333 class CYapfRailBaseT
: public TAstar
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
;
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
350 bool m_stopped_on_first_two_way_signal
;
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)
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;
383 /* some statistics */
384 if (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
;
399 CYapfRailBaseT (const Train
*v
, bool allow_90deg
, bool override_rail_type
, bool mask_reserved_tracks
)
400 : m_settings(&_settings_game
.pf
.yapf
)
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())
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
);
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());
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();
450 bool found
= m_global_cache
.Get(key
, &n
->m_segment
);
451 if (!found
) n
->m_segment
->m_last
= n
->GetPos();
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);
461 AttachSegmentToNode(node
);
462 TAstar::InsertInitialNode(node
);
466 void SetOrigin(const RailPathPos
&pos
)
468 AddStartupNode (pos
);
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
;
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
));
528 return (pos1
.td
== pos2
.td
) ? 0 : m_settings
->rail_curve45_penalty
;
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 */
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
;
556 /** Return one tile cost (base cost + level crossing penalty). */
557 inline int OneTileCost (const RailPathPos
&pos
) const
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
;
568 /* non-diagonal trackdir */
569 cost
= YAPF_TILE_CORNER_LENGTH
;
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;
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
);
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;
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);
628 /** Penalty for platform shorter/longer than needed. */
629 inline int PlatformLengthPenalty (int platform_length
) const
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
;
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). */
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
)
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
) {
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
;
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;
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) {
728 case SIGTYPE_EXIT
: cost
+= m_settings
->rail_firstred_exit_penalty
; break; // first signal is red pre-signal-exit
730 case SIGTYPE_ENTRY
: cost
+= m_settings
->rail_firstred_penalty
; 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
;
743 cost
+= n
->m_num_signals_passed
< m_settings
->rail_look_ahead_max_signals
? m_settings
->rail_pbs_signal_back_penalty
: 0;
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
;
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
);
792 if (!ft
.FollowNext()) {
794 add_extra_cost
= !IsWaitingPositionFree(v
, ft
.m_old
, _settings_game
.pf
.forbid_90_deg
);
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;
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
;
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
;
867 segment
->end_reason
|= ESRB_DEAD_END
;
870 if (mask_reserved_tracks
&& !HasOnewaySignalBlockingPos(segment
->pos
)) {
871 segment
->end_reason
|= ESRB_SAFE_TILE
;
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
;
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
;
906 /* Avoid infinite looping. */
907 if (tf
->m_new
== n
->GetPos()) {
908 segment
->end_reason
|= ESRB_INFINITE_LOOP
;
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
;
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)
934 * - YAPF_TILE_LENGTH for diagonal tiles
935 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
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
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 */
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();
970 HandleNodeTile (n
, tf
, &segment
, prev
);
972 /* Move to the next tile/trackdir. */
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
;
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
);
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. */
1038 const BaseStation
*st
= BaseStation::GetByTile(n
->GetLastPos().tile
);
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. */
1069 res
->pos
= node
->GetLastPos();
1071 /* Walk through the path back to the origin. */
1072 CFollowTrackRail
ft (m_veh
, Allow90degTurns(), m_compatible_railtypes
);
1075 Node
*parent
= node
->m_parent
;
1076 assert (parent
!= NULL
);
1078 /* Search node for a safe position. */
1079 ft
.SetPos (node
->GetPos());
1081 if (IsSafeWaitingPosition (m_veh
, ft
.m_new
, !Allow90degTurns())) {
1082 /* Found a safe position in this node. */
1084 res
->pos
= ft
.m_new
;
1088 if (ft
.m_new
== node
->GetLastPos()) break; // no safe position found in node
1090 bool follow
= ft
.FollowNext();
1092 assert (ft
.m_new
.is_single());
1095 /* Stop at node after initial node. */
1096 if (parent
->m_parent
== NULL
) return node
;
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
;
1117 if (HasStationReservation(t
)) {
1118 /* Platform could not be reserved, undo. */
1120 while (t
!= pos
.tile
) {
1121 t
= TILE_ADD(t
, diff
);
1122 SetRailStationReservation(t
, 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
);
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
);
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());
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());
1182 UnreserveSingleTrack (ft
.m_new
, origin
);
1183 if (ft
.m_new
== res
->pos
) break;
1184 if (ft
.m_new
== node
->GetLastPos()) break;
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
);
1198 if (ft
.m_new
== res
->pos
) break;
1199 if (ft
.m_new
== node
->GetLastPos()) break;
1200 bool follow
= ft
.FollowNext();
1202 assert (ft
.m_new
.is_single());
1206 if (CanUseGlobalCache(*res
->node
)) {
1207 YapfNotifyTrackLayoutChange(INVALID_TILE
, INVALID_TRACK
);
1214 template <class TAstar
>
1215 struct CYapfRailOrderT
: CYapfRailBaseT
<TAstar
>
1218 typedef CYapfRailBaseT
<TAstar
> Base
;
1219 typedef typename
TAstar::Node Node
;
1222 TileIndex m_dest_tile
;
1223 StationID m_dest_station_id
;
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
1237 Base::DisableCache(true);
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
);
1246 m_dest_station_id
= INVALID_STATION
;
1247 m_dest_tile
= v
->dest_tile
;
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
));
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
);
1351 Base::m_stats_cost_calcs
++;
1353 Base::m_stats_cache_hits
++;
1356 CPerfStart
perf_cost(Base::m_perf_cost
);
1358 EndSegmentReasonBits end_reason
;
1360 end_reason
= Base::RestoreCachedNode (n
);
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
);
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. */
1381 Base::CalcEstimate(n
);
1382 this->InsertNode(n
);
1387 /** call the node follower */
1388 static inline void Follow (CYapfRailT
*pf
, Node
*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
;
1407 #endif /* !NO_DEBUG_MESSAGES */
1409 bool bDestFound
= Base::FindPath (Follow
, Base::m_settings
->max_search_nodes
);
1411 #ifndef NO_DEBUG_MESSAGES
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 */
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
;
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
) {
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);
1521 /** Pathfind, then optionally try to reserve the path found. */
1522 bool FindNearestSafeTile (const RailPathPos
&pos
, bool reserve
)
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
);
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
);
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
);
1619 typedef CYapfRailT
<CYapfAnyDepotRailT
<AstarRailTrackDir
> > CYapfAnyDepotRail
;
1621 bool YapfTrainFindNearestDepot(const Train
*v
, uint max_penalty
, FindDepotData
*res
)
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
);
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);
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
);