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"
15 #include "yapf_cache.h"
16 #include "yapf_node_rail.hpp"
17 #include "../../viewport_func.h"
18 #include "../../newgrf_station.h"
19 #include "../../station_func.h"
20 #include "../../misc/blob.hpp"
21 #include "../../misc/array.hpp"
22 #include "../../misc/hashtable.hpp"
23 #include "../../map/slope.h"
24 #include "../../pbs.h"
25 #include "../../waypoint_base.h"
26 #include "../../date_func.h"
28 #define DEBUG_YAPF_CACHE 0
31 template <typename Tpf
> void DumpState(Tpf
&pf1
, Tpf
&pf2
)
33 DumpTarget dmp1
, dmp2
;
36 FILE *f1
= fopen("yapf1.txt", "wt");
37 FILE *f2
= fopen("yapf2.txt", "wt");
38 fwrite(dmp1
.m_out
.Data(), 1, dmp1
.m_out
.Size(), f1
);
39 fwrite(dmp2
.m_out
.Data(), 1, dmp2
.m_out
.Size(), f2
);
45 int _total_pf_time_us
= 0;
49 * Base class for segment cost cache providers. Contains global counter
50 * of track layout changes and static notification function called whenever
51 * the track layout changes. It is implemented as base class because it needs
52 * to be shared between all rail YAPF types (one shared counter, one notification
55 struct CSegmentCostCacheBase
57 static int s_rail_change_counter
;
59 static void NotifyTrackLayoutChange(TileIndex tile
, Track track
)
61 s_rail_change_counter
++;
66 * CSegmentCostCacheT - template class providing hash-map and storage (heap)
67 * of Tsegment structures. Each rail node contains pointer to the segment
68 * that contains cached (or non-cached) segment cost information. Nodes can
69 * differ by key type, but they use the same segment type. Segment key should
70 * be always the same (TileIndex + DiagDirection) that represent the beginning
71 * of the segment (origin tile and exit-dir from this tile).
72 * Different CYapfCachedCostT types can share the same type of CSegmentCostCacheT.
73 * Look at CYapfRailSegment (yapf_node_rail.hpp) for the segment example
75 template <class Tsegment
>
76 struct CSegmentCostCacheT
77 : public CSegmentCostCacheBase
79 static const int C_HASH_BITS
= 14;
81 typedef CHashTableT
<Tsegment
, C_HASH_BITS
> HashTable
;
82 typedef SmallArray
<Tsegment
> Heap
;
83 typedef typename
Tsegment::Key Key
; ///< key to hash table
88 inline CSegmentCostCacheT() {}
90 /** flush (clear) the cache */
97 inline Tsegment
& Get(Key
& key
, bool *found
)
99 Tsegment
*item
= m_map
.Find(key
);
102 item
= new (m_heap
.Append()) Tsegment(key
);
112 * CYapfSegmentCostCacheGlobalT - the yapf cost cache provider that adds the segment cost
113 * caching functionality to yapf. Using this class as base of your will provide the global
114 * segment cost caching services for your Nodes.
116 template <class Types
>
117 class CYapfSegmentCostCacheGlobalT
120 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
121 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
122 typedef typename
Node::Key Key
; ///< key to hash tables
123 typedef typename
Node::CachedData CachedData
;
124 typedef typename
CachedData::Key CacheKey
;
125 typedef CSegmentCostCacheT
<CachedData
> Cache
;
126 typedef SmallArray
<CachedData
> LocalCache
;
129 Cache
& m_global_cache
;
130 LocalCache m_local_cache
;
132 inline CYapfSegmentCostCacheGlobalT() : m_global_cache(stGetGlobalCache()) {};
134 /** to access inherited path finder */
137 return *static_cast<Tpf
*>(this);
140 inline static Cache
& stGetGlobalCache()
142 static int last_rail_change_counter
= 0;
143 static Date last_date
= 0;
146 /* some statistics */
147 if (last_date
!= _date
) {
149 DEBUG(yapf
, 2, "Pf time today: %5d ms", _total_pf_time_us
/ 1000);
150 _total_pf_time_us
= 0;
153 /* delete the cache sometimes... */
154 if (last_rail_change_counter
!= Cache::s_rail_change_counter
) {
155 last_rail_change_counter
= Cache::s_rail_change_counter
;
163 * Called by YAPF to attach cached or local segment cost data to the given node.
164 * @return true if globally cached data were used or false if local data was used
166 inline bool PfNodeCacheFetch(Node
& n
)
168 CacheKey
key(n
.GetKey());
169 if (!Yapf().CanUseGlobalCache(n
)) {
170 Yapf().ConnectNodeToCachedData(n
, *new (m_local_cache
.Append()) CachedData(key
));
174 CachedData
& item
= m_global_cache
.Get(key
, &found
);
175 Yapf().ConnectNodeToCachedData(n
, item
);
180 * Called by YAPF to flush the cached segment cost data back into cache storage.
181 * Current cache implementation doesn't use that.
183 inline void PfNodeCacheFlush(Node
& n
)
189 template <class Types
>
193 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
194 typedef typename
Types::TrackFollower TrackFollower
;
195 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
196 typedef typename
Node::Key Key
; ///< key to hash tables
197 typedef typename
Node::CachedData CachedData
;
201 * @note maximum cost doesn't work with caching enabled
202 * @todo fix maximum cost failing with caching (e.g. FS#2900)
205 CBlobT
<int> m_sig_look_ahead_costs
;
206 bool m_disable_cache
;
209 bool m_stopped_on_first_two_way_signal
;
212 static const int s_max_segment_cost
= 10000;
216 , m_disable_cache(false)
217 , m_stopped_on_first_two_way_signal(false)
219 /* pre-compute look-ahead penalties into array */
220 int p0
= Yapf().PfGetSettings().rail_look_ahead_signal_p0
;
221 int p1
= Yapf().PfGetSettings().rail_look_ahead_signal_p1
;
222 int p2
= Yapf().PfGetSettings().rail_look_ahead_signal_p2
;
223 int *pen
= m_sig_look_ahead_costs
.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals
);
224 for (uint i
= 0; i
< Yapf().PfGetSettings().rail_look_ahead_max_signals
; i
++) {
225 pen
[i
] = p0
+ i
* (p1
+ i
* p2
);
229 /** to access inherited path finder */
232 return *static_cast<Tpf
*>(this);
236 /** return debug report character to identify the transportation type */
237 inline char TransportTypeChar() const
242 inline int SlopeCost(const PathPos
&pos
)
244 CPerfStart
perf_cost(Yapf().m_perf_slope_cost
);
246 if (pos
.in_wormhole() || !IsDiagonalTrackdir(pos
.td
)) return 0;
248 /* Only rail tracks and bridgeheads can have sloped rail. */
249 if (!IsRailwayTile(pos
.tile
)) return 0;
252 if (IsTileSubtype(pos
.tile
, TT_BRIDGE
)) {
253 /* it is bridge ramp, check if we are entering the bridge */
254 DiagDirection dir
= GetTunnelBridgeDirection(pos
.tile
);
255 if (dir
!= TrackdirToExitdir(pos
.td
)) return 0; // no, we are leaving it, no penalty
256 /* we are entering the bridge */
257 Slope tile_slope
= GetTileSlope(pos
.tile
);
258 Axis axis
= DiagDirToAxis(dir
);
259 uphill
= !HasBridgeFlatRamp(tile_slope
, axis
);
261 /* not bridge ramp */
262 Slope tile_slope
= GetTileSlope(pos
.tile
);
263 uphill
= IsUphillTrackdir(tile_slope
, pos
.td
); // slopes uphill => apply penalty
266 return uphill
? Yapf().PfGetSettings().rail_slope_penalty
: 0;
269 inline int CurveCost(Trackdir td1
, Trackdir td2
)
271 assert(IsValidTrackdir(td1
));
272 assert(IsValidTrackdir(td2
));
274 if (TrackFollower::Allow90degTurns()
275 && ((TrackdirToTrackdirBits(td2
) & (TrackdirBits
)TrackdirCrossesTrackdirs(td1
)) != 0)) {
276 /* 90-deg curve penalty */
277 cost
+= Yapf().PfGetSettings().rail_curve90_penalty
;
278 } else if (td2
!= NextTrackdir(td1
)) {
279 /* 45-deg curve penalty */
280 cost
+= Yapf().PfGetSettings().rail_curve45_penalty
;
285 inline int SwitchCost(const PathPos
&pos1
, const PathPos
&pos2
)
287 if (!pos1
.in_wormhole() && IsRailwayTile(pos1
.tile
) && !pos2
.in_wormhole() && IsRailwayTile(pos2
.tile
)) {
288 DiagDirection exitdir
= TrackdirToExitdir(pos1
.td
);
289 bool t1
= KillFirstBit(GetTrackBits(pos1
.tile
) & DiagdirReachesTracks(ReverseDiagDir(exitdir
))) != TRACK_BIT_NONE
;
290 bool t2
= KillFirstBit(GetTrackBits(pos2
.tile
) & DiagdirReachesTracks(exitdir
)) != TRACK_BIT_NONE
;
291 if (t1
&& t2
) return Yapf().PfGetSettings().rail_doubleslip_penalty
;
296 /** Return one tile cost (base cost + level crossing penalty). */
297 inline int OneTileCost(const PathPos
&pos
)
301 if (IsDiagonalTrackdir(pos
.td
)) {
302 cost
+= YAPF_TILE_LENGTH
;
303 if (IsLevelCrossingTile(pos
.tile
)) {
304 /* Increase the cost for level crossings */
305 cost
+= Yapf().PfGetSettings().rail_crossing_penalty
;
308 /* non-diagonal trackdir */
309 cost
= YAPF_TILE_CORNER_LENGTH
;
314 /** Check for a reserved station platform. */
315 inline bool IsAnyStationTileReserved(const PathPos
&pos
, int skipped
)
317 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos
.td
)));
318 for (TileIndex tile
= pos
.tile
; skipped
>= 0; skipped
--, tile
+= diff
) {
319 if (HasStationReservation(tile
)) return true;
324 /** The cost for reserved tiles, including skipped ones. */
325 inline int ReservationCost(Node
& n
, const PathPos
&pos
, int skipped
)
327 if (n
.m_num_signals_passed
>= m_sig_look_ahead_costs
.Size() / 2) return 0;
328 if (!IsPbsSignal(n
.m_last_signal_type
)) return 0;
330 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
) && IsAnyStationTileReserved(pos
, skipped
)) {
331 return Yapf().PfGetSettings().rail_pbs_station_penalty
* (skipped
+ 1);
332 } else if (TrackOverlapsTracks(GetReservedTrackbits(pos
.tile
), TrackdirToTrack(pos
.td
))) {
333 int cost
= Yapf().PfGetSettings().rail_pbs_cross_penalty
;
334 if (!IsDiagonalTrackdir(pos
.td
)) cost
= (cost
* YAPF_TILE_CORNER_LENGTH
) / YAPF_TILE_LENGTH
;
335 return cost
* (skipped
+ 1);
340 int SignalCost(Node
& n
, const PathPos
&pos
)
343 /* if there is one-way signal in the opposite direction, then it is not our way */
344 CPerfStart
perf_cost(Yapf().m_perf_other_cost
);
346 if (HasSignalAlongPos(pos
)) {
347 SignalState sig_state
= GetSignalStateByPos(pos
);
348 SignalType sig_type
= GetSignalType(pos
);
350 n
.m_last_signal_type
= sig_type
;
352 /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */
353 int look_ahead_cost
= (n
.m_num_signals_passed
< m_sig_look_ahead_costs
.Size()) ? m_sig_look_ahead_costs
.Data()[n
.m_num_signals_passed
] : 0;
354 if (sig_state
!= SIGNAL_STATE_RED
) {
356 n
.flags_u
.flags_s
.m_last_signal_was_red
= false;
357 /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */
358 if (look_ahead_cost
< 0) {
359 /* add its negation to the cost */
360 cost
-= look_ahead_cost
;
363 /* we have a red signal in our direction
364 * was it first signal which is two-way? */
365 if (!IsPbsSignal(sig_type
) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n
.flags_u
.flags_s
.m_choice_seen
&& HasSignalAgainstPos(pos
) && n
.m_num_signals_passed
== 0) {
366 /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
367 Yapf().PruneIntermediateNodeBranch();
368 n
.m_segment
->m_end_segment_reason
|= ESRB_DEAD_END
;
369 Yapf().m_stopped_on_first_two_way_signal
= true;
372 n
.m_last_red_signal_type
= sig_type
;
373 n
.flags_u
.flags_s
.m_last_signal_was_red
= true;
375 /* look-ahead signal penalty */
376 if (!IsPbsSignal(sig_type
) && look_ahead_cost
> 0) {
377 /* add the look ahead penalty only if it is positive */
378 cost
+= look_ahead_cost
;
381 /* special signal penalties */
382 if (n
.m_num_signals_passed
== 0) {
385 case SIGTYPE_EXIT
: cost
+= Yapf().PfGetSettings().rail_firstred_exit_penalty
; break; // first signal is red pre-signal-exit
387 case SIGTYPE_ENTRY
: cost
+= Yapf().PfGetSettings().rail_firstred_penalty
; break;
393 n
.m_num_signals_passed
++;
394 n
.m_segment
->m_last_signal
= pos
;
395 } else if (HasSignalAgainstPos(pos
)) {
396 if (GetSignalType(pos
) != SIGTYPE_PBS
) {
397 /* one-way signal in opposite direction */
398 n
.m_segment
->m_end_segment_reason
|= ESRB_DEAD_END
;
400 cost
+= n
.m_num_signals_passed
< Yapf().PfGetSettings().rail_look_ahead_max_signals
? Yapf().PfGetSettings().rail_pbs_signal_back_penalty
: 0;
407 inline int PlatformLengthPenalty(int platform_length
)
410 const Train
*v
= Yapf().GetVehicle();
412 assert(v
->type
== VEH_TRAIN
);
413 assert(v
->gcache
.cached_total_length
!= 0);
414 int missing_platform_length
= CeilDiv(v
->gcache
.cached_total_length
, TILE_SIZE
) - platform_length
;
415 if (missing_platform_length
< 0) {
416 /* apply penalty for longer platform than needed */
417 cost
+= Yapf().PfGetSettings().rail_longer_platform_penalty
+ Yapf().PfGetSettings().rail_longer_platform_per_tile_penalty
* -missing_platform_length
;
418 } else if (missing_platform_length
> 0) {
419 /* apply penalty for shorter platform than needed */
420 cost
+= Yapf().PfGetSettings().rail_shorter_platform_penalty
+ Yapf().PfGetSettings().rail_shorter_platform_per_tile_penalty
* missing_platform_length
;
426 inline void SetMaxCost(int max_cost
)
428 m_max_cost
= max_cost
;
432 * Called by YAPF to calculate the cost from the origin to the given node.
433 * Calculates only the cost of given node, adds it to the parent node cost
434 * and stores the result into Node::m_cost member
436 inline bool PfCalcCost(Node
&n
, const TrackFollower
*tf
)
438 assert(!n
.flags_u
.flags_s
.m_targed_seen
);
439 assert(tf
->m_new
.tile
== n
.GetPos().tile
);
440 assert(tf
->m_new
.wormhole
== n
.GetPos().wormhole
);
441 assert((TrackdirToTrackdirBits(n
.GetPos().td
) & tf
->m_new
.trackdirs
) != TRACKDIR_BIT_NONE
);
443 CPerfStart
perf_cost(Yapf().m_perf_cost
);
445 /* Does the node have some parent node? */
446 bool has_parent
= (n
.m_parent
!= NULL
);
448 /* Do we already have a cached segment? */
449 CachedData
&segment
= *n
.m_segment
;
450 bool is_cached_segment
= (segment
.m_cost
>= 0);
452 int parent_cost
= has_parent
? n
.m_parent
->m_cost
: 0;
454 /* Each node cost contains 2 or 3 main components:
455 * 1. Transition cost - cost of the move from previous node (tile):
456 * - curve cost (or zero for straight move)
459 * - YAPF_TILE_LENGTH for diagonal tiles
460 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
462 * - tile slope penalty (upward slopes)
463 * - red signal penalty
464 * - level crossing penalty
465 * - speed-limit penalty (bridges)
466 * - station platform penalty
467 * - penalty for reversing in the depot
469 * 3. Extra cost (applies to the last node only)
470 * - last red signal penalty
471 * - penalty for too long or too short platform on the destination station
475 /* Segment: one or more tiles connected by contiguous tracks of the same type.
476 * Each segment cost includes 'Tile cost' for all its tiles (including the first
477 * and last), and the 'Transition cost' between its tiles. The first transition
478 * cost of segment entry (move from the 'parent' node) is not included!
480 int segment_entry_cost
= 0;
481 int segment_cost
= 0;
483 const Train
*v
= Yapf().GetVehicle();
485 /* start at n and walk to the end of segment */
486 PathPos
cur(n
.GetPos());
488 RailType rail_type
= !cur
.in_wormhole() ? GetTileRailType(cur
.tile
, TrackdirToTrack(cur
.td
)) :
489 IsRailwayTile(cur
.wormhole
) ? GetBridgeRailType(cur
.wormhole
) : GetRailType(cur
.wormhole
);
491 EndSegmentReasonBits end_segment_reason
= ESRB_NONE
;
493 TrackFollower
tf_local(v
, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost
);
498 /* First transition cost goes to segment entry cost */
499 PathPos ppos
= n
.m_parent
->GetLastPos();
500 segment_entry_cost
= CurveCost(ppos
.td
, cur
.td
);
501 segment_entry_cost
+= SwitchCost(ppos
, cur
);
503 /* It is the right time now to look if we can reuse the cached segment cost. */
504 if (is_cached_segment
) {
505 /* Yes, we already know the segment cost. */
506 segment_cost
= segment
.m_cost
;
507 /* We know also the reason why the segment ends. */
508 end_segment_reason
= segment
.m_end_segment_reason
;
509 /* We will need also some information about the last signal (if it was red). */
510 if (segment
.m_last_signal
.tile
!= INVALID_TILE
) {
511 assert(HasSignalAlongPos(segment
.m_last_signal
));
512 SignalState sig_state
= GetSignalStateByPos(segment
.m_last_signal
);
513 bool is_red
= (sig_state
== SIGNAL_STATE_RED
);
514 n
.flags_u
.flags_s
.m_last_signal_was_red
= is_red
;
516 n
.m_last_red_signal_type
= GetSignalType(segment
.m_last_signal
);
519 /* No further calculation needed. */
520 cur
= n
.GetLastPos();
526 assert(!is_cached_segment
);
531 /* All other tile costs will be calculated here. */
532 segment_cost
+= OneTileCost(cur
);
534 /* If we skipped some tunnel/bridge/station tiles, add their base cost */
535 segment_cost
+= YAPF_TILE_LENGTH
* tf
->m_tiles_skipped
;
538 segment_cost
+= SlopeCost(cur
);
540 /* Signal cost (routine can modify segment data). */
541 segment_cost
+= SignalCost(n
, cur
);
543 /* Reserved tiles. */
544 segment_cost
+= ReservationCost(n
, cur
, tf
->m_tiles_skipped
);
546 end_segment_reason
= segment
.m_end_segment_reason
;
548 /* Tests for 'potential target' reasons to close the segment. */
549 if (cur
.tile
== prev
) {
550 /* Penalty for reversing in a depot. */
551 assert(!cur
.in_wormhole());
552 assert(IsRailDepotTile(cur
.tile
));
553 assert(cur
.td
== DiagDirToDiagTrackdir(GetGroundDepotDirection(cur
.tile
)));
554 segment_cost
+= Yapf().PfGetSettings().rail_depot_reverse_penalty
;
555 /* We will end in this pass (depot is possible target) */
556 end_segment_reason
|= ESRB_DEPOT
;
558 } else if (!cur
.in_wormhole() && IsRailWaypointTile(cur
.tile
)) {
559 if (v
->current_order
.IsType(OT_GOTO_WAYPOINT
) &&
560 GetStationIndex(cur
.tile
) == v
->current_order
.GetDestination() &&
561 !Waypoint::Get(v
->current_order
.GetDestination())->IsSingleTile()) {
562 /* This waypoint is our destination; maybe this isn't an unreserved
563 * one, so check that and if so see that as the last signal being
564 * red. This way waypoints near stations should work better. */
565 CFollowTrackRail
ft(v
);
570 if (!ft
.FollowNext()) {
572 add_extra_cost
= !IsWaitingPositionFree(v
, ft
.m_old
, _settings_game
.pf
.forbid_90_deg
);
576 assert(ft
.m_old
.tile
!= ft
.m_new
.tile
);
577 if (!ft
.m_new
.is_single()) {
578 /* We encountered a junction; it's going to be too complex to
579 * handle this perfectly, so just bail out. There is no simple
580 * free path, so try the other possibilities. */
581 add_extra_cost
= true;
585 /* If this is a safe waiting position we're done searching for it */
586 PBSPositionState state
= CheckWaitingPosition (v
, ft
.m_new
, _settings_game
.pf
.forbid_90_deg
);
587 if (state
!= PBS_UNSAFE
) {
588 add_extra_cost
= state
== PBS_BUSY
;
593 /* In the case this platform is (possibly) occupied we add penalty so the
594 * other platforms of this waypoint are evaluated as well, i.e. we assume
595 * that there is a red signal in the waypoint when it's occupied. */
596 if (add_extra_cost
) extra_cost
+= Yapf().PfGetSettings().rail_lastred_penalty
;
598 /* Waypoint is also a good reason to finish. */
599 end_segment_reason
|= ESRB_WAYPOINT
;
601 } else if (tf
->m_flag
== tf
->TF_STATION
) {
602 /* Station penalties. */
603 uint platform_length
= tf
->m_tiles_skipped
+ 1;
604 /* We don't know yet if the station is our target or not. Act like
605 * if it is pass-through station (not our destination). */
606 segment_cost
+= Yapf().PfGetSettings().rail_station_penalty
* platform_length
;
607 /* We will end in this pass (station is possible target) */
608 end_segment_reason
|= ESRB_STATION
;
610 } else if (TrackFollower::DoTrackMasking()) {
611 /* Searching for a safe tile? */
612 if (HasSignalAlongPos(cur
) && !IsPbsSignal(GetSignalType(cur
))) {
613 end_segment_reason
|= ESRB_SAFE_TILE
;
617 /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
618 * it would cause desync in MP. */
619 if (n
.m_num_signals_passed
< m_sig_look_ahead_costs
.Size())
622 int max_speed
= tf
->GetSpeedLimit(&min_speed
);
623 int max_veh_speed
= v
->GetDisplayMaxSpeed();
624 if (max_speed
< max_veh_speed
) {
625 extra_cost
+= YAPF_TILE_LENGTH
* (max_veh_speed
- max_speed
) * (1 + tf
->m_tiles_skipped
) / max_veh_speed
;
627 if (min_speed
> max_veh_speed
) {
628 extra_cost
+= YAPF_TILE_LENGTH
* (min_speed
- max_veh_speed
);
632 /* Finish if we already exceeded the maximum path cost (i.e. when
633 * searching for the nearest depot). */
634 if (m_max_cost
> 0 && (parent_cost
+ segment_entry_cost
+ segment_cost
) > m_max_cost
) {
635 end_segment_reason
|= ESRB_PATH_TOO_LONG
;
638 /* Move to the next tile/trackdir. */
640 assert(tf_local
.m_veh_owner
== v
->owner
);
641 assert(tf_local
.m_railtypes
== Yapf().GetCompatibleRailTypes());
642 assert(tf_local
.m_pPerf
== &Yapf().m_perf_ts_cost
);
644 if (!tf_local
.Follow(cur
)) {
645 assert(tf_local
.m_err
!= TrackFollower::EC_NONE
);
646 /* Can't move to the next tile (EOL?). */
647 if (tf_local
.m_err
== TrackFollower::EC_RAIL_TYPE
) {
648 end_segment_reason
|= ESRB_RAIL_TYPE
;
650 end_segment_reason
|= ESRB_DEAD_END
;
653 if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingPos(cur
)) {
654 end_segment_reason
|= ESRB_SAFE_TILE
;
659 /* Check if the next tile is not a choice. */
660 if (!tf_local
.m_new
.is_single()) {
661 /* More than one segment will follow. Close this one. */
662 end_segment_reason
|= ESRB_CHOICE_FOLLOWS
;
666 /* Gather the next tile/trackdir. */
667 PathPos
next(tf_local
.m_new
);
669 if (TrackFollower::DoTrackMasking()) {
670 if (HasSignalAlongPos(next
) && IsPbsSignal(GetSignalType(next
))) {
671 /* Possible safe tile. */
672 end_segment_reason
|= ESRB_SAFE_TILE
;
673 } else if (HasSignalAgainstPos(next
) && GetSignalType(next
) == SIGTYPE_PBS_ONEWAY
) {
674 /* Possible safe tile, but not so good as it's the back of a signal... */
675 end_segment_reason
|= ESRB_SAFE_TILE
| ESRB_DEAD_END
;
676 extra_cost
+= Yapf().PfGetSettings().rail_lastred_exit_penalty
;
680 /* Check the next tile for the rail type. */
681 if (next
.in_wormhole()) {
682 RailType next_rail_type
= IsRailwayTile(next
.wormhole
) ? GetBridgeRailType(next
.wormhole
) : GetRailType(next
.wormhole
);
683 assert(next_rail_type
== rail_type
);
684 } else if (GetTileRailType(next
.tile
, TrackdirToTrack(next
.td
)) != rail_type
) {
685 /* Segment must consist from the same rail_type tiles. */
686 end_segment_reason
|= ESRB_RAIL_TYPE
;
690 /* Avoid infinite looping. */
691 if (next
== n
.GetPos()) {
692 end_segment_reason
|= ESRB_INFINITE_LOOP
;
696 if (segment_cost
> s_max_segment_cost
) {
697 /* Potentially in the infinite loop (or only very long segment?). We should
698 * not force it to finish prematurely unless we are on a regular tile. */
699 if (!tf
->m_new
.in_wormhole() && IsNormalRailTile(tf
->m_new
.tile
)) {
700 end_segment_reason
|= ESRB_SEGMENT_TOO_LONG
;
705 /* Any other reason bit set? */
706 if (end_segment_reason
!= ESRB_NONE
) {
710 /* Transition cost (cost of the move from previous tile) */
711 segment_cost
+= CurveCost(cur
.td
, next
.td
);
712 segment_cost
+= SwitchCost(cur
, next
);
714 /* For the next loop set new prev and cur tile info. */
721 bool target_seen
= false;
722 if ((end_segment_reason
& ESRB_POSSIBLE_TARGET
) != ESRB_NONE
) {
723 /* Depot, station or waypoint. */
724 if (Yapf().PfDetectDestination(cur
)) {
725 /* Destination found. */
730 /* Update the segment if needed. */
731 if (!is_cached_segment
) {
732 /* Write back the segment information so it can be reused the next time. */
733 segment
.m_cost
= segment_cost
;
734 segment
.m_end_segment_reason
= end_segment_reason
& ESRB_CACHED_MASK
;
735 /* Save end of segment back to the node. */
739 /* Do we have an excuse why not to continue pathfinding in this direction? */
740 if (!target_seen
&& (end_segment_reason
& ESRB_ABORT_PF_MASK
) != ESRB_NONE
) {
741 /* Reason to not continue. Stop this PF branch. */
745 /* Special costs for the case we have reached our target. */
747 n
.flags_u
.flags_s
.m_targed_seen
= true;
748 /* Last-red and last-red-exit penalties. */
749 if (n
.flags_u
.flags_s
.m_last_signal_was_red
) {
750 if (n
.m_last_red_signal_type
== SIGTYPE_EXIT
) {
751 /* last signal was red pre-signal-exit */
752 extra_cost
+= Yapf().PfGetSettings().rail_lastred_exit_penalty
;
753 } else if (!IsPbsSignal(n
.m_last_red_signal_type
)) {
754 /* Last signal was red, but not exit or path signal. */
755 extra_cost
+= Yapf().PfGetSettings().rail_lastred_penalty
;
759 /* Station platform-length penalty. */
760 if ((end_segment_reason
& ESRB_STATION
) != ESRB_NONE
) {
761 const BaseStation
*st
= BaseStation::GetByTile(n
.GetLastPos().tile
);
763 uint platform_length
= st
->GetPlatformLength(n
.GetLastPos().tile
, ReverseDiagDir(TrackdirToExitdir(n
.GetLastPos().td
)));
764 /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
765 extra_cost
-= Yapf().PfGetSettings().rail_station_penalty
* platform_length
;
766 /* Add penalty for the inappropriate platform length. */
767 extra_cost
+= PlatformLengthPenalty(platform_length
);
771 /* total node cost */
772 n
.m_cost
= parent_cost
+ segment_entry_cost
+ segment_cost
+ extra_cost
;
777 inline bool CanUseGlobalCache(Node
& n
) const
779 return !m_disable_cache
780 && (n
.m_parent
!= NULL
)
781 && (n
.m_parent
->m_num_signals_passed
>= m_sig_look_ahead_costs
.Size());
784 inline void ConnectNodeToCachedData(Node
& n
, CachedData
& ci
)
787 if (n
.m_segment
->m_cost
< 0) {
788 n
.m_segment
->m_last
= n
.GetPos();
792 void DisableCache(bool disable
)
794 m_disable_cache
= disable
;
799 class CYapfDestinationRailBase
802 RailTypes m_compatible_railtypes
;
805 void SetDestination(const Train
*v
, bool override_rail_type
= false)
807 m_compatible_railtypes
= v
->compatible_railtypes
;
808 if (override_rail_type
) m_compatible_railtypes
|= GetRailTypeInfo(v
->railtype
)->compatible_railtypes
;
811 bool IsCompatibleRailType(RailType rt
)
813 return HasBit(m_compatible_railtypes
, rt
);
816 RailTypes
GetCompatibleRailTypes() const
818 return m_compatible_railtypes
;
822 template <class Types
>
823 class CYapfDestinationAnyDepotRailT
824 : public CYapfDestinationRailBase
827 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
828 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
829 typedef typename
Node::Key Key
; ///< key to hash tables
831 /** to access inherited path finder */
834 return *static_cast<Tpf
*>(this);
837 /** Called by YAPF to detect if node ends in the desired destination */
838 inline bool PfDetectDestination(Node
& n
)
840 return PfDetectDestination(n
.GetLastPos());
843 /** Called by YAPF to detect if node ends in the desired destination */
844 inline bool PfDetectDestination(const PathPos
&pos
)
846 return !pos
.in_wormhole() && IsRailDepotTile(pos
.tile
);
850 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
851 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
853 inline bool PfCalcEstimate(Node
& n
)
855 n
.m_estimate
= n
.m_cost
;
860 template <class Types
>
861 class CYapfDestinationAnySafeTileRailT
862 : public CYapfDestinationRailBase
865 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
866 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
867 typedef typename
Node::Key Key
; ///< key to hash tables
868 typedef typename
Types::TrackFollower TrackFollower
; ///< TrackFollower. Need to typedef for gcc 2.95
870 /** to access inherited path finder */
873 return *static_cast<Tpf
*>(this);
876 /** Called by YAPF to detect if node ends in the desired destination */
877 inline bool PfDetectDestination(Node
& n
)
879 return PfDetectDestination(n
.GetLastPos());
882 /** Called by YAPF to detect if node ends in the desired destination */
883 inline bool PfDetectDestination(const PathPos
&pos
)
885 return IsFreeSafeWaitingPosition(Yapf().GetVehicle(), pos
, !TrackFollower::Allow90degTurns());
889 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
890 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate.
892 inline bool PfCalcEstimate(Node
& n
)
894 n
.m_estimate
= n
.m_cost
;
899 template <class Types
>
900 class CYapfDestinationTileOrStationRailT
901 : public CYapfDestinationRailBase
904 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
905 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
906 typedef typename
Node::Key Key
; ///< key to hash tables
909 TileIndex m_destTile
;
910 StationID m_dest_station_id
;
912 /** to access inherited path finder */
915 return *static_cast<Tpf
*>(this);
919 void SetDestination(const Train
*v
)
921 switch (v
->current_order
.GetType()) {
922 case OT_GOTO_WAYPOINT
:
923 if (!Waypoint::Get(v
->current_order
.GetDestination())->IsSingleTile()) {
924 /* In case of 'complex' waypoints we need to do a look
925 * ahead. This look ahead messes a bit about, which
926 * means that it 'corrupts' the cache. To prevent this
927 * we disable caching when we're looking for a complex
929 Yapf().DisableCache(true);
932 case OT_GOTO_STATION
:
933 m_dest_station_id
= v
->current_order
.GetDestination();
934 m_destTile
= BaseStation::Get(m_dest_station_id
)->GetClosestTile(v
->tile
, v
->current_order
.IsType(OT_GOTO_STATION
) ? STATION_RAIL
: STATION_WAYPOINT
);
938 m_destTile
= v
->dest_tile
;
939 m_dest_station_id
= INVALID_STATION
;
942 CYapfDestinationRailBase::SetDestination(v
);
945 /** Called by YAPF to detect if node ends in the desired destination */
946 inline bool PfDetectDestination(Node
& n
)
948 return PfDetectDestination(n
.GetLastPos());
951 /** Called by YAPF to detect if node ends in the desired destination */
952 inline bool PfDetectDestination(const PathPos
&pos
)
955 if (m_dest_station_id
!= INVALID_STATION
) {
956 bDest
= !pos
.in_wormhole() && HasStationTileRail(pos
.tile
)
957 && (GetStationIndex(pos
.tile
) == m_dest_station_id
)
958 && (GetRailStationTrack(pos
.tile
) == TrackdirToTrack(pos
.td
));
960 bDest
= !pos
.in_wormhole() && (pos
.tile
== m_destTile
);
966 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
967 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
969 inline bool PfCalcEstimate(Node
& n
)
971 static const int dg_dir_to_x_offs
[] = {-1, 0, 1, 0};
972 static const int dg_dir_to_y_offs
[] = {0, 1, 0, -1};
973 if (PfDetectDestination(n
)) {
974 n
.m_estimate
= n
.m_cost
;
978 TileIndex tile
= n
.GetLastPos().tile
;
979 DiagDirection exitdir
= TrackdirToExitdir(n
.GetLastPos().td
);
980 int x1
= 2 * TileX(tile
) + dg_dir_to_x_offs
[(int)exitdir
];
981 int y1
= 2 * TileY(tile
) + dg_dir_to_y_offs
[(int)exitdir
];
982 int x2
= 2 * TileX(m_destTile
);
983 int y2
= 2 * TileY(m_destTile
);
984 int dx
= abs(x1
- x2
);
985 int dy
= abs(y1
- y2
);
986 int dmin
= min(dx
, dy
);
987 int dxy
= abs(dx
- dy
);
988 int d
= dmin
* YAPF_TILE_CORNER_LENGTH
+ (dxy
- 1) * (YAPF_TILE_LENGTH
/ 2);
989 n
.m_estimate
= n
.m_cost
+ d
;
990 assert(n
.m_estimate
>= n
.m_parent
->m_estimate
);
996 template <class Types
>
997 class CYapfReserveTrack
1000 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1001 typedef typename
Types::TrackFollower TrackFollower
;
1002 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1005 /** to access inherited pathfinder */
1008 return *static_cast<Tpf
*>(this);
1012 PathPos m_res_dest
; ///< The reservation target
1013 Node
*m_res_node
; ///< The reservation target node
1014 TileIndex m_origin_tile
; ///< Tile our reservation will originate from
1016 bool FindSafePositionProc(const PathPos
&pos
, PathPos
*)
1018 if (IsSafeWaitingPosition(Yapf().GetVehicle(), pos
, !TrackFollower::Allow90degTurns())) {
1020 return false; // Stop iterating segment
1025 /** Try to reserve a single track/platform. */
1026 bool ReserveSingleTrack(const PathPos
&pos
, PathPos
*fail
)
1028 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
)) {
1029 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos
.td
)));
1030 TileIndex t
= pos
.tile
;
1033 if (HasStationReservation(t
)) {
1034 /* Platform could not be reserved, undo. */
1035 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(pos
.td
));
1036 while (t
!= pos
.tile
) {
1037 t
= TILE_ADD(t
, diff
);
1038 SetRailStationReservation(t
, false);
1043 SetRailStationReservation(t
, true);
1044 MarkTileDirtyByTile(t
);
1045 t
= TILE_ADD(t
, diff
);
1046 } while (IsCompatibleTrainStationTile(t
, pos
.tile
) && t
!= m_origin_tile
);
1048 TriggerStationRandomisation(NULL
, pos
.tile
, SRT_PATH_RESERVATION
);
1050 if (!TryReserveRailTrack(pos
)) {
1051 /* Tile couldn't be reserved, undo. */
1057 return pos
!= m_res_dest
;
1060 /** Unreserve a single track/platform. Stops when the previous failer is reached. */
1061 bool UnreserveSingleTrack(const PathPos
&pos
, PathPos
*stop
)
1063 if (stop
!= NULL
&& pos
== *stop
) return false;
1065 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
)) {
1066 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos
.td
)));
1067 TileIndex t
= pos
.tile
;
1068 while (IsCompatibleTrainStationTile(t
, pos
.tile
) && t
!= m_origin_tile
) {
1069 assert(HasStationReservation(t
));
1070 SetRailStationReservation(t
, false);
1071 t
= TILE_ADD(t
, diff
);
1074 UnreserveRailTrack(pos
);
1077 return pos
!= m_res_dest
;
1081 /** Set the target to where the reservation should be extended. */
1082 inline void SetReservationTarget(Node
*node
, const PathPos
&pos
)
1088 /** Check the node for a possible reservation target. */
1089 inline void FindSafePositionOnNode(Node
*node
)
1091 assert(node
->m_parent
!= NULL
);
1093 /* We will never pass more than two signals, no need to check for a safe tile. */
1094 if (node
->m_parent
->m_num_signals_passed
>= 2) return;
1096 if (!node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::FindSafePositionProc
)) {
1101 /** Try to reserve the path till the reservation target. */
1102 bool TryReservePath(TileIndex origin
, PathPos
*target
= NULL
)
1104 m_origin_tile
= origin
;
1106 if (target
!= NULL
) *target
= m_res_dest
;
1108 /* Don't bother if the target is reserved. */
1109 if (!IsWaitingPositionFree(Yapf().GetVehicle(), m_res_dest
)) return false;
1113 for (Node
*node
= m_res_node
; node
->m_parent
!= NULL
; node
= node
->m_parent
) {
1114 node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::ReserveSingleTrack
, &res_fail
);
1115 if (res_fail
.tile
!= INVALID_TILE
) {
1116 /* Reservation failed, undo. */
1117 Node
*failed_node
= node
;
1118 for (node
= m_res_node
; node
!= failed_node
; node
= node
->m_parent
) {
1119 node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::UnreserveSingleTrack
, NULL
);
1121 failed_node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::UnreserveSingleTrack
, &res_fail
);
1126 if (Yapf().CanUseGlobalCache(*m_res_node
)) {
1127 YapfNotifyTrackLayoutChange(INVALID_TILE
, INVALID_TRACK
);
1134 template <class Types
>
1135 class CYapfFollowAnyDepotRailT
1138 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1139 typedef typename
Types::TrackFollower TrackFollower
;
1140 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1141 typedef typename
Node::Key Key
; ///< key to hash tables
1144 /** to access inherited path finder */
1147 return *static_cast<Tpf
*>(this);
1152 * Called by YAPF to move from the given node to the next tile. For each
1153 * reachable trackdir on the new tile creates new node, initializes it
1154 * and adds it to the open list by calling Yapf().AddNewNode(n)
1156 inline void PfFollowNode(Node
& old_node
)
1158 TrackFollower
F(Yapf().GetVehicle());
1159 if (F
.Follow(old_node
.GetLastPos())) {
1160 Yapf().AddMultipleNodes(&old_node
, F
);
1164 static bool stFindNearestDepotTwoWay(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int max_penalty
, int reverse_penalty
, TileIndex
*depot_tile
, bool *reversed
)
1168 * With caching enabled it simply cannot get a reliable result when you
1169 * have limited the distance a train may travel. This means that the
1170 * cached result does not match uncached result in all cases and that
1171 * causes desyncs. So disable caching when finding for a depot that is
1172 * nearby. This only happens with automatic servicing of vehicles,
1173 * so it will only impact performance when you do not manually set
1174 * depot orders and you do not disable automatic servicing.
1176 if (max_penalty
!= 0) pf1
.DisableCache(true);
1177 bool result1
= pf1
.FindNearestDepotTwoWay(v
, pos1
, pos2
, max_penalty
, reverse_penalty
, depot_tile
, reversed
);
1179 #if DEBUG_YAPF_CACHE
1181 TileIndex depot_tile2
= INVALID_TILE
;
1182 bool reversed2
= false;
1183 pf2
.DisableCache(true);
1184 bool result2
= pf2
.FindNearestDepotTwoWay(v
, pos1
, pos2
, max_penalty
, reverse_penalty
, &depot_tile2
, &reversed2
);
1185 if (result1
!= result2
|| (result1
&& (*depot_tile
!= depot_tile2
|| *reversed
!= reversed2
))) {
1186 DEBUG(yapf
, 0, "CACHE ERROR: FindNearestDepotTwoWay() = [%s, %s]", result1
? "T" : "F", result2
? "T" : "F");
1187 DumpState(pf1
, pf2
);
1194 inline bool FindNearestDepotTwoWay(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int max_penalty
, int reverse_penalty
, TileIndex
*depot_tile
, bool *reversed
)
1196 /* set origin and destination nodes */
1197 Yapf().SetOrigin(pos1
, pos2
, reverse_penalty
, true);
1198 Yapf().SetDestination(v
);
1199 Yapf().SetMaxCost(max_penalty
);
1201 /* find the best path */
1202 bool bFound
= Yapf().FindPath(v
);
1203 if (!bFound
) return false;
1206 * get found depot tile */
1207 Node
*n
= Yapf().GetBestNode();
1208 *depot_tile
= n
->GetLastPos().tile
;
1210 /* walk through the path back to the origin */
1212 while (pNode
->m_parent
!= NULL
) {
1213 pNode
= pNode
->m_parent
;
1216 /* if the origin node is our front vehicle tile/Trackdir then we didn't reverse
1217 * but we can also look at the cost (== 0 -> not reversed, == reverse_penalty -> reversed) */
1218 *reversed
= (pNode
->m_cost
!= 0);
1224 template <class Types
>
1225 class CYapfFollowAnySafeTileRailT
: public CYapfReserveTrack
<Types
>
1228 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1229 typedef typename
Types::TrackFollower TrackFollower
;
1230 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1231 typedef typename
Node::Key Key
; ///< key to hash tables
1234 /** to access inherited path finder */
1237 return *static_cast<Tpf
*>(this);
1242 * Called by YAPF to move from the given node to the next tile. For each
1243 * reachable trackdir on the new tile creates new node, initializes it
1244 * and adds it to the open list by calling Yapf().AddNewNode(n)
1246 inline void PfFollowNode(Node
& old_node
)
1248 TrackFollower
F(Yapf().GetVehicle(), Yapf().GetCompatibleRailTypes());
1249 if (F
.Follow(old_node
.GetLastPos()) && F
.MaskReservedTracks()) {
1250 Yapf().AddMultipleNodes(&old_node
, F
);
1254 static bool stFindNearestSafeTile(const Train
*v
, const PathPos
&pos
, bool override_railtype
)
1256 /* Create pathfinder instance */
1258 #if !DEBUG_YAPF_CACHE
1259 bool result1
= pf1
.FindNearestSafeTile(v
, pos
, override_railtype
, false);
1262 bool result2
= pf1
.FindNearestSafeTile(v
, pos
, override_railtype
, true);
1264 pf2
.DisableCache(true);
1265 bool result1
= pf2
.FindNearestSafeTile(v
, pos
, override_railtype
, false);
1266 if (result1
!= result2
) {
1267 DEBUG(yapf
, 0, "CACHE ERROR: FindSafeTile() = [%s, %s]", result2
? "T" : "F", result1
? "T" : "F");
1268 DumpState(pf1
, pf2
);
1275 bool FindNearestSafeTile(const Train
*v
, const PathPos
&pos
, bool override_railtype
, bool dont_reserve
)
1277 /* Set origin and destination. */
1278 Yapf().SetOrigin(pos
);
1279 Yapf().SetDestination(v
, override_railtype
);
1281 bool bFound
= Yapf().FindPath(v
);
1282 if (!bFound
) return false;
1284 /* Found a destination, set as reservation target. */
1285 Node
*pNode
= Yapf().GetBestNode();
1286 this->SetReservationTarget(pNode
, pNode
->GetLastPos());
1288 /* Walk through the path back to the origin. */
1290 while (pNode
->m_parent
!= NULL
) {
1292 pNode
= pNode
->m_parent
;
1294 this->FindSafePositionOnNode(pPrev
);
1297 return dont_reserve
|| this->TryReservePath(pNode
->GetLastPos().tile
);
1301 template <class Types
>
1302 class CYapfFollowRailT
: public CYapfReserveTrack
<Types
>
1305 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1306 typedef typename
Types::TrackFollower TrackFollower
;
1307 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1308 typedef typename
Node::Key Key
; ///< key to hash tables
1311 /** to access inherited path finder */
1314 return *static_cast<Tpf
*>(this);
1319 * Called by YAPF to move from the given node to the next tile. For each
1320 * reachable trackdir on the new tile creates new node, initializes it
1321 * and adds it to the open list by calling Yapf().AddNewNode(n)
1323 inline void PfFollowNode(Node
& old_node
)
1325 TrackFollower
F(Yapf().GetVehicle());
1326 if (F
.Follow(old_node
.GetLastPos())) {
1327 Yapf().AddMultipleNodes(&old_node
, F
);
1331 static Trackdir
stChooseRailTrack(const Train
*v
, const PathPos
&origin
, bool reserve_track
, PFResult
*target
)
1333 /* create pathfinder instance */
1335 #if !DEBUG_YAPF_CACHE
1336 Trackdir result1
= pf1
.ChooseRailTrack(v
, origin
, reserve_track
, target
);
1339 Trackdir result1
= pf1
.ChooseRailTrack(v
, origin
, false, NULL
);
1341 pf2
.DisableCache(true);
1342 Trackdir result2
= pf2
.ChooseRailTrack(v
, origin
, reserve_track
, target
);
1343 if (result1
!= result2
) {
1344 DEBUG(yapf
, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1
, result2
);
1345 DumpState(pf1
, pf2
);
1352 inline Trackdir
ChooseRailTrack(const Train
*v
, const PathPos
&origin
, bool reserve_track
, PFResult
*target
)
1354 if (target
!= NULL
) target
->pos
.tile
= INVALID_TILE
;
1356 /* set origin and destination nodes */
1357 Yapf().SetOrigin(origin
);
1358 Yapf().SetDestination(v
);
1360 /* find the best path */
1361 bool path_found
= Yapf().FindPath(v
);
1363 /* if path not found - return INVALID_TRACKDIR */
1364 Trackdir next_trackdir
= INVALID_TRACKDIR
;
1365 Node
*pNode
= Yapf().GetBestNode();
1366 if (pNode
!= NULL
) {
1367 /* reserve till end of path */
1368 this->SetReservationTarget(pNode
, pNode
->GetLastPos());
1370 /* path was found or at least suggested
1371 * walk through the path back to the origin */
1373 while (pNode
->m_parent
!= NULL
) {
1375 pNode
= pNode
->m_parent
;
1377 this->FindSafePositionOnNode(pPrev
);
1379 /* return trackdir from the best origin node (one of start nodes) */
1380 Node
& best_next_node
= *pPrev
;
1381 next_trackdir
= best_next_node
.GetPos().td
;
1383 if (reserve_track
&& path_found
) {
1384 bool okay
= this->TryReservePath(pNode
->GetLastPos().tile
, target
!= NULL
? &target
->pos
: NULL
);
1385 if (target
!= NULL
) target
->okay
= okay
;
1389 /* Treat the path as found if stopped on the first two way signal(s). */
1390 if (target
!= NULL
) target
->found
= path_found
| Yapf().m_stopped_on_first_two_way_signal
;
1391 return next_trackdir
;
1394 static bool stCheckReverseTrain(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int reverse_penalty
)
1397 bool result1
= pf1
.CheckReverseTrain(v
, pos1
, pos2
, reverse_penalty
);
1399 #if DEBUG_YAPF_CACHE
1401 pf2
.DisableCache(true);
1402 bool result2
= pf2
.CheckReverseTrain(v
, pos1
, pos2
, reverse_penalty
);
1403 if (result1
!= result2
) {
1404 DEBUG(yapf
, 0, "CACHE ERROR: CheckReverseTrain() = [%s, %s]", result1
? "T" : "F", result2
? "T" : "F");
1405 DumpState(pf1
, pf2
);
1412 inline bool CheckReverseTrain(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int reverse_penalty
)
1414 /* create pathfinder instance
1415 * set origin and destination nodes */
1416 Yapf().SetOrigin(pos1
, pos2
, reverse_penalty
, false);
1417 Yapf().SetDestination(v
);
1419 /* find the best path */
1420 bool bFound
= Yapf().FindPath(v
);
1422 if (!bFound
) return false;
1425 * walk through the path back to the origin */
1426 Node
*pNode
= Yapf().GetBestNode();
1427 while (pNode
->m_parent
!= NULL
) {
1428 pNode
= pNode
->m_parent
;
1431 /* check if it was reversed origin */
1432 Node
& best_org_node
= *pNode
;
1433 bool reversed
= (best_org_node
.m_cost
!= 0);
1438 template <class Tpf_
, class Ttrack_follower
>
1439 struct CYapfRail_TypesT
1441 typedef CYapfRail_TypesT
<Tpf_
, Ttrack_follower
> Types
;
1444 typedef Ttrack_follower TrackFollower
;
1445 typedef AstarRailTrackDir Astar
;
1446 typedef Train VehicleType
;
1450 : CYapfBaseT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1451 , CYapfRailT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1452 , CYapfSegmentCostCacheGlobalT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1453 , CYapfOriginTileTwoWayT
<CYapfRail1
>
1454 , CYapfDestinationTileOrStationRailT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1455 , CYapfFollowRailT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1460 : CYapfBaseT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1461 , CYapfRailT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1462 , CYapfSegmentCostCacheGlobalT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1463 , CYapfOriginTileTwoWayT
<CYapfRail2
>
1464 , CYapfDestinationTileOrStationRailT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1465 , CYapfFollowRailT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1469 struct CYapfAnyDepotRail1
1470 : CYapfBaseT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1471 , CYapfRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1472 , CYapfSegmentCostCacheGlobalT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1473 , CYapfOriginTileTwoWayT
<CYapfAnyDepotRail1
>
1474 , CYapfDestinationAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1475 , CYapfFollowAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1479 struct CYapfAnyDepotRail2
1480 : CYapfBaseT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1481 , CYapfRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1482 , CYapfSegmentCostCacheGlobalT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1483 , CYapfOriginTileTwoWayT
<CYapfAnyDepotRail2
>
1484 , CYapfDestinationAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1485 , CYapfFollowAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1489 struct CYapfAnySafeTileRail1
1490 : CYapfBaseT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1491 , CYapfRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1492 , CYapfSegmentCostCacheGlobalT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1493 , CYapfOriginTileTwoWayT
<CYapfAnySafeTileRail1
>
1494 , CYapfDestinationAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1495 , CYapfFollowAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1499 struct CYapfAnySafeTileRail2
1500 : CYapfBaseT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1501 , CYapfRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1502 , CYapfSegmentCostCacheGlobalT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1503 , CYapfOriginTileTwoWayT
<CYapfAnySafeTileRail2
>
1504 , CYapfDestinationAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1505 , CYapfFollowAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1510 Trackdir
YapfTrainChooseTrack(const Train
*v
, const PathPos
&origin
, bool reserve_track
, PFResult
*target
)
1512 /* default is YAPF type 2 */
1513 typedef Trackdir (*PfnChooseRailTrack
)(const Train
*, const PathPos
&, bool, PFResult
*);
1514 PfnChooseRailTrack pfnChooseRailTrack
= &CYapfRail1::stChooseRailTrack
;
1516 /* check if non-default YAPF type needed */
1517 if (_settings_game
.pf
.forbid_90_deg
) {
1518 pfnChooseRailTrack
= &CYapfRail2::stChooseRailTrack
; // Trackdir, forbid 90-deg
1521 return pfnChooseRailTrack(v
, origin
, reserve_track
, target
);
1524 bool YapfTrainCheckReverse(const Train
*v
)
1526 const Train
*last_veh
= v
->Last();
1528 /* tiles where front and back are */
1529 PathPos pos
= v
->GetPos();
1530 PathPos rev
= last_veh
->GetReversePos();
1532 int reverse_penalty
= 0;
1534 if (pos
.in_wormhole()) {
1535 /* front in tunnel / on bridge */
1536 assert(TrackdirToExitdir(pos
.td
) == ReverseDiagDir(GetTunnelBridgeDirection(pos
.wormhole
)));
1538 /* Current position of the train in the wormhole */
1539 TileIndex cur_tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
1541 /* Add distance to drive in the wormhole as penalty for the forward path, i.e. bonus for the reverse path
1542 * Note: Negative penalties are ok for the start tile. */
1543 reverse_penalty
-= DistanceManhattan(cur_tile
, pos
.tile
) * YAPF_TILE_LENGTH
;
1546 if (rev
.in_wormhole()) {
1547 /* back in tunnel / on bridge */
1548 assert(TrackdirToExitdir(rev
.td
) == ReverseDiagDir(GetTunnelBridgeDirection(rev
.wormhole
)));
1550 /* Current position of the last wagon in the wormhole */
1551 TileIndex cur_tile
= TileVirtXY(last_veh
->x_pos
, last_veh
->y_pos
);
1553 /* Add distance to drive in the wormhole as penalty for the revere path. */
1554 reverse_penalty
+= DistanceManhattan(cur_tile
, rev
.tile
) * YAPF_TILE_LENGTH
;
1557 typedef bool (*PfnCheckReverseTrain
)(const Train
*, const PathPos
&, const PathPos
&, int);
1558 PfnCheckReverseTrain pfnCheckReverseTrain
= CYapfRail1::stCheckReverseTrain
;
1560 /* check if non-default YAPF type needed */
1561 if (_settings_game
.pf
.forbid_90_deg
) {
1562 pfnCheckReverseTrain
= &CYapfRail2::stCheckReverseTrain
; // Trackdir, forbid 90-deg
1565 /* slightly hackish: If the pathfinders finds a path, the cost of the first node is tested to distinguish between forward- and reverse-path. */
1566 if (reverse_penalty
== 0) reverse_penalty
= 1;
1568 bool reverse
= pfnCheckReverseTrain(v
, pos
, rev
, reverse_penalty
);
1573 bool YapfTrainFindNearestDepot(const Train
*v
, uint max_penalty
, FindDepotData
*res
)
1576 FollowTrainReservation(v
, &origin
);
1577 PathPos rev
= v
->Last()->GetReversePos();
1579 typedef bool (*PfnFindNearestDepotTwoWay
)(const Train
*, const PathPos
&, const PathPos
&, int, int, TileIndex
*, bool*);
1580 PfnFindNearestDepotTwoWay pfnFindNearestDepotTwoWay
= &CYapfAnyDepotRail1::stFindNearestDepotTwoWay
;
1582 /* check if non-default YAPF type needed */
1583 if (_settings_game
.pf
.forbid_90_deg
) {
1584 pfnFindNearestDepotTwoWay
= &CYapfAnyDepotRail2::stFindNearestDepotTwoWay
; // Trackdir, forbid 90-deg
1587 return pfnFindNearestDepotTwoWay(v
, origin
, rev
, max_penalty
, YAPF_INFINITE_PENALTY
, &res
->tile
, &res
->reverse
);
1590 bool YapfTrainFindNearestSafeTile(const Train
*v
, const PathPos
&pos
, bool override_railtype
)
1592 typedef bool (*PfnFindNearestSafeTile
)(const Train
*, const PathPos
&, bool);
1593 PfnFindNearestSafeTile pfnFindNearestSafeTile
= CYapfAnySafeTileRail1::stFindNearestSafeTile
;
1595 /* check if non-default YAPF type needed */
1596 if (_settings_game
.pf
.forbid_90_deg
) {
1597 pfnFindNearestSafeTile
= &CYapfAnySafeTileRail2::stFindNearestSafeTile
;
1600 return pfnFindNearestSafeTile(v
, pos
, override_railtype
);
1603 /** if any track changes, this counter is incremented - that will invalidate segment cost cache */
1604 int CSegmentCostCacheBase::s_rail_change_counter
= 0;
1606 void YapfNotifyTrackLayoutChange(TileIndex tile
, Track track
)
1608 CSegmentCostCacheBase::NotifyTrackLayoutChange(tile
, track
);