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_node_rail.hpp"
16 #include "../../viewport_func.h"
17 #include "../../newgrf_station.h"
18 #include "../../station_func.h"
19 #include "../../misc/blob.hpp"
20 #include "../../misc/array.hpp"
21 #include "../../misc/hashtable.hpp"
22 #include "../../map/slope.h"
23 #include "../../pbs.h"
24 #include "../../waypoint_base.h"
25 #include "../../date_func.h"
27 #define DEBUG_YAPF_CACHE 0
30 template <typename Tpf
> void DumpState(Tpf
&pf1
, Tpf
&pf2
)
32 DumpTarget dmp1
, dmp2
;
35 FILE *f1
= fopen("yapf1.txt", "wt");
36 FILE *f2
= fopen("yapf2.txt", "wt");
37 fwrite(dmp1
.m_out
.Data(), 1, dmp1
.m_out
.Size(), f1
);
38 fwrite(dmp2
.m_out
.Data(), 1, dmp2
.m_out
.Size(), f2
);
44 int _total_pf_time_us
= 0;
48 * Base class for segment cost cache providers. Contains global counter
49 * of track layout changes and static notification function called whenever
50 * the track layout changes. It is implemented as base class because it needs
51 * to be shared between all rail YAPF types (one shared counter, one notification
54 struct CSegmentCostCacheBase
56 static int s_rail_change_counter
;
58 static void NotifyTrackLayoutChange(TileIndex tile
, Track track
)
60 s_rail_change_counter
++;
64 /** if any track changes, this counter is incremented - that will invalidate segment cost cache */
65 int CSegmentCostCacheBase::s_rail_change_counter
= 0;
67 void YapfNotifyTrackLayoutChange(TileIndex tile
, Track track
)
69 CSegmentCostCacheBase::NotifyTrackLayoutChange(tile
, track
);
74 * CSegmentCostCacheT - template class providing hash-map and storage (heap)
75 * of Tsegment structures. Each rail node contains pointer to the segment
76 * that contains cached (or non-cached) segment cost information. Nodes can
77 * differ by key type, but they use the same segment type. Segment key should
78 * be always the same (TileIndex + DiagDirection) that represent the beginning
79 * of the segment (origin tile and exit-dir from this tile).
80 * Different CYapfCachedCostT types can share the same type of CSegmentCostCacheT.
81 * Look at CYapfRailSegment (yapf_node_rail.hpp) for the segment example
83 template <class Tsegment
>
84 struct CSegmentCostCacheT
85 : public CSegmentCostCacheBase
87 static const int C_HASH_BITS
= 14;
89 typedef CHashTableT
<Tsegment
, C_HASH_BITS
> HashTable
;
90 typedef SmallArray
<Tsegment
> Heap
;
91 typedef typename
Tsegment::Key Key
; ///< key to hash table
96 inline CSegmentCostCacheT() {}
98 /** flush (clear) the cache */
105 inline Tsegment
& Get(Key
& key
, bool *found
)
107 Tsegment
*item
= m_map
.Find(key
);
110 item
= new (m_heap
.Append()) Tsegment(key
);
120 template <class Types
>
121 class CYapfRailT
: public Types::Astar
124 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
125 typedef typename
Types::TrackFollower TrackFollower
;
126 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
127 typedef typename
Node::Key Key
; ///< key to hash tables
128 typedef typename
Node::CachedData CachedData
;
129 typedef typename
CachedData::Key CacheKey
;
130 typedef CSegmentCostCacheT
<CachedData
> Cache
;
131 typedef SmallArray
<CachedData
> LocalCache
;
134 const YAPFSettings
*m_settings
; ///< current settings (_settings_game.yapf)
135 const Train
*m_veh
; ///< vehicle that we are trying to drive
137 PathPos m_org
; ///< first origin position
138 PathPos m_rev
; ///< second (reversed) origin position
139 int m_reverse_penalty
; ///< penalty to be added for using the reversed origin
140 bool m_treat_first_red_two_way_signal_as_eol
; ///< in some cases (leaving station) we need to handle first two-way signal differently
143 * @note maximum cost doesn't work with caching enabled
144 * @todo fix maximum cost failing with caching (e.g. FS#2900)
147 CBlobT
<int> m_sig_look_ahead_costs
;
148 bool m_disable_cache
;
149 Cache
& m_global_cache
;
150 LocalCache m_local_cache
;
152 int m_stats_cost_calcs
; ///< stats - how many node's costs were calculated
153 int m_stats_cache_hits
; ///< stats - how many node's costs were reused from cache
155 CPerformanceTimer m_perf_cost
; ///< stats - total CPU time of this run
156 CPerformanceTimer m_perf_slope_cost
; ///< stats - slope calculation CPU time
157 CPerformanceTimer m_perf_ts_cost
; ///< stats - GetTrackStatus() CPU time
158 CPerformanceTimer m_perf_other_cost
; ///< stats - other CPU time
161 bool m_stopped_on_first_two_way_signal
;
164 static const int s_max_segment_cost
= 10000;
167 : m_settings(&_settings_game
.pf
.yapf
)
170 , m_disable_cache(false)
171 , m_global_cache(stGetGlobalCache())
172 , m_stats_cost_calcs(0)
173 , m_stats_cache_hits(0)
174 , m_stopped_on_first_two_way_signal(false)
176 /* pre-compute look-ahead penalties into array */
177 int p0
= Yapf().PfGetSettings().rail_look_ahead_signal_p0
;
178 int p1
= Yapf().PfGetSettings().rail_look_ahead_signal_p1
;
179 int p2
= Yapf().PfGetSettings().rail_look_ahead_signal_p2
;
180 int *pen
= m_sig_look_ahead_costs
.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals
);
181 for (uint i
= 0; i
< Yapf().PfGetSettings().rail_look_ahead_max_signals
; i
++) {
182 pen
[i
] = p0
+ i
* (p1
+ i
* p2
);
186 /** to access inherited path finder */
189 return *static_cast<Tpf
*>(this);
192 inline static Cache
& stGetGlobalCache()
194 static int last_rail_change_counter
= 0;
195 static Date last_date
= 0;
198 /* some statistics */
199 if (last_date
!= _date
) {
201 DEBUG(yapf
, 2, "Pf time today: %5d ms", _total_pf_time_us
/ 1000);
202 _total_pf_time_us
= 0;
205 /* delete the cache sometimes... */
206 if (last_rail_change_counter
!= Cache::s_rail_change_counter
) {
207 last_rail_change_counter
= Cache::s_rail_change_counter
;
215 /** return current settings (can be custom - company based - but later) */
216 inline const YAPFSettings
& PfGetSettings() const
221 const Train
* GetVehicle() const
227 void SetOrigin(const PathPos
&pos
, const PathPos
&rev
= PathPos(), int reverse_penalty
= 0, bool treat_first_red_two_way_signal_as_eol
= true)
231 m_reverse_penalty
= reverse_penalty
;
232 m_treat_first_red_two_way_signal_as_eol
= treat_first_red_two_way_signal_as_eol
;
235 /** Create and add a new node */
236 inline void AddStartupNode (const PathPos
&pos
, bool is_choice
, int cost
= 0)
238 Node
*node
= Types::Astar::CreateNewNode (NULL
, pos
, is_choice
);
240 PfNodeCacheFetch(*node
);
241 Types::Astar::InsertInitialNode(node
);
244 /** Called when YAPF needs to place origin nodes into open list */
245 void PfSetStartupNodes()
247 if (m_org
.tile
!= INVALID_TILE
&& m_org
.td
!= INVALID_TRACKDIR
) {
248 AddStartupNode(m_org
, false);
250 if (m_rev
.tile
!= INVALID_TILE
&& m_rev
.td
!= INVALID_TRACKDIR
) {
251 AddStartupNode(m_rev
, false, m_reverse_penalty
);
255 /** return true if first two-way signal should be treated as dead end */
256 inline bool TreatFirstRedTwoWaySignalAsEOL()
258 return Yapf().PfGetSettings().rail_firstred_twoway_eol
&& m_treat_first_red_two_way_signal_as_eol
;
268 EndSegmentReasonBits end_reason
;
271 inline int SlopeCost(const PathPos
&pos
)
273 CPerfStart
perf_cost(Yapf().m_perf_slope_cost
);
275 if (pos
.in_wormhole() || !IsDiagonalTrackdir(pos
.td
)) return 0;
277 /* Only rail tracks and bridgeheads can have sloped rail. */
278 if (!IsRailwayTile(pos
.tile
)) return 0;
281 if (IsTileSubtype(pos
.tile
, TT_BRIDGE
)) {
282 /* it is bridge ramp, check if we are entering the bridge */
283 DiagDirection dir
= GetTunnelBridgeDirection(pos
.tile
);
284 if (dir
!= TrackdirToExitdir(pos
.td
)) return 0; // no, we are leaving it, no penalty
285 /* we are entering the bridge */
286 Slope tile_slope
= GetTileSlope(pos
.tile
);
287 Axis axis
= DiagDirToAxis(dir
);
288 uphill
= !HasBridgeFlatRamp(tile_slope
, axis
);
290 /* not bridge ramp */
291 Slope tile_slope
= GetTileSlope(pos
.tile
);
292 uphill
= IsUphillTrackdir(tile_slope
, pos
.td
); // slopes uphill => apply penalty
295 return uphill
? Yapf().PfGetSettings().rail_slope_penalty
: 0;
298 inline int TransitionCost (const PathPos
&pos1
, const PathPos
&pos2
)
300 assert(IsValidTrackdir(pos1
.td
));
301 assert(IsValidTrackdir(pos2
.td
));
303 if (pos1
.in_wormhole() || !IsRailwayTile(pos1
.tile
)) {
304 assert(IsDiagonalTrackdir(pos1
.td
));
305 if (pos2
.in_wormhole() || !IsRailwayTile(pos2
.tile
)) {
306 /* compare only the tracks, as depots cause reversing */
307 assert(TrackdirToTrack(pos1
.td
) == TrackdirToTrack(pos2
.td
));
310 return (pos1
.td
== pos2
.td
) ? 0 : Yapf().PfGetSettings().rail_curve45_penalty
;
313 if (pos2
.in_wormhole() || !IsRailwayTile(pos2
.tile
)) {
314 assert(IsDiagonalTrackdir(pos2
.td
));
315 return (pos1
.td
== pos2
.td
) ? 0 : Yapf().PfGetSettings().rail_curve45_penalty
;
319 /* both tiles are railway tiles */
322 if (TrackFollower::Allow90degTurns()
323 && ((TrackdirToTrackdirBits(pos2
.td
) & (TrackdirBits
)TrackdirCrossesTrackdirs(pos1
.td
)) != 0)) {
324 /* 90-deg curve penalty */
325 cost
+= Yapf().PfGetSettings().rail_curve90_penalty
;
326 } else if (pos2
.td
!= NextTrackdir(pos1
.td
)) {
327 /* 45-deg curve penalty */
328 cost
+= Yapf().PfGetSettings().rail_curve45_penalty
;
331 DiagDirection exitdir
= TrackdirToExitdir(pos1
.td
);
332 bool t1
= KillFirstBit(GetTrackBits(pos1
.tile
) & DiagdirReachesTracks(ReverseDiagDir(exitdir
))) != TRACK_BIT_NONE
;
333 bool t2
= KillFirstBit(GetTrackBits(pos2
.tile
) & DiagdirReachesTracks(exitdir
)) != TRACK_BIT_NONE
;
334 if (t1
&& t2
) cost
+= Yapf().PfGetSettings().rail_doubleslip_penalty
;
339 /** Return one tile cost (base cost + level crossing penalty). */
340 inline int OneTileCost(const PathPos
&pos
)
344 if (IsDiagonalTrackdir(pos
.td
)) {
345 cost
+= YAPF_TILE_LENGTH
;
346 if (IsLevelCrossingTile(pos
.tile
)) {
347 /* Increase the cost for level crossings */
348 cost
+= Yapf().PfGetSettings().rail_crossing_penalty
;
351 /* non-diagonal trackdir */
352 cost
= YAPF_TILE_CORNER_LENGTH
;
357 /** Check for a reserved station platform. */
358 inline bool IsAnyStationTileReserved(const PathPos
&pos
, int skipped
)
360 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos
.td
)));
361 for (TileIndex tile
= pos
.tile
; skipped
>= 0; skipped
--, tile
+= diff
) {
362 if (HasStationReservation(tile
)) return true;
367 /** The cost for reserved tiles, including skipped ones. */
368 inline int ReservationCost(Node
& n
, const PathPos
&pos
, int skipped
)
370 if (n
.m_num_signals_passed
>= m_sig_look_ahead_costs
.Size() / 2) return 0;
371 if (!IsPbsSignal(n
.m_last_signal_type
)) return 0;
373 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
) && IsAnyStationTileReserved(pos
, skipped
)) {
374 return Yapf().PfGetSettings().rail_pbs_station_penalty
* (skipped
+ 1);
375 } else if (TrackOverlapsTracks(GetReservedTrackbits(pos
.tile
), TrackdirToTrack(pos
.td
))) {
376 int cost
= Yapf().PfGetSettings().rail_pbs_cross_penalty
;
377 if (!IsDiagonalTrackdir(pos
.td
)) cost
= (cost
* YAPF_TILE_CORNER_LENGTH
) / YAPF_TILE_LENGTH
;
378 return cost
* (skipped
+ 1);
383 int SignalCost(Node
& n
, const PathPos
&pos
, NodeData
*data
)
386 /* if there is one-way signal in the opposite direction, then it is not our way */
387 CPerfStart
perf_cost(Yapf().m_perf_other_cost
);
389 if (HasSignalAlongPos(pos
)) {
390 SignalState sig_state
= GetSignalStateByPos(pos
);
391 SignalType sig_type
= GetSignalType(pos
);
393 n
.m_last_signal_type
= sig_type
;
395 /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */
396 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;
397 if (sig_state
!= SIGNAL_STATE_RED
) {
399 n
.flags_u
.flags_s
.m_last_signal_was_red
= false;
400 /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */
401 if (look_ahead_cost
< 0) {
402 /* add its negation to the cost */
403 cost
-= look_ahead_cost
;
406 /* we have a red signal in our direction
407 * was it first signal which is two-way? */
408 if (!IsPbsSignal(sig_type
) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n
.flags_u
.flags_s
.m_choice_seen
&& HasSignalAgainstPos(pos
) && n
.m_num_signals_passed
== 0) {
409 /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
410 Yapf().PruneIntermediateNodeBranch();
411 data
->end_reason
|= ESRB_DEAD_END
;
412 Yapf().m_stopped_on_first_two_way_signal
= true;
415 n
.m_last_red_signal_type
= sig_type
;
416 n
.flags_u
.flags_s
.m_last_signal_was_red
= true;
418 /* look-ahead signal penalty */
419 if (!IsPbsSignal(sig_type
) && look_ahead_cost
> 0) {
420 /* add the look ahead penalty only if it is positive */
421 cost
+= look_ahead_cost
;
424 /* special signal penalties */
425 if (n
.m_num_signals_passed
== 0) {
428 case SIGTYPE_EXIT
: cost
+= Yapf().PfGetSettings().rail_firstred_exit_penalty
; break; // first signal is red pre-signal-exit
430 case SIGTYPE_ENTRY
: cost
+= Yapf().PfGetSettings().rail_firstred_penalty
; break;
436 n
.m_num_signals_passed
++;
437 data
->last_signal
= pos
;
438 } else if (HasSignalAgainstPos(pos
)) {
439 if (GetSignalType(pos
) != SIGTYPE_PBS
) {
440 /* one-way signal in opposite direction */
441 data
->end_reason
|= ESRB_DEAD_END
;
443 cost
+= n
.m_num_signals_passed
< Yapf().PfGetSettings().rail_look_ahead_max_signals
? Yapf().PfGetSettings().rail_pbs_signal_back_penalty
: 0;
450 inline int PlatformLengthPenalty(int platform_length
)
453 const Train
*v
= Yapf().GetVehicle();
455 assert(v
->type
== VEH_TRAIN
);
456 assert(v
->gcache
.cached_total_length
!= 0);
457 int missing_platform_length
= CeilDiv(v
->gcache
.cached_total_length
, TILE_SIZE
) - platform_length
;
458 if (missing_platform_length
< 0) {
459 /* apply penalty for longer platform than needed */
460 cost
+= Yapf().PfGetSettings().rail_longer_platform_penalty
+ Yapf().PfGetSettings().rail_longer_platform_per_tile_penalty
* -missing_platform_length
;
461 } else if (missing_platform_length
> 0) {
462 /* apply penalty for shorter platform than needed */
463 cost
+= Yapf().PfGetSettings().rail_shorter_platform_penalty
+ Yapf().PfGetSettings().rail_shorter_platform_per_tile_penalty
* missing_platform_length
;
469 inline void SetMaxCost(int max_cost
)
471 m_max_cost
= max_cost
;
474 inline void PfCalcSegment (Node
&n
, const TrackFollower
*tf
, NodeData
*segment
)
476 const Train
*v
= Yapf().GetVehicle();
477 TrackFollower
tf_local(v
, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost
);
479 TileIndex prev
= n
.m_parent
->GetLastPos().tile
;
481 /* start at n and walk to the end of segment */
482 segment
->segment_cost
= 0;
483 segment
->extra_cost
= 0;
484 segment
->pos
= n
.GetPos();
485 segment
->last_signal
= PathPos();
486 segment
->end_reason
= ESRB_NONE
;
488 RailType rail_type
= !segment
->pos
.in_wormhole() ? GetTileRailType(segment
->pos
.tile
, TrackdirToTrack(segment
->pos
.td
)) :
489 IsRailwayTile(segment
->pos
.wormhole
) ? GetBridgeRailType(segment
->pos
.wormhole
) : GetRailType(segment
->pos
.wormhole
);
492 /* All other tile costs will be calculated here. */
493 segment
->segment_cost
+= OneTileCost(segment
->pos
);
495 /* If we skipped some tunnel/bridge/station tiles, add their base cost */
496 segment
->segment_cost
+= YAPF_TILE_LENGTH
* tf
->m_tiles_skipped
;
499 segment
->segment_cost
+= SlopeCost(segment
->pos
);
501 /* Signal cost (routine can modify segment data). */
502 segment
->segment_cost
+= SignalCost(n
, segment
->pos
, segment
);
504 /* Reserved tiles. */
505 segment
->segment_cost
+= ReservationCost(n
, segment
->pos
, tf
->m_tiles_skipped
);
507 /* Tests for 'potential target' reasons to close the segment. */
508 if (segment
->pos
.tile
== prev
) {
509 /* Penalty for reversing in a depot. */
510 assert(!segment
->pos
.in_wormhole());
511 assert(IsRailDepotTile(segment
->pos
.tile
));
512 assert(segment
->pos
.td
== DiagDirToDiagTrackdir(GetGroundDepotDirection(segment
->pos
.tile
)));
513 segment
->segment_cost
+= Yapf().PfGetSettings().rail_depot_reverse_penalty
;
514 /* We will end in this pass (depot is possible target) */
515 segment
->end_reason
|= ESRB_DEPOT
;
517 } else if (!segment
->pos
.in_wormhole() && IsRailWaypointTile(segment
->pos
.tile
)) {
518 if (v
->current_order
.IsType(OT_GOTO_WAYPOINT
) &&
519 GetStationIndex(segment
->pos
.tile
) == v
->current_order
.GetDestination() &&
520 !Waypoint::Get(v
->current_order
.GetDestination())->IsSingleTile()) {
521 /* This waypoint is our destination; maybe this isn't an unreserved
522 * one, so check that and if so see that as the last signal being
523 * red. This way waypoints near stations should work better. */
524 CFollowTrackRail
ft(v
);
525 ft
.SetPos(segment
->pos
);
529 if (!ft
.FollowNext()) {
531 add_extra_cost
= !IsWaitingPositionFree(v
, ft
.m_old
, _settings_game
.pf
.forbid_90_deg
);
535 assert(ft
.m_old
.tile
!= ft
.m_new
.tile
);
536 if (!ft
.m_new
.is_single()) {
537 /* We encountered a junction; it's going to be too complex to
538 * handle this perfectly, so just bail out. There is no simple
539 * free path, so try the other possibilities. */
540 add_extra_cost
= true;
544 /* If this is a safe waiting position we're done searching for it */
545 PBSPositionState state
= CheckWaitingPosition (v
, ft
.m_new
, _settings_game
.pf
.forbid_90_deg
);
546 if (state
!= PBS_UNSAFE
) {
547 add_extra_cost
= state
== PBS_BUSY
;
552 /* In the case this platform is (possibly) occupied we add penalty so the
553 * other platforms of this waypoint are evaluated as well, i.e. we assume
554 * that there is a red signal in the waypoint when it's occupied. */
555 if (add_extra_cost
) segment
->extra_cost
+= Yapf().PfGetSettings().rail_lastred_penalty
;
557 /* Waypoint is also a good reason to finish. */
558 segment
->end_reason
|= ESRB_WAYPOINT
;
560 } else if (tf
->m_flag
== tf
->TF_STATION
) {
561 /* Station penalties. */
562 uint platform_length
= tf
->m_tiles_skipped
+ 1;
563 /* We don't know yet if the station is our target or not. Act like
564 * if it is pass-through station (not our destination). */
565 segment
->segment_cost
+= Yapf().PfGetSettings().rail_station_penalty
* platform_length
;
566 /* We will end in this pass (station is possible target) */
567 segment
->end_reason
|= ESRB_STATION
;
569 } else if (TrackFollower::DoTrackMasking()) {
570 /* Searching for a safe tile? */
571 if (HasSignalAlongPos(segment
->pos
) && !IsPbsSignal(GetSignalType(segment
->pos
))) {
572 segment
->end_reason
|= ESRB_SAFE_TILE
;
576 /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
577 * it would cause desync in MP. */
578 if (n
.m_num_signals_passed
< m_sig_look_ahead_costs
.Size())
581 int max_speed
= tf
->GetSpeedLimit(&min_speed
);
582 int max_veh_speed
= v
->GetDisplayMaxSpeed();
583 if (max_speed
< max_veh_speed
) {
584 segment
->extra_cost
+= YAPF_TILE_LENGTH
* (max_veh_speed
- max_speed
) * (1 + tf
->m_tiles_skipped
) / max_veh_speed
;
586 if (min_speed
> max_veh_speed
) {
587 segment
->extra_cost
+= YAPF_TILE_LENGTH
* (min_speed
- max_veh_speed
);
591 /* Finish if we already exceeded the maximum path cost (i.e. when
592 * searching for the nearest depot). */
593 if (m_max_cost
> 0 && (segment
->parent_cost
+ segment
->entry_cost
+ segment
->segment_cost
) > m_max_cost
) {
594 segment
->end_reason
|= ESRB_PATH_TOO_LONG
;
597 /* Move to the next tile/trackdir. */
599 assert(tf_local
.m_veh_owner
== v
->owner
);
600 assert(tf_local
.m_railtypes
== Yapf().GetCompatibleRailTypes());
601 assert(tf_local
.m_pPerf
== &Yapf().m_perf_ts_cost
);
603 if (!tf_local
.Follow(segment
->pos
)) {
604 assert(tf_local
.m_err
!= TrackFollower::EC_NONE
);
605 /* Can't move to the next tile (EOL?). */
606 if (tf_local
.m_err
== TrackFollower::EC_RAIL_TYPE
) {
607 segment
->end_reason
|= ESRB_RAIL_TYPE
;
609 segment
->end_reason
|= ESRB_DEAD_END
;
612 if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingPos(segment
->pos
)) {
613 segment
->end_reason
|= ESRB_SAFE_TILE
;
618 /* Check if the next tile is not a choice. */
619 if (!tf_local
.m_new
.is_single()) {
620 /* More than one segment will follow. Close this one. */
621 segment
->end_reason
|= ESRB_CHOICE_FOLLOWS
;
625 /* Gather the next tile/trackdir. */
627 if (TrackFollower::DoTrackMasking()) {
628 if (HasSignalAlongPos(tf_local
.m_new
) && IsPbsSignal(GetSignalType(tf_local
.m_new
))) {
629 /* Possible safe tile. */
630 segment
->end_reason
|= ESRB_SAFE_TILE
;
631 } else if (HasSignalAgainstPos(tf_local
.m_new
) && GetSignalType(tf_local
.m_new
) == SIGTYPE_PBS_ONEWAY
) {
632 /* Possible safe tile, but not so good as it's the back of a signal... */
633 segment
->end_reason
|= ESRB_SAFE_TILE
| ESRB_DEAD_END
;
634 segment
->extra_cost
+= Yapf().PfGetSettings().rail_lastred_exit_penalty
;
638 /* Check the next tile for the rail type. */
639 if (tf_local
.m_new
.in_wormhole()) {
640 RailType next_rail_type
= IsRailwayTile(tf_local
.m_new
.wormhole
) ? GetBridgeRailType(tf_local
.m_new
.wormhole
) : GetRailType(tf_local
.m_new
.wormhole
);
641 assert(next_rail_type
== rail_type
);
642 } else if (GetTileRailType(tf_local
.m_new
.tile
, TrackdirToTrack(tf_local
.m_new
.td
)) != rail_type
) {
643 /* Segment must consist from the same rail_type tiles. */
644 segment
->end_reason
|= ESRB_RAIL_TYPE
;
648 /* Avoid infinite looping. */
649 if (tf_local
.m_new
== n
.GetPos()) {
650 segment
->end_reason
|= ESRB_INFINITE_LOOP
;
654 if (segment
->segment_cost
> s_max_segment_cost
) {
655 /* Potentially in the infinite loop (or only very long segment?). We should
656 * not force it to finish prematurely unless we are on a regular tile. */
657 if (!tf
->m_new
.in_wormhole() && IsNormalRailTile(tf
->m_new
.tile
)) {
658 segment
->end_reason
|= ESRB_SEGMENT_TOO_LONG
;
663 /* Any other reason bit set? */
664 if (segment
->end_reason
!= ESRB_NONE
) {
668 /* Transition cost (cost of the move from previous tile) */
669 segment
->segment_cost
+= TransitionCost (segment
->pos
, tf_local
.m_new
);
671 /* For the next loop set new prev and cur tile info. */
672 prev
= segment
->pos
.tile
;
673 segment
->pos
= tf_local
.m_new
;
678 * Called by YAPF to calculate the cost from the origin to the given node.
679 * Calculates only the cost of given node, adds it to the parent node cost
680 * and stores the result into Node::m_cost member
682 inline bool PfCalcCost(Node
&n
, const TrackFollower
*tf
)
684 assert(!n
.flags_u
.flags_s
.m_targed_seen
);
685 assert(tf
->m_new
.tile
== n
.GetPos().tile
);
686 assert(tf
->m_new
.wormhole
== n
.GetPos().wormhole
);
687 assert((TrackdirToTrackdirBits(n
.GetPos().td
) & tf
->m_new
.trackdirs
) != TRACKDIR_BIT_NONE
);
688 assert(n
.m_parent
!= NULL
);
690 CPerfStart
perf_cost(Yapf().m_perf_cost
);
692 /* Do we already have a cached segment? */
693 CachedData
&segment
= *n
.m_segment
;
694 bool is_cached_segment
= (segment
.m_cost
>= 0);
696 /* Each node cost contains 2 or 3 main components:
697 * 1. Transition cost - cost of the move from previous node (tile):
698 * - curve cost (or zero for straight move)
701 * - YAPF_TILE_LENGTH for diagonal tiles
702 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
704 * - tile slope penalty (upward slopes)
705 * - red signal penalty
706 * - level crossing penalty
707 * - speed-limit penalty (bridges)
708 * - station platform penalty
709 * - penalty for reversing in the depot
711 * 3. Extra cost (applies to the last node only)
712 * - last red signal penalty
713 * - penalty for too long or too short platform on the destination station
716 /* Segment: one or more tiles connected by contiguous tracks of the same type.
717 * Each segment cost includes 'Tile cost' for all its tiles (including the first
718 * and last), and the 'Transition cost' between its tiles. The first transition
719 * cost of segment entry (move from the 'parent' node) is not included!
722 NodeData segment_data
;
723 segment_data
.parent_cost
= n
.m_parent
->m_cost
;
724 segment_data
.entry_cost
= TransitionCost (n
.m_parent
->GetLastPos(), n
.GetPos());
726 /* It is the right time now to look if we can reuse the cached segment cost. */
727 if (is_cached_segment
) {
728 segment_data
.segment_cost
= segment
.m_cost
;
729 segment_data
.extra_cost
= 0;
730 segment_data
.pos
= n
.GetLastPos();
731 segment_data
.last_signal
= segment
.m_last_signal
;
732 segment_data
.end_reason
= segment
.m_end_segment_reason
;
733 /* We will need also some information about the last signal (if it was red). */
734 if (segment
.m_last_signal
.tile
!= INVALID_TILE
) {
735 assert(HasSignalAlongPos(segment
.m_last_signal
));
736 SignalState sig_state
= GetSignalStateByPos(segment
.m_last_signal
);
737 bool is_red
= (sig_state
== SIGNAL_STATE_RED
);
738 n
.flags_u
.flags_s
.m_last_signal_was_red
= is_red
;
740 n
.m_last_red_signal_type
= GetSignalType(segment
.m_last_signal
);
743 /* No further calculation needed. */
745 PfCalcSegment (n
, tf
, &segment_data
);
747 /* Write back the segment information so it can be reused the next time. */
748 segment
.m_last
= segment_data
.pos
;
749 segment
.m_cost
= segment_data
.segment_cost
;
750 segment
.m_last_signal
= segment_data
.last_signal
;
751 segment
.m_end_segment_reason
= segment_data
.end_reason
& ESRB_CACHED_MASK
;
755 bool target_seen
= false;
756 if ((segment_data
.end_reason
& ESRB_POSSIBLE_TARGET
) != ESRB_NONE
) {
757 /* Depot, station or waypoint. */
758 if (Yapf().PfDetectDestination(segment_data
.pos
)) {
759 /* Destination found. */
764 /* Do we have an excuse why not to continue pathfinding in this direction? */
765 if (!target_seen
&& (segment_data
.end_reason
& ESRB_ABORT_PF_MASK
) != ESRB_NONE
) {
766 /* Reason to not continue. Stop this PF branch. */
770 /* Special costs for the case we have reached our target. */
772 n
.flags_u
.flags_s
.m_targed_seen
= true;
773 /* Last-red and last-red-exit penalties. */
774 if (n
.flags_u
.flags_s
.m_last_signal_was_red
) {
775 if (n
.m_last_red_signal_type
== SIGTYPE_EXIT
) {
776 /* last signal was red pre-signal-exit */
777 segment_data
.extra_cost
+= Yapf().PfGetSettings().rail_lastred_exit_penalty
;
778 } else if (!IsPbsSignal(n
.m_last_red_signal_type
)) {
779 /* Last signal was red, but not exit or path signal. */
780 segment_data
.extra_cost
+= Yapf().PfGetSettings().rail_lastred_penalty
;
784 /* Station platform-length penalty. */
785 if ((segment_data
.end_reason
& ESRB_STATION
) != ESRB_NONE
) {
786 const BaseStation
*st
= BaseStation::GetByTile(n
.GetLastPos().tile
);
788 uint platform_length
= st
->GetPlatformLength(n
.GetLastPos().tile
, ReverseDiagDir(TrackdirToExitdir(n
.GetLastPos().td
)));
789 /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
790 segment_data
.extra_cost
-= Yapf().PfGetSettings().rail_station_penalty
* platform_length
;
791 /* Add penalty for the inappropriate platform length. */
792 segment_data
.extra_cost
+= PlatformLengthPenalty(platform_length
);
796 /* total node cost */
797 n
.m_cost
= segment_data
.parent_cost
+ segment_data
.entry_cost
+ segment_data
.segment_cost
+ segment_data
.extra_cost
;
803 * In some cases an intermediate node branch should be pruned.
804 * The most prominent case is when a red EOL signal is encountered, but
805 * there was a segment change (e.g. a rail type change) before that. If
806 * the branch would not be pruned, the rail type change location would
807 * remain the best intermediate node, and thus the vehicle would still
808 * go towards the red EOL signal.
810 void PruneIntermediateNodeBranch()
812 while (Types::Astar::best_intermediate
!= NULL
&& (Types::Astar::best_intermediate
->m_segment
->m_end_segment_reason
& ESRB_CHOICE_FOLLOWS
) == 0) {
813 Types::Astar::best_intermediate
= Types::Astar::best_intermediate
->m_parent
;
817 inline bool CanUseGlobalCache(Node
& n
) const
819 return !m_disable_cache
820 && (n
.m_parent
!= NULL
)
821 && (n
.m_parent
->m_num_signals_passed
>= m_sig_look_ahead_costs
.Size());
824 inline void ConnectNodeToCachedData(Node
& n
, CachedData
& ci
)
827 if (n
.m_segment
->m_cost
< 0) {
828 n
.m_segment
->m_last
= n
.GetPos();
832 void DisableCache(bool disable
)
834 m_disable_cache
= disable
;
838 * Called by YAPF to attach cached or local segment cost data to the given node.
839 * @return true if globally cached data were used or false if local data was used
841 inline bool PfNodeCacheFetch(Node
& n
)
843 CacheKey
key(n
.GetKey());
844 if (!CanUseGlobalCache(n
)) {
845 ConnectNodeToCachedData(n
, *new (m_local_cache
.Append()) CachedData(key
));
849 CachedData
& item
= m_global_cache
.Get(key
, &found
);
850 ConnectNodeToCachedData(n
, item
);
854 /** add multiple nodes - direct children of the given node */
855 inline void AddMultipleNodes(Node
*parent
, const TrackFollower
&tf
)
857 bool is_choice
= !tf
.m_new
.is_single();
858 PathPos pos
= tf
.m_new
;
859 for (TrackdirBits rtds
= tf
.m_new
.trackdirs
; rtds
!= TRACKDIR_BIT_NONE
; rtds
= KillFirstBit(rtds
)) {
860 pos
.td
= FindFirstTrackdir(rtds
);
861 Node
& n
= *Types::Astar::CreateNewNode(parent
, pos
, is_choice
);
862 Yapf().AddNewNode(n
, tf
);
867 * AddNewNode() - called by Tderived::PfFollowNode() for each child node.
868 * Nodes are evaluated here and added into open list
870 void AddNewNode(Node
&n
, const TrackFollower
&tf
)
872 /* evaluate the node */
873 bool bCached
= PfNodeCacheFetch(n
);
875 Yapf().m_stats_cost_calcs
++;
877 Yapf().m_stats_cache_hits
++;
880 bool bValid
= PfCalcCost(n
, &tf
) && Yapf().PfCalcEstimate(n
);
882 /* have the cost or estimate callbacks marked this node as invalid? */
885 /* detect the destination */
886 if (Yapf().PfDetectDestination(n
)) {
887 this->FoundTarget(&n
);
889 this->InsertNode(&n
);
893 /** call the node follower */
894 static inline void Follow (Tpf
*pf
, Node
*n
)
896 pf
->PfFollowNode(*n
);
900 * Main pathfinder routine:
901 * - set startup node(s)
902 * - main loop that stops if:
903 * - the destination was found
904 * - or the open list is empty (no route to destination).
905 * - or the maximum amount of loops reached - m_max_search_nodes (default = 10000)
906 * @return true if the path was found
908 inline bool FindPath(const Train
*v
)
912 #ifndef NO_DEBUG_MESSAGES
913 CPerformanceTimer perf
;
915 #endif /* !NO_DEBUG_MESSAGES */
917 Yapf().PfSetStartupNodes();
918 bool bDestFound
= Types::Astar::FindPath (Follow
, PfGetSettings().max_search_nodes
);
920 #ifndef NO_DEBUG_MESSAGES
922 if (_debug_yapf_level
>= 2) {
923 int t
= perf
.Get(1000000);
924 _total_pf_time_us
+= t
;
926 if (_debug_yapf_level
>= 3) {
927 UnitID veh_idx
= (m_veh
!= NULL
) ? m_veh
->unitnumber
: 0;
928 float cache_hit_ratio
= (m_stats_cache_hits
== 0) ? 0.0f
: ((float)m_stats_cache_hits
/ (float)(m_stats_cache_hits
+ m_stats_cost_calcs
) * 100.0f
);
929 int cost
= bDestFound
? Types::Astar::best
->m_cost
: -1;
930 int dist
= bDestFound
? Types::Astar::best
->m_estimate
- Types::Astar::best
->m_cost
: -1;
932 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) -- ",
933 bDestFound
? '-' : '!', veh_idx
, t
, Types::Astar::num_steps
, Types::Astar::OpenCount(), Types::Astar::ClosedCount(),
934 cache_hit_ratio
, cost
, dist
, m_perf_cost
.Get(1000000), m_perf_slope_cost
.Get(1000000),
935 m_perf_ts_cost
.Get(1000000), m_perf_other_cost
.Get(1000000)
939 #endif /* !NO_DEBUG_MESSAGES */
945 class CYapfDestinationRailBase
948 RailTypes m_compatible_railtypes
;
951 void SetDestination(const Train
*v
, bool override_rail_type
= false)
953 m_compatible_railtypes
= v
->compatible_railtypes
;
954 if (override_rail_type
) m_compatible_railtypes
|= GetRailTypeInfo(v
->railtype
)->compatible_railtypes
;
957 bool IsCompatibleRailType(RailType rt
)
959 return HasBit(m_compatible_railtypes
, rt
);
962 RailTypes
GetCompatibleRailTypes() const
964 return m_compatible_railtypes
;
968 template <class Types
>
969 class CYapfDestinationAnyDepotRailT
970 : public CYapfDestinationRailBase
973 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
974 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
975 typedef typename
Node::Key Key
; ///< key to hash tables
977 /** to access inherited path finder */
980 return *static_cast<Tpf
*>(this);
983 /** Called by YAPF to detect if node ends in the desired destination */
984 inline bool PfDetectDestination(Node
& n
)
986 return PfDetectDestination(n
.GetLastPos());
989 /** Called by YAPF to detect if node ends in the desired destination */
990 inline bool PfDetectDestination(const PathPos
&pos
)
992 return !pos
.in_wormhole() && IsRailDepotTile(pos
.tile
);
996 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
997 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
999 inline bool PfCalcEstimate(Node
& n
)
1001 n
.m_estimate
= n
.m_cost
;
1006 template <class Types
>
1007 class CYapfDestinationAnySafeTileRailT
1008 : public CYapfDestinationRailBase
1011 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1012 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1013 typedef typename
Node::Key Key
; ///< key to hash tables
1014 typedef typename
Types::TrackFollower TrackFollower
; ///< TrackFollower. Need to typedef for gcc 2.95
1016 /** to access inherited path finder */
1019 return *static_cast<Tpf
*>(this);
1022 /** Called by YAPF to detect if node ends in the desired destination */
1023 inline bool PfDetectDestination(Node
& n
)
1025 return PfDetectDestination(n
.GetLastPos());
1028 /** Called by YAPF to detect if node ends in the desired destination */
1029 inline bool PfDetectDestination(const PathPos
&pos
)
1031 return IsFreeSafeWaitingPosition(Yapf().GetVehicle(), pos
, !TrackFollower::Allow90degTurns());
1035 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
1036 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate.
1038 inline bool PfCalcEstimate(Node
& n
)
1040 n
.m_estimate
= n
.m_cost
;
1045 template <class Types
>
1046 class CYapfDestinationTileOrStationRailT
1047 : public CYapfDestinationRailBase
1050 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1051 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1052 typedef typename
Node::Key Key
; ///< key to hash tables
1055 TileIndex m_destTile
;
1056 StationID m_dest_station_id
;
1058 /** to access inherited path finder */
1061 return *static_cast<Tpf
*>(this);
1065 void SetDestination(const Train
*v
)
1067 switch (v
->current_order
.GetType()) {
1068 case OT_GOTO_WAYPOINT
:
1069 if (!Waypoint::Get(v
->current_order
.GetDestination())->IsSingleTile()) {
1070 /* In case of 'complex' waypoints we need to do a look
1071 * ahead. This look ahead messes a bit about, which
1072 * means that it 'corrupts' the cache. To prevent this
1073 * we disable caching when we're looking for a complex
1075 Yapf().DisableCache(true);
1078 case OT_GOTO_STATION
:
1079 m_dest_station_id
= v
->current_order
.GetDestination();
1080 m_destTile
= BaseStation::Get(m_dest_station_id
)->GetClosestTile(v
->tile
, v
->current_order
.IsType(OT_GOTO_STATION
) ? STATION_RAIL
: STATION_WAYPOINT
);
1084 m_destTile
= v
->dest_tile
;
1085 m_dest_station_id
= INVALID_STATION
;
1088 CYapfDestinationRailBase::SetDestination(v
);
1091 /** Called by YAPF to detect if node ends in the desired destination */
1092 inline bool PfDetectDestination(Node
& n
)
1094 return PfDetectDestination(n
.GetLastPos());
1097 /** Called by YAPF to detect if node ends in the desired destination */
1098 inline bool PfDetectDestination(const PathPos
&pos
)
1101 if (m_dest_station_id
!= INVALID_STATION
) {
1102 bDest
= !pos
.in_wormhole() && HasStationTileRail(pos
.tile
)
1103 && (GetStationIndex(pos
.tile
) == m_dest_station_id
)
1104 && (GetRailStationTrack(pos
.tile
) == TrackdirToTrack(pos
.td
));
1106 bDest
= !pos
.in_wormhole() && (pos
.tile
== m_destTile
);
1112 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
1113 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
1115 inline bool PfCalcEstimate(Node
& n
)
1117 static const int dg_dir_to_x_offs
[] = {-1, 0, 1, 0};
1118 static const int dg_dir_to_y_offs
[] = {0, 1, 0, -1};
1119 if (PfDetectDestination(n
)) {
1120 n
.m_estimate
= n
.m_cost
;
1124 TileIndex tile
= n
.GetLastPos().tile
;
1125 DiagDirection exitdir
= TrackdirToExitdir(n
.GetLastPos().td
);
1126 int x1
= 2 * TileX(tile
) + dg_dir_to_x_offs
[(int)exitdir
];
1127 int y1
= 2 * TileY(tile
) + dg_dir_to_y_offs
[(int)exitdir
];
1128 int x2
= 2 * TileX(m_destTile
);
1129 int y2
= 2 * TileY(m_destTile
);
1130 int dx
= abs(x1
- x2
);
1131 int dy
= abs(y1
- y2
);
1132 int dmin
= min(dx
, dy
);
1133 int dxy
= abs(dx
- dy
);
1134 int d
= dmin
* YAPF_TILE_CORNER_LENGTH
+ (dxy
- 1) * (YAPF_TILE_LENGTH
/ 2);
1135 n
.m_estimate
= n
.m_cost
+ d
;
1136 assert(n
.m_estimate
>= n
.m_parent
->m_estimate
);
1142 template <class Types
>
1143 class CYapfReserveTrack
1146 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1147 typedef typename
Types::TrackFollower TrackFollower
;
1148 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1151 /** to access inherited pathfinder */
1154 return *static_cast<Tpf
*>(this);
1158 PathPos m_res_dest
; ///< The reservation target
1159 Node
*m_res_node
; ///< The reservation target node
1160 TileIndex m_origin_tile
; ///< Tile our reservation will originate from
1162 /** Try to reserve a single track/platform. */
1163 bool ReserveSingleTrack(const PathPos
&pos
, PathPos
*fail
)
1165 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
)) {
1166 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos
.td
)));
1167 TileIndex t
= pos
.tile
;
1170 if (HasStationReservation(t
)) {
1171 /* Platform could not be reserved, undo. */
1172 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(pos
.td
));
1173 while (t
!= pos
.tile
) {
1174 t
= TILE_ADD(t
, diff
);
1175 SetRailStationReservation(t
, false);
1180 SetRailStationReservation(t
, true);
1181 MarkTileDirtyByTile(t
);
1182 t
= TILE_ADD(t
, diff
);
1183 } while (IsCompatibleTrainStationTile(t
, pos
.tile
) && t
!= m_origin_tile
);
1185 TriggerStationRandomisation(NULL
, pos
.tile
, SRT_PATH_RESERVATION
);
1187 if (!TryReserveRailTrack(pos
)) {
1188 /* Tile couldn't be reserved, undo. */
1194 return pos
!= m_res_dest
;
1197 /** Unreserve a single track/platform. Stops when the previous failer is reached. */
1198 bool UnreserveSingleTrack(const PathPos
&pos
, PathPos
*stop
)
1200 if (stop
!= NULL
&& pos
== *stop
) return false;
1202 if (!pos
.in_wormhole() && IsRailStationTile(pos
.tile
)) {
1203 TileIndexDiff diff
= TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos
.td
)));
1204 TileIndex t
= pos
.tile
;
1205 while (IsCompatibleTrainStationTile(t
, pos
.tile
) && t
!= m_origin_tile
) {
1206 assert(HasStationReservation(t
));
1207 SetRailStationReservation(t
, false);
1208 t
= TILE_ADD(t
, diff
);
1211 UnreserveRailTrack(pos
);
1214 return pos
!= m_res_dest
;
1218 /** Set the target to where the reservation should be extended. */
1219 inline void SetReservationTarget(Node
*node
, const PathPos
&pos
)
1225 /** Check the node for a possible reservation target. */
1226 inline void FindSafePositionOnNode(Node
*node
)
1228 assert(node
->m_parent
!= NULL
);
1230 /* We will never pass more than two signals, no need to check for a safe tile. */
1231 if (node
->m_parent
->m_num_signals_passed
>= 2) return;
1233 const Train
*v
= Yapf().GetVehicle();
1234 TrackFollower
ft (v
, Yapf().GetCompatibleRailTypes());
1235 ft
.SetPos (node
->GetPos());
1237 while (!IsSafeWaitingPosition(v
, ft
.m_new
, !TrackFollower::Allow90degTurns())) {
1238 if (ft
.m_new
== node
->GetLastPos()) return; // no safe position found in node
1239 bool follow
= ft
.FollowNext();
1241 assert (ft
.m_new
.is_single());
1244 m_res_dest
= ft
.m_new
;
1248 /** Try to reserve the path till the reservation target. */
1249 bool TryReservePath(TileIndex origin
, PathPos
*target
= NULL
)
1251 m_origin_tile
= origin
;
1253 if (target
!= NULL
) *target
= m_res_dest
;
1255 /* Don't bother if the target is reserved. */
1256 if (!IsWaitingPositionFree(Yapf().GetVehicle(), m_res_dest
)) return false;
1260 for (Node
*node
= m_res_node
; node
->m_parent
!= NULL
; node
= node
->m_parent
) {
1261 node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::ReserveSingleTrack
, &res_fail
);
1262 if (res_fail
.tile
!= INVALID_TILE
) {
1263 /* Reservation failed, undo. */
1264 Node
*failed_node
= node
;
1265 for (node
= m_res_node
; node
!= failed_node
; node
= node
->m_parent
) {
1266 node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::UnreserveSingleTrack
, NULL
);
1268 failed_node
->IterateTiles(Yapf().GetVehicle(), Yapf(), *this, &CYapfReserveTrack
<Types
>::UnreserveSingleTrack
, &res_fail
);
1273 if (Yapf().CanUseGlobalCache(*m_res_node
)) {
1274 YapfNotifyTrackLayoutChange(INVALID_TILE
, INVALID_TRACK
);
1281 template <class Types
>
1282 class CYapfFollowAnyDepotRailT
1285 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1286 typedef typename
Types::TrackFollower TrackFollower
;
1287 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1288 typedef typename
Node::Key Key
; ///< key to hash tables
1291 /** to access inherited path finder */
1294 return *static_cast<Tpf
*>(this);
1299 * Called by YAPF to move from the given node to the next tile. For each
1300 * reachable trackdir on the new tile creates new node, initializes it
1301 * and adds it to the open list by calling Yapf().AddNewNode(n)
1303 inline void PfFollowNode(Node
& old_node
)
1305 TrackFollower
F(Yapf().GetVehicle());
1306 if (F
.Follow(old_node
.GetLastPos())) {
1307 Yapf().AddMultipleNodes(&old_node
, F
);
1311 static bool stFindNearestDepotTwoWay(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int max_penalty
, int reverse_penalty
, TileIndex
*depot_tile
, bool *reversed
)
1315 * With caching enabled it simply cannot get a reliable result when you
1316 * have limited the distance a train may travel. This means that the
1317 * cached result does not match uncached result in all cases and that
1318 * causes desyncs. So disable caching when finding for a depot that is
1319 * nearby. This only happens with automatic servicing of vehicles,
1320 * so it will only impact performance when you do not manually set
1321 * depot orders and you do not disable automatic servicing.
1323 if (max_penalty
!= 0) pf1
.DisableCache(true);
1324 bool result1
= pf1
.FindNearestDepotTwoWay(v
, pos1
, pos2
, max_penalty
, reverse_penalty
, depot_tile
, reversed
);
1326 #if DEBUG_YAPF_CACHE
1328 TileIndex depot_tile2
= INVALID_TILE
;
1329 bool reversed2
= false;
1330 pf2
.DisableCache(true);
1331 bool result2
= pf2
.FindNearestDepotTwoWay(v
, pos1
, pos2
, max_penalty
, reverse_penalty
, &depot_tile2
, &reversed2
);
1332 if (result1
!= result2
|| (result1
&& (*depot_tile
!= depot_tile2
|| *reversed
!= reversed2
))) {
1333 DEBUG(yapf
, 0, "CACHE ERROR: FindNearestDepotTwoWay() = [%s, %s]", result1
? "T" : "F", result2
? "T" : "F");
1334 DumpState(pf1
, pf2
);
1341 inline bool FindNearestDepotTwoWay(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int max_penalty
, int reverse_penalty
, TileIndex
*depot_tile
, bool *reversed
)
1343 /* set origin and destination nodes */
1344 Yapf().SetOrigin(pos1
, pos2
, reverse_penalty
, true);
1345 Yapf().SetDestination(v
);
1346 Yapf().SetMaxCost(max_penalty
);
1348 /* find the best path */
1349 bool bFound
= Yapf().FindPath(v
);
1350 if (!bFound
) return false;
1353 * get found depot tile */
1354 Node
*n
= Yapf().GetBestNode();
1355 *depot_tile
= n
->GetLastPos().tile
;
1357 /* walk through the path back to the origin */
1359 while (pNode
->m_parent
!= NULL
) {
1360 pNode
= pNode
->m_parent
;
1363 /* if the origin node is our front vehicle tile/Trackdir then we didn't reverse
1364 * but we can also look at the cost (== 0 -> not reversed, == reverse_penalty -> reversed) */
1365 *reversed
= (pNode
->m_cost
!= 0);
1371 template <class Types
>
1372 class CYapfFollowAnySafeTileRailT
: public CYapfReserveTrack
<Types
>
1375 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1376 typedef typename
Types::TrackFollower TrackFollower
;
1377 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1378 typedef typename
Node::Key Key
; ///< key to hash tables
1381 /** to access inherited path finder */
1384 return *static_cast<Tpf
*>(this);
1389 * Called by YAPF to move from the given node to the next tile. For each
1390 * reachable trackdir on the new tile creates new node, initializes it
1391 * and adds it to the open list by calling Yapf().AddNewNode(n)
1393 inline void PfFollowNode(Node
& old_node
)
1395 TrackFollower
F(Yapf().GetVehicle(), Yapf().GetCompatibleRailTypes());
1396 if (F
.Follow(old_node
.GetLastPos()) && F
.MaskReservedTracks()) {
1397 Yapf().AddMultipleNodes(&old_node
, F
);
1401 static bool stFindNearestSafeTile(const Train
*v
, const PathPos
&pos
, bool override_railtype
)
1403 /* Create pathfinder instance */
1405 #if !DEBUG_YAPF_CACHE
1406 bool result1
= pf1
.FindNearestSafeTile(v
, pos
, override_railtype
, false);
1409 bool result2
= pf1
.FindNearestSafeTile(v
, pos
, override_railtype
, true);
1411 pf2
.DisableCache(true);
1412 bool result1
= pf2
.FindNearestSafeTile(v
, pos
, override_railtype
, false);
1413 if (result1
!= result2
) {
1414 DEBUG(yapf
, 0, "CACHE ERROR: FindSafeTile() = [%s, %s]", result2
? "T" : "F", result1
? "T" : "F");
1415 DumpState(pf1
, pf2
);
1422 bool FindNearestSafeTile(const Train
*v
, const PathPos
&pos
, bool override_railtype
, bool dont_reserve
)
1424 /* Set origin and destination. */
1425 Yapf().SetOrigin(pos
);
1426 Yapf().SetDestination(v
, override_railtype
);
1428 bool bFound
= Yapf().FindPath(v
);
1429 if (!bFound
) return false;
1431 /* Found a destination, set as reservation target. */
1432 Node
*pNode
= Yapf().GetBestNode();
1433 this->SetReservationTarget(pNode
, pNode
->GetLastPos());
1435 /* Walk through the path back to the origin. */
1437 while (pNode
->m_parent
!= NULL
) {
1439 pNode
= pNode
->m_parent
;
1441 this->FindSafePositionOnNode(pPrev
);
1444 return dont_reserve
|| this->TryReservePath(pNode
->GetLastPos().tile
);
1448 template <class Types
>
1449 class CYapfFollowRailT
: public CYapfReserveTrack
<Types
>
1452 typedef typename
Types::Tpf Tpf
; ///< the pathfinder class (derived from THIS class)
1453 typedef typename
Types::TrackFollower TrackFollower
;
1454 typedef typename
Types::Astar::Node Node
; ///< this will be our node type
1455 typedef typename
Node::Key Key
; ///< key to hash tables
1458 /** to access inherited path finder */
1461 return *static_cast<Tpf
*>(this);
1466 * Called by YAPF to move from the given node to the next tile. For each
1467 * reachable trackdir on the new tile creates new node, initializes it
1468 * and adds it to the open list by calling Yapf().AddNewNode(n)
1470 inline void PfFollowNode(Node
& old_node
)
1472 TrackFollower
F(Yapf().GetVehicle());
1473 if (F
.Follow(old_node
.GetLastPos())) {
1474 Yapf().AddMultipleNodes(&old_node
, F
);
1478 static Trackdir
stChooseRailTrack(const Train
*v
, const PathPos
&origin
, bool reserve_track
, PFResult
*target
)
1480 /* create pathfinder instance */
1482 #if !DEBUG_YAPF_CACHE
1483 Trackdir result1
= pf1
.ChooseRailTrack(v
, origin
, reserve_track
, target
);
1486 Trackdir result1
= pf1
.ChooseRailTrack(v
, origin
, false, NULL
);
1488 pf2
.DisableCache(true);
1489 Trackdir result2
= pf2
.ChooseRailTrack(v
, origin
, reserve_track
, target
);
1490 if (result1
!= result2
) {
1491 DEBUG(yapf
, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1
, result2
);
1492 DumpState(pf1
, pf2
);
1499 inline Trackdir
ChooseRailTrack(const Train
*v
, const PathPos
&origin
, bool reserve_track
, PFResult
*target
)
1501 if (target
!= NULL
) target
->pos
.tile
= INVALID_TILE
;
1503 /* set origin and destination nodes */
1504 Yapf().SetOrigin(origin
);
1505 Yapf().SetDestination(v
);
1507 /* find the best path */
1508 bool path_found
= Yapf().FindPath(v
);
1510 /* if path not found - return INVALID_TRACKDIR */
1511 Trackdir next_trackdir
= INVALID_TRACKDIR
;
1512 Node
*pNode
= Yapf().GetBestNode();
1513 if (pNode
!= NULL
) {
1514 /* reserve till end of path */
1515 this->SetReservationTarget(pNode
, pNode
->GetLastPos());
1517 /* path was found or at least suggested
1518 * walk through the path back to the origin */
1520 while (pNode
->m_parent
!= NULL
) {
1522 pNode
= pNode
->m_parent
;
1524 this->FindSafePositionOnNode(pPrev
);
1526 /* return trackdir from the best origin node (one of start nodes) */
1527 Node
& best_next_node
= *pPrev
;
1528 next_trackdir
= best_next_node
.GetPos().td
;
1530 if (reserve_track
&& path_found
) {
1531 bool okay
= this->TryReservePath(pNode
->GetLastPos().tile
, target
!= NULL
? &target
->pos
: NULL
);
1532 if (target
!= NULL
) target
->okay
= okay
;
1536 /* Treat the path as found if stopped on the first two way signal(s). */
1537 if (target
!= NULL
) target
->found
= path_found
| Yapf().m_stopped_on_first_two_way_signal
;
1538 return next_trackdir
;
1541 static bool stCheckReverseTrain(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int reverse_penalty
)
1544 bool result1
= pf1
.CheckReverseTrain(v
, pos1
, pos2
, reverse_penalty
);
1546 #if DEBUG_YAPF_CACHE
1548 pf2
.DisableCache(true);
1549 bool result2
= pf2
.CheckReverseTrain(v
, pos1
, pos2
, reverse_penalty
);
1550 if (result1
!= result2
) {
1551 DEBUG(yapf
, 0, "CACHE ERROR: CheckReverseTrain() = [%s, %s]", result1
? "T" : "F", result2
? "T" : "F");
1552 DumpState(pf1
, pf2
);
1559 inline bool CheckReverseTrain(const Train
*v
, const PathPos
&pos1
, const PathPos
&pos2
, int reverse_penalty
)
1561 /* create pathfinder instance
1562 * set origin and destination nodes */
1563 Yapf().SetOrigin(pos1
, pos2
, reverse_penalty
, false);
1564 Yapf().SetDestination(v
);
1566 /* find the best path */
1567 bool bFound
= Yapf().FindPath(v
);
1569 if (!bFound
) return false;
1572 * walk through the path back to the origin */
1573 Node
*pNode
= Yapf().GetBestNode();
1574 while (pNode
->m_parent
!= NULL
) {
1575 pNode
= pNode
->m_parent
;
1578 /* check if it was reversed origin */
1579 Node
& best_org_node
= *pNode
;
1580 bool reversed
= (best_org_node
.m_cost
!= 0);
1585 template <class Tpf_
, class Ttrack_follower
>
1586 struct CYapfRail_TypesT
1588 typedef CYapfRail_TypesT
<Tpf_
, Ttrack_follower
> Types
;
1591 typedef Ttrack_follower TrackFollower
;
1592 typedef AstarRailTrackDir Astar
;
1596 : CYapfRailT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1597 , CYapfDestinationTileOrStationRailT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1598 , CYapfFollowRailT
<CYapfRail_TypesT
<CYapfRail1
, CFollowTrackRail90
> >
1603 : CYapfRailT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1604 , CYapfDestinationTileOrStationRailT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1605 , CYapfFollowRailT
<CYapfRail_TypesT
<CYapfRail2
, CFollowTrackRailNo90
> >
1609 struct CYapfAnyDepotRail1
1610 : CYapfRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1611 , CYapfDestinationAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1612 , CYapfFollowAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail1
, CFollowTrackRail90
> >
1616 struct CYapfAnyDepotRail2
1617 : CYapfRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1618 , CYapfDestinationAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1619 , CYapfFollowAnyDepotRailT
<CYapfRail_TypesT
<CYapfAnyDepotRail2
, CFollowTrackRailNo90
> >
1623 struct CYapfAnySafeTileRail1
1624 : CYapfRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1625 , CYapfDestinationAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1626 , CYapfFollowAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail1
, CFollowTrackFreeRail90
> >
1630 struct CYapfAnySafeTileRail2
1631 : CYapfRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1632 , CYapfDestinationAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1633 , CYapfFollowAnySafeTileRailT
<CYapfRail_TypesT
<CYapfAnySafeTileRail2
, CFollowTrackFreeRailNo90
> >
1638 Trackdir
YapfTrainChooseTrack(const Train
*v
, const PathPos
&origin
, bool reserve_track
, PFResult
*target
)
1640 /* default is YAPF type 2 */
1641 typedef Trackdir (*PfnChooseRailTrack
)(const Train
*, const PathPos
&, bool, PFResult
*);
1642 PfnChooseRailTrack pfnChooseRailTrack
= &CYapfRail1::stChooseRailTrack
;
1644 /* check if non-default YAPF type needed */
1645 if (_settings_game
.pf
.forbid_90_deg
) {
1646 pfnChooseRailTrack
= &CYapfRail2::stChooseRailTrack
; // Trackdir, forbid 90-deg
1649 return pfnChooseRailTrack(v
, origin
, reserve_track
, target
);
1652 bool YapfTrainCheckReverse(const Train
*v
)
1654 const Train
*last_veh
= v
->Last();
1656 /* tiles where front and back are */
1657 PathPos pos
= v
->GetPos();
1658 PathPos rev
= last_veh
->GetReversePos();
1660 int reverse_penalty
= 0;
1662 if (pos
.in_wormhole()) {
1663 /* front in tunnel / on bridge */
1664 assert(TrackdirToExitdir(pos
.td
) == ReverseDiagDir(GetTunnelBridgeDirection(pos
.wormhole
)));
1666 /* Current position of the train in the wormhole */
1667 TileIndex cur_tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
1669 /* Add distance to drive in the wormhole as penalty for the forward path, i.e. bonus for the reverse path
1670 * Note: Negative penalties are ok for the start tile. */
1671 reverse_penalty
-= DistanceManhattan(cur_tile
, pos
.tile
) * YAPF_TILE_LENGTH
;
1674 if (rev
.in_wormhole()) {
1675 /* back in tunnel / on bridge */
1676 assert(TrackdirToExitdir(rev
.td
) == ReverseDiagDir(GetTunnelBridgeDirection(rev
.wormhole
)));
1678 /* Current position of the last wagon in the wormhole */
1679 TileIndex cur_tile
= TileVirtXY(last_veh
->x_pos
, last_veh
->y_pos
);
1681 /* Add distance to drive in the wormhole as penalty for the revere path. */
1682 reverse_penalty
+= DistanceManhattan(cur_tile
, rev
.tile
) * YAPF_TILE_LENGTH
;
1685 typedef bool (*PfnCheckReverseTrain
)(const Train
*, const PathPos
&, const PathPos
&, int);
1686 PfnCheckReverseTrain pfnCheckReverseTrain
= CYapfRail1::stCheckReverseTrain
;
1688 /* check if non-default YAPF type needed */
1689 if (_settings_game
.pf
.forbid_90_deg
) {
1690 pfnCheckReverseTrain
= &CYapfRail2::stCheckReverseTrain
; // Trackdir, forbid 90-deg
1693 /* slightly hackish: If the pathfinders finds a path, the cost of the first node is tested to distinguish between forward- and reverse-path. */
1694 if (reverse_penalty
== 0) reverse_penalty
= 1;
1696 bool reverse
= pfnCheckReverseTrain(v
, pos
, rev
, reverse_penalty
);
1701 bool YapfTrainFindNearestDepot(const Train
*v
, uint max_penalty
, FindDepotData
*res
)
1704 FollowTrainReservation(v
, &origin
);
1705 PathPos rev
= v
->Last()->GetReversePos();
1707 typedef bool (*PfnFindNearestDepotTwoWay
)(const Train
*, const PathPos
&, const PathPos
&, int, int, TileIndex
*, bool*);
1708 PfnFindNearestDepotTwoWay pfnFindNearestDepotTwoWay
= &CYapfAnyDepotRail1::stFindNearestDepotTwoWay
;
1710 /* check if non-default YAPF type needed */
1711 if (_settings_game
.pf
.forbid_90_deg
) {
1712 pfnFindNearestDepotTwoWay
= &CYapfAnyDepotRail2::stFindNearestDepotTwoWay
; // Trackdir, forbid 90-deg
1715 return pfnFindNearestDepotTwoWay(v
, origin
, rev
, max_penalty
, YAPF_INFINITE_PENALTY
, &res
->tile
, &res
->reverse
);
1718 bool YapfTrainFindNearestSafeTile(const Train
*v
, const PathPos
&pos
, bool override_railtype
)
1720 typedef bool (*PfnFindNearestSafeTile
)(const Train
*, const PathPos
&, bool);
1721 PfnFindNearestSafeTile pfnFindNearestSafeTile
= CYapfAnySafeTileRail1::stFindNearestSafeTile
;
1723 /* check if non-default YAPF type needed */
1724 if (_settings_game
.pf
.forbid_90_deg
) {
1725 pfnFindNearestSafeTile
= &CYapfAnySafeTileRail2::stFindNearestSafeTile
;
1728 return pfnFindNearestSafeTile(v
, pos
, override_railtype
);