Remove GetTileRailType
[openttd/fttd.git] / src / pathfinder / yapf / yapf_rail.cpp
blobc941bf2c9b80ad94709ffe072a4128b922983e03
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file yapf_rail.cpp The rail pathfinding. */
12 #include "../../stdafx.h"
14 #include "yapf.hpp"
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
29 #if DEBUG_YAPF_CACHE
30 template <typename Tpf> void DumpState(Tpf &pf1, Tpf &pf2)
32 DumpTarget dmp1, dmp2;
33 pf1.Dump(dmp1);
34 pf2.Dump(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);
39 fclose(f1);
40 fclose(f2);
42 #endif
44 int _total_pf_time_us = 0;
47 /**
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
52 * function.
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);
73 /**
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
93 HashTable m_map;
94 Heap m_heap;
96 inline CSegmentCostCacheT() {}
98 /** flush (clear) the cache */
99 inline void Flush()
101 m_map.Clear();
102 m_heap.Clear();
105 inline bool Get(const Key& key, Tsegment **item)
107 *item = m_map.Find(key);
108 if (*item != NULL) return true;
109 *item = new (m_heap.Append()) Tsegment(key);
110 m_map.Push(**item);
111 return false;
116 template <class TAstar>
117 class CYapfRailBaseT : public TAstar
119 public:
120 typedef typename TAstar::Node Node; ///< this will be our node type
121 typedef typename Node::Key Key; ///< key to hash tables
122 typedef typename Node::CachedData CachedData;
123 typedef typename CachedData::Key CacheKey;
124 typedef CSegmentCostCacheT<CachedData> Cache;
125 typedef SmallArray<CachedData> LocalCache;
127 protected:
128 const YAPFSettings *const m_settings; ///< current settings (_settings_game.yapf)
129 const Train *const m_veh; ///< vehicle that we are trying to drive
130 const RailTypes m_compatible_railtypes;
131 const bool mask_reserved_tracks;
132 bool m_treat_first_red_two_way_signal_as_eol; ///< in some cases (leaving station) we need to handle first two-way signal differently
133 public:
134 bool m_stopped_on_first_two_way_signal;
136 protected:
137 bool m_disable_cache;
138 Cache& m_global_cache;
139 LocalCache m_local_cache;
142 * @note maximum cost doesn't work with caching enabled
143 * @todo fix maximum cost failing with caching (e.g. FS#2900)
145 int m_max_cost;
146 CBlobT<int> m_sig_look_ahead_costs;
148 int m_stats_cost_calcs; ///< stats - how many node's costs were calculated
149 int m_stats_cache_hits; ///< stats - how many node's costs were reused from cache
151 CPerformanceTimer m_perf_cost; ///< stats - total CPU time of this run
152 CPerformanceTimer m_perf_slope_cost; ///< stats - slope calculation CPU time
153 CPerformanceTimer m_perf_ts_cost; ///< stats - GetTrackStatus() CPU time
154 CPerformanceTimer m_perf_other_cost; ///< stats - other CPU time
156 CFollowTrackRail tf; ///< track follower to be used by Follow
157 CFollowTrackRail tf_local; ///< track follower to be used by CalcSegment
159 static const int s_max_segment_cost = 10000;
161 inline static Cache& stGetGlobalCache()
163 static int last_rail_change_counter = 0;
164 static Date last_date = 0;
165 static Cache C;
167 /* some statistics */
168 if (last_date != _date) {
169 last_date = _date;
170 DEBUG(yapf, 2, "Pf time today: %5d ms", _total_pf_time_us / 1000);
171 _total_pf_time_us = 0;
174 /* delete the cache sometimes... */
175 if (last_rail_change_counter != Cache::s_rail_change_counter) {
176 last_rail_change_counter = Cache::s_rail_change_counter;
177 C.Flush();
180 return C;
183 CYapfRailBaseT (const Train *v, bool allow_90deg, bool override_rail_type, bool mask_reserved_tracks)
184 : m_settings(&_settings_game.pf.yapf)
185 , m_veh(v)
186 , m_compatible_railtypes(v->compatible_railtypes | (override_rail_type ? GetRailTypeInfo(v->railtype)->compatible_railtypes : RAILTYPES_NONE))
187 , mask_reserved_tracks(mask_reserved_tracks)
188 , m_treat_first_red_two_way_signal_as_eol(true)
189 , m_stopped_on_first_two_way_signal(false)
190 , m_disable_cache(false)
191 , m_global_cache(stGetGlobalCache())
192 , m_max_cost(0)
193 , m_stats_cost_calcs(0)
194 , m_stats_cache_hits(0)
195 , tf (v, allow_90deg, mask_reserved_tracks ? m_compatible_railtypes : v->compatible_railtypes)
196 , tf_local (v, allow_90deg, m_compatible_railtypes, &m_perf_ts_cost)
198 /* pre-compute look-ahead penalties into array */
199 int p0 = m_settings->rail_look_ahead_signal_p0;
200 int p1 = m_settings->rail_look_ahead_signal_p1;
201 int p2 = m_settings->rail_look_ahead_signal_p2;
202 int *pen = m_sig_look_ahead_costs.GrowSizeNC(m_settings->rail_look_ahead_max_signals);
203 for (uint i = 0; i < m_settings->rail_look_ahead_max_signals; i++) {
204 pen[i] = p0 + i * (p1 + i * p2);
208 public:
209 inline bool Allow90degTurns (void) const
211 return tf.m_allow_90deg;
214 inline bool CanUseGlobalCache(Node& n) const
216 return !m_disable_cache
217 && (n.m_parent != NULL)
218 && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.Size());
221 protected:
223 * Called by YAPF to attach cached or local segment cost data to the given node.
224 * @return true if globally cached data were used or false if local data was used
226 inline bool AttachSegmentToNode(Node *n)
228 CacheKey key(n->GetKey());
229 if (!CanUseGlobalCache(*n)) {
230 n->m_segment = new (m_local_cache.Append()) CachedData(key);
231 n->m_segment->m_last = n->GetPos();
232 return false;
234 bool found = m_global_cache.Get(key, &n->m_segment);
235 if (!found) n->m_segment->m_last = n->GetPos();
236 return found;
239 public:
240 /** Create and add a new node */
241 inline void AddStartupNode (const RailPathPos &pos, int cost = 0)
243 Node *node = TAstar::CreateNewNode (NULL, pos, false);
244 node->m_cost = cost;
245 AttachSegmentToNode(node);
246 TAstar::InsertInitialNode(node);
249 /** set origin */
250 void SetOrigin(const RailPathPos &pos)
252 AddStartupNode (pos);
255 /** set origin */
256 void SetOrigin(const RailPathPos &pos, const RailPathPos &rev, int reverse_penalty, bool treat_first_red_two_way_signal_as_eol)
258 m_treat_first_red_two_way_signal_as_eol = treat_first_red_two_way_signal_as_eol;
260 AddStartupNode (pos);
261 AddStartupNode (rev, reverse_penalty);
264 inline void SetMaxCost (int max_cost)
266 m_max_cost = max_cost;
269 inline void DisableCache(bool disable)
271 m_disable_cache = disable;
275 * In some cases an intermediate node branch should be pruned.
276 * The most prominent case is when a red EOL signal is encountered, but
277 * there was a segment change (e.g. a rail type change) before that. If
278 * the branch would not be pruned, the rail type change location would
279 * remain the best intermediate node, and thus the vehicle would still
280 * go towards the red EOL signal.
282 void PruneIntermediateNodeBranch()
284 while (TAstar::best_intermediate != NULL && (TAstar::best_intermediate->m_segment->m_end_segment_reason & ESRB_CHOICE_FOLLOWS) == 0) {
285 TAstar::best_intermediate = TAstar::best_intermediate->m_parent;
289 struct NodeData {
290 int parent_cost;
291 int entry_cost;
292 int segment_cost;
293 int extra_cost;
294 RailPathPos pos;
295 RailPathPos last_signal;
296 EndSegmentReasonBits end_reason;
299 /** Return the transition cost from one tile to another. */
300 inline int TransitionCost (const RailPathPos &pos1, const RailPathPos &pos2) const
302 assert(IsValidTrackdir(pos1.td));
303 assert(IsValidTrackdir(pos2.td));
305 if (pos1.in_wormhole() || !IsRailwayTile(pos1.tile)) {
306 assert(IsDiagonalTrackdir(pos1.td));
307 if (pos2.in_wormhole() || !IsRailwayTile(pos2.tile)) {
308 /* compare only the tracks, as depots cause reversing */
309 assert(TrackdirToTrack(pos1.td) == TrackdirToTrack(pos2.td));
310 return 0;
311 } else {
312 return (pos1.td == pos2.td) ? 0 : m_settings->rail_curve45_penalty;
314 } else {
315 if (pos2.in_wormhole() || !IsRailwayTile(pos2.tile)) {
316 assert(IsDiagonalTrackdir(pos2.td));
317 return (pos1.td == pos2.td) ? 0 : m_settings->rail_curve45_penalty;
321 /* both tiles are railway tiles */
323 int cost = 0;
324 if ((TrackdirToTrackdirBits(pos2.td) & (TrackdirBits)TrackdirCrossesTrackdirs(pos1.td)) != 0) {
325 /* 90-deg curve penalty */
326 cost += m_settings->rail_curve90_penalty;
327 } else if (pos2.td != NextTrackdir(pos1.td)) {
328 /* 45-deg curve penalty */
329 cost += m_settings->rail_curve45_penalty;
332 DiagDirection exitdir = TrackdirToExitdir(pos1.td);
333 bool t1 = KillFirstBit(GetTrackBits(pos1.tile) & DiagdirReachesTracks(ReverseDiagDir(exitdir))) != TRACK_BIT_NONE;
334 bool t2 = KillFirstBit(GetTrackBits(pos2.tile) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE;
335 if (t1 && t2) cost += m_settings->rail_doubleslip_penalty;
337 return cost;
340 /** Return one tile cost (base cost + level crossing penalty). */
341 inline int OneTileCost (const RailPathPos &pos) const
343 int cost = 0;
344 /* set base cost */
345 if (IsDiagonalTrackdir(pos.td)) {
346 cost += YAPF_TILE_LENGTH;
347 if (IsLevelCrossingTile(pos.tile)) {
348 /* Increase the cost for level crossings */
349 cost += m_settings->rail_crossing_penalty;
351 } else {
352 /* non-diagonal trackdir */
353 cost = YAPF_TILE_CORNER_LENGTH;
355 return cost;
358 /** Return slope cost for a tile. */
359 inline int SlopeCost (const RailPathPos &pos)
361 CPerfStart perf_cost(m_perf_slope_cost);
363 if (pos.in_wormhole() || !IsDiagonalTrackdir(pos.td)) return 0;
365 /* Only rail tracks and bridgeheads can have sloped rail. */
366 if (!IsRailwayTile(pos.tile)) return 0;
368 bool uphill;
369 if (IsTileSubtype(pos.tile, TT_BRIDGE)) {
370 /* it is bridge ramp, check if we are entering the bridge */
371 DiagDirection dir = GetTunnelBridgeDirection(pos.tile);
372 if (dir != TrackdirToExitdir(pos.td)) return 0; // no, we are leaving it, no penalty
373 /* we are entering the bridge */
374 Slope tile_slope = GetTileSlope(pos.tile);
375 Axis axis = DiagDirToAxis(dir);
376 uphill = !HasBridgeFlatRamp(tile_slope, axis);
377 } else {
378 /* not bridge ramp */
379 Slope tile_slope = GetTileSlope(pos.tile);
380 uphill = IsUphillTrackdir(tile_slope, pos.td); // slopes uphill => apply penalty
383 return uphill ? m_settings->rail_slope_penalty : 0;
386 /** Check for a reserved station platform. */
387 static inline bool IsAnyStationTileReserved (const RailPathPos &pos, int skipped)
389 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos.td)));
390 for (TileIndex tile = pos.tile; skipped >= 0; skipped--, tile += diff) {
391 if (HasStationReservation(tile)) return true;
393 return false;
396 /** The cost for reserved tiles, including skipped ones. */
397 inline int ReservationCost(Node& n, const RailPathPos &pos, int skipped) const
399 if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 2) return 0;
400 if (!IsPbsSignal(n.m_last_signal_type)) return 0;
402 if (!pos.in_wormhole() && IsRailStationTile(pos.tile) && IsAnyStationTileReserved(pos, skipped)) {
403 return m_settings->rail_pbs_station_penalty * (skipped + 1);
404 } else if (TrackOverlapsTracks(GetReservedTrackbits(pos.tile), TrackdirToTrack(pos.td))) {
405 int cost = m_settings->rail_pbs_cross_penalty;
406 if (!IsDiagonalTrackdir(pos.td)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH;
407 return cost * (skipped + 1);
409 return 0;
412 /** Penalty for platform shorter/longer than needed. */
413 inline int PlatformLengthPenalty (int platform_length) const
415 int cost = 0;
416 const Train *v = m_veh;
417 assert(v->gcache.cached_total_length != 0);
418 int missing_platform_length = CeilDiv(v->gcache.cached_total_length, TILE_SIZE) - platform_length;
419 if (missing_platform_length < 0) {
420 /* apply penalty for longer platform than needed */
421 cost += m_settings->rail_longer_platform_penalty + m_settings->rail_longer_platform_per_tile_penalty * -missing_platform_length;
422 } else if (missing_platform_length > 0) {
423 /* apply penalty for shorter platform than needed */
424 cost += m_settings->rail_shorter_platform_penalty + m_settings->rail_shorter_platform_per_tile_penalty * missing_platform_length;
426 return cost;
429 /* Compute cost and modify node state for a signal. */
430 inline int SignalCost(Node& n, const RailPathPos &pos, NodeData *data);
432 /* Compute cost and modify node state for a position. */
433 inline void HandleNodeTile (Node *n, const CFollowTrackRail *tf, NodeData *segment, TileIndex prev);
435 /* Check for possible reasons to end a segment at the next tile. */
436 inline bool HandleNodeNextTile (Node *n, CFollowTrackRail *tf, NodeData *segment, RailType rail_type);
438 /** Compute all costs for a newly-allocated (not cached) segment. */
439 inline EndSegmentReasonBits CalcSegment (Node *n, const CFollowTrackRail *tf);
441 /* Fill in a node from cached data. */
442 inline EndSegmentReasonBits RestoreCachedNode (Node *n);
444 /* Add special extra cost when the segment reaches our target. */
445 inline void AddTargetCost (Node *n, bool is_station);
447 /** Struct to store a position in a path (node and path position). */
448 struct NodePos {
449 RailPathPos pos; ///< position (tile and trackdir)
450 Node *node; ///< node where the position is
453 /* Find the earliest safe position on a path. */
454 Node *FindSafePositionOnPath (Node *node, NodePos *res);
456 /* Try to reserve the path up to a given position. */
457 bool TryReservePath (TileIndex origin, const NodePos *res);
460 /** Compute cost and modify node state for a signal. */
461 template <class TAstar>
462 inline int CYapfRailBaseT<TAstar>::SignalCost(Node& n, const RailPathPos &pos, NodeData *data)
464 int cost = 0;
465 /* if there is one-way signal in the opposite direction, then it is not our way */
466 CPerfStart perf_cost(m_perf_other_cost);
468 if (HasSignalAlongPos(pos)) {
469 SignalState sig_state = GetSignalStateByPos(pos);
470 SignalType sig_type = GetSignalType(pos);
472 n.m_last_signal_type = sig_type;
474 /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */
475 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;
476 if (sig_state != SIGNAL_STATE_RED) {
477 /* green signal */
478 n.flags.reset (n.FLAG_LAST_SIGNAL_WAS_RED);
479 /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */
480 if (look_ahead_cost < 0) {
481 /* add its negation to the cost */
482 cost -= look_ahead_cost;
484 } else {
485 /* we have a red signal in our direction
486 * was it first signal which is two-way? */
487 if (!IsPbsSignal(sig_type)
488 && m_settings->rail_firstred_twoway_eol
489 && m_treat_first_red_two_way_signal_as_eol
490 && n.flags.test(n.FLAG_CHOICE_SEEN)
491 && HasSignalAgainstPos(pos)
492 && n.m_num_signals_passed == 0) {
493 /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
494 PruneIntermediateNodeBranch();
495 data->end_reason |= ESRB_DEAD_END;
496 m_stopped_on_first_two_way_signal = true;
497 return -1;
499 n.m_last_red_signal_type = sig_type;
500 n.flags.set (n.FLAG_LAST_SIGNAL_WAS_RED);
502 /* look-ahead signal penalty */
503 if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
504 /* add the look ahead penalty only if it is positive */
505 cost += look_ahead_cost;
508 /* special signal penalties */
509 if (n.m_num_signals_passed == 0) {
510 switch (sig_type) {
511 case SIGTYPE_COMBO:
512 case SIGTYPE_EXIT: cost += m_settings->rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
513 case SIGTYPE_NORMAL:
514 case SIGTYPE_ENTRY: cost += m_settings->rail_firstred_penalty; break;
515 default: break;
520 n.m_num_signals_passed++;
521 data->last_signal = pos;
522 } else if (HasSignalAgainstPos(pos)) {
523 if (GetSignalType(pos) != SIGTYPE_PBS) {
524 /* one-way signal in opposite direction */
525 data->end_reason |= ESRB_DEAD_END;
526 } else {
527 cost += n.m_num_signals_passed < m_settings->rail_look_ahead_max_signals ? m_settings->rail_pbs_signal_back_penalty : 0;
531 return cost;
534 /** Compute cost and modify node state for a position. */
535 template <class TAstar>
536 inline void CYapfRailBaseT<TAstar>::HandleNodeTile (Node *n, const CFollowTrackRail *tf, NodeData *segment, TileIndex prev)
538 /* All other tile costs will be calculated here. */
539 segment->segment_cost += OneTileCost(segment->pos);
541 /* If we skipped some tunnel/bridge/station tiles, add their base cost */
542 segment->segment_cost += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
544 /* Slope cost. */
545 segment->segment_cost += SlopeCost(segment->pos);
547 /* Signal cost (routine can modify segment data). */
548 segment->segment_cost += SignalCost(*n, segment->pos, segment);
550 /* Reserved tiles. */
551 segment->segment_cost += ReservationCost(*n, segment->pos, tf->m_tiles_skipped);
553 /* Tests for 'potential target' reasons to close the segment. */
554 if (segment->pos.tile == prev) {
555 /* Penalty for reversing in a depot. */
556 assert(!segment->pos.in_wormhole());
557 assert(IsRailDepotTile(segment->pos.tile));
558 assert(segment->pos.td == DiagDirToDiagTrackdir(GetGroundDepotDirection(segment->pos.tile)));
559 segment->segment_cost += m_settings->rail_depot_reverse_penalty;
560 /* We will end in this pass (depot is possible target) */
561 segment->end_reason |= ESRB_DEPOT;
563 } else if (!segment->pos.in_wormhole() && IsRailWaypointTile(segment->pos.tile)) {
564 const Train *v = m_veh;
565 if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
566 GetStationIndex(segment->pos.tile) == v->current_order.GetDestination() &&
567 !Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) {
568 /* This waypoint is our destination; maybe this isn't an unreserved
569 * one, so check that and if so see that as the last signal being
570 * red. This way waypoints near stations should work better. */
571 CFollowTrackRail ft(v);
572 ft.SetPos(segment->pos);
574 bool add_extra_cost;
575 for (;;) {
576 if (!ft.FollowNext()) {
577 /* end of line */
578 add_extra_cost = !IsWaitingPositionFree(v, ft.m_old, _settings_game.pf.forbid_90_deg);
579 break;
582 assert(ft.m_old.tile != ft.m_new.tile);
583 if (!ft.m_new.is_single()) {
584 /* We encountered a junction; it's going to be too complex to
585 * handle this perfectly, so just bail out. There is no simple
586 * free path, so try the other possibilities. */
587 add_extra_cost = true;
588 break;
591 /* If this is a safe waiting position we're done searching for it */
592 PBSPositionState state = CheckWaitingPosition (v, ft.m_new, _settings_game.pf.forbid_90_deg);
593 if (state != PBS_UNSAFE) {
594 add_extra_cost = state == PBS_BUSY;
595 break;
599 /* In the case this platform is (possibly) occupied we add penalty so the
600 * other platforms of this waypoint are evaluated as well, i.e. we assume
601 * that there is a red signal in the waypoint when it's occupied. */
602 if (add_extra_cost) segment->extra_cost += m_settings->rail_lastred_penalty;
604 /* Waypoint is also a good reason to finish. */
605 segment->end_reason |= ESRB_WAYPOINT;
607 } else if (tf->m_flag == tf->TF_STATION) {
608 /* Station penalties. */
609 uint platform_length = tf->m_tiles_skipped + 1;
610 /* We don't know yet if the station is our target or not. Act like
611 * if it is pass-through station (not our destination). */
612 segment->segment_cost += m_settings->rail_station_penalty * platform_length;
613 /* We will end in this pass (station is possible target) */
614 segment->end_reason |= ESRB_STATION;
616 } else if (mask_reserved_tracks) {
617 /* Searching for a safe tile? */
618 if (HasSignalAlongPos(segment->pos) && !IsPbsSignal(GetSignalType(segment->pos))) {
619 segment->end_reason |= ESRB_SAFE_TILE;
623 /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
624 * it would cause desync in MP. */
625 if (n->m_num_signals_passed < m_sig_look_ahead_costs.Size())
627 int min_speed = 0;
628 int max_speed = tf->GetSpeedLimit(&min_speed);
629 int max_veh_speed = m_veh->GetDisplayMaxSpeed();
630 if (max_speed < max_veh_speed) {
631 segment->extra_cost += YAPF_TILE_LENGTH * (max_veh_speed - max_speed) * (1 + tf->m_tiles_skipped) / max_veh_speed;
633 if (min_speed > max_veh_speed) {
634 segment->extra_cost += YAPF_TILE_LENGTH * (min_speed - max_veh_speed);
638 /* Finish if we already exceeded the maximum path cost (i.e. when
639 * searching for the nearest depot). */
640 if (m_max_cost > 0 && (segment->parent_cost + segment->entry_cost + segment->segment_cost) > m_max_cost) {
641 segment->end_reason |= ESRB_PATH_TOO_LONG;
645 /** Check for possible reasons to end a segment at the next tile. */
646 template <class TAstar>
647 inline bool CYapfRailBaseT<TAstar>::HandleNodeNextTile (Node *n, CFollowTrackRail *tf, NodeData *segment, RailType rail_type)
649 if (!tf->Follow(segment->pos)) {
650 assert(tf->m_err != CFollowTrackRail::EC_NONE);
651 /* Can't move to the next tile (EOL?). */
652 if (tf->m_err == CFollowTrackRail::EC_RAIL_TYPE) {
653 segment->end_reason |= ESRB_RAIL_TYPE;
654 } else {
655 segment->end_reason |= ESRB_DEAD_END;
658 if (mask_reserved_tracks && !HasOnewaySignalBlockingPos(segment->pos)) {
659 segment->end_reason |= ESRB_SAFE_TILE;
661 return false;
664 /* Check if the next tile is not a choice. */
665 if (!tf->m_new.is_single()) {
666 /* More than one segment will follow. Close this one. */
667 segment->end_reason |= ESRB_CHOICE_FOLLOWS;
668 return false;
671 /* Gather the next tile/trackdir. */
673 if (mask_reserved_tracks) {
674 if (HasSignalAlongPos(tf->m_new) && IsPbsSignal(GetSignalType(tf->m_new))) {
675 /* Possible safe tile. */
676 segment->end_reason |= ESRB_SAFE_TILE;
677 } else if (HasSignalAgainstPos(tf->m_new) && GetSignalType(tf->m_new) == SIGTYPE_PBS_ONEWAY) {
678 /* Possible safe tile, but not so good as it's the back of a signal... */
679 segment->end_reason |= ESRB_SAFE_TILE | ESRB_DEAD_END;
680 segment->extra_cost += m_settings->rail_lastred_exit_penalty;
684 /* Check the next tile for the rail type. */
685 if (tf->m_new.in_wormhole()) {
686 RailType next_rail_type = IsRailwayTile(tf->m_new.wormhole) ? GetBridgeRailType(tf->m_new.wormhole) : GetRailType(tf->m_new.wormhole);
687 assert(next_rail_type == rail_type);
688 } else if (GetRailType(tf->m_new.tile, TrackdirToTrack(tf->m_new.td)) != rail_type) {
689 /* Segment must consist from the same rail_type tiles. */
690 segment->end_reason |= ESRB_RAIL_TYPE;
691 return false;
694 /* Avoid infinite looping. */
695 if (tf->m_new == n->GetPos()) {
696 segment->end_reason |= ESRB_INFINITE_LOOP;
697 return false;
700 if (segment->segment_cost > s_max_segment_cost) {
701 /* Potentially in the infinite loop (or only very long segment?). We should
702 * not force it to finish prematurely unless we are on a regular tile. */
703 if (!tf->m_new.in_wormhole() && IsNormalRailTile(tf->m_new.tile)) {
704 segment->end_reason |= ESRB_SEGMENT_TOO_LONG;
705 return false;
709 /* Any other reason bit set? */
710 return segment->end_reason == ESRB_NONE;
713 /** Compute all costs for a newly-allocated (not cached) segment. */
714 template <class TAstar>
715 inline EndSegmentReasonBits CYapfRailBaseT<TAstar>::CalcSegment (Node *n, const CFollowTrackRail *tf)
717 /* Each node cost contains 2 or 3 main components:
718 * 1. Transition cost - cost of the move from previous node (tile):
719 * - curve cost (or zero for straight move)
720 * 2. Tile cost:
721 * - base tile cost
722 * - YAPF_TILE_LENGTH for diagonal tiles
723 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
724 * - tile penalties
725 * - tile slope penalty (upward slopes)
726 * - red signal penalty
727 * - level crossing penalty
728 * - speed-limit penalty (bridges)
729 * - station platform penalty
730 * - penalty for reversing in the depot
731 * - etc.
732 * 3. Extra cost (applies to the last node only)
733 * - last red signal penalty
734 * - penalty for too long or too short platform on the destination station
737 /* Segment: one or more tiles connected by contiguous tracks of the same type.
738 * Each segment cost includes 'Tile cost' for all its tiles (including the first
739 * and last), and the 'Transition cost' between its tiles. The first transition
740 * cost of segment entry (move from the 'parent' node) is not included!
743 /* start at n and walk to the end of segment */
744 NodeData segment;
745 segment.parent_cost = n->m_parent->m_cost;
746 segment.entry_cost = TransitionCost (n->m_parent->GetLastPos(), n->GetPos());
747 segment.segment_cost = 0;
748 segment.extra_cost = 0;
749 segment.pos = n->GetPos();
750 segment.last_signal = RailPathPos();
751 segment.end_reason = ESRB_NONE;
753 TileIndex prev = n->m_parent->GetLastPos().tile;
755 RailType rail_type = !segment.pos.in_wormhole() ? GetRailType(segment.pos.tile, TrackdirToTrack(segment.pos.td)) :
756 IsRailwayTile(segment.pos.wormhole) ? GetBridgeRailType(segment.pos.wormhole) : GetRailType(segment.pos.wormhole);
758 for (;;) {
759 HandleNodeTile (n, tf, &segment, prev);
761 /* Move to the next tile/trackdir. */
762 tf = &tf_local;
763 assert(tf_local.m_veh_owner == m_veh->owner);
764 assert(tf_local.m_railtypes == m_compatible_railtypes);
765 assert(tf_local.m_pPerf == &m_perf_ts_cost);
767 if (!HandleNodeNextTile (n, &tf_local, &segment, rail_type)) break;
769 /* Transition cost (cost of the move from previous tile) */
770 segment.segment_cost += TransitionCost (segment.pos, tf_local.m_new);
772 /* For the next loop set new prev and cur tile info. */
773 prev = segment.pos.tile;
774 segment.pos = tf_local.m_new;
775 } // for (;;)
777 /* Write back the segment information so it can be reused the next time. */
778 n->m_segment->m_last = segment.pos;
779 n->m_segment->m_cost = segment.segment_cost;
780 n->m_segment->m_last_signal = segment.last_signal;
781 n->m_segment->m_end_segment_reason = segment.end_reason & ESRB_CACHED_MASK;
783 /* total node cost */
784 n->m_cost = segment.parent_cost + segment.entry_cost + segment.segment_cost + segment.extra_cost;
785 return segment.end_reason;
788 /** Fill in a node from cached data. */
789 template <class TAstar>
790 inline EndSegmentReasonBits CYapfRailBaseT<TAstar>::RestoreCachedNode (Node *n)
792 /* total node cost */
793 n->m_cost = n->m_parent->m_cost + TransitionCost (n->m_parent->GetLastPos(), n->GetPos()) + n->m_segment->m_cost;
794 /* We will need also some information about the last signal (if it was red). */
795 if (n->m_segment->m_last_signal.is_valid()) {
796 assert(HasSignalAlongPos(n->m_segment->m_last_signal));
797 SignalState sig_state = GetSignalStateByPos(n->m_segment->m_last_signal);
798 bool is_red = (sig_state == SIGNAL_STATE_RED);
799 n->flags.set (n->FLAG_LAST_SIGNAL_WAS_RED, is_red);
800 if (is_red) {
801 n->m_last_red_signal_type = GetSignalType(n->m_segment->m_last_signal);
804 /* No further calculation needed. */
805 return n->m_segment->m_end_segment_reason;
808 /** Add special extra cost when the segment reaches our target. */
809 template <class TAstar>
810 inline void CYapfRailBaseT<TAstar>::AddTargetCost (Node *n, bool is_station)
812 n->flags.set (n->FLAG_TARGET_SEEN);
814 /* Last-red and last-red-exit penalties. */
815 if (n->flags.test (n->FLAG_LAST_SIGNAL_WAS_RED)) {
816 if (n->m_last_red_signal_type == SIGTYPE_EXIT) {
817 /* last signal was red pre-signal-exit */
818 n->m_cost += m_settings->rail_lastred_exit_penalty;
819 } else if (!IsPbsSignal(n->m_last_red_signal_type)) {
820 /* Last signal was red, but not exit or path signal. */
821 n->m_cost += m_settings->rail_lastred_penalty;
825 /* Station platform-length penalty. */
826 if (is_station) {
827 const BaseStation *st = BaseStation::GetByTile(n->GetLastPos().tile);
828 assert(st != NULL);
829 uint platform_length = st->GetPlatformLength(n->GetLastPos().tile, ReverseDiagDir(TrackdirToExitdir(n->GetLastPos().td)));
830 /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
831 n->m_cost -= m_settings->rail_station_penalty * platform_length;
832 /* Add penalty for the inappropriate platform length. */
833 n->m_cost += PlatformLengthPenalty(platform_length);
838 * Find the earliest safe position on a path.
839 * @param node Node to start searching back from (usually the best found node).
840 * @param res Where to store the safe position found.
841 * @return The first node in the path after the initial node.
843 template <class TAstar>
844 inline typename TAstar::Node *CYapfRailBaseT<TAstar>::FindSafePositionOnPath (Node *node, NodePos *res)
846 /* We will never pass more than two signals, no need to check for a safe tile. */
847 assert (node->m_parent != NULL);
848 while (node->m_parent->m_num_signals_passed >= 2) {
849 node = node->m_parent;
850 /* If the parent node has passed at least 2 signals, it
851 * cannot be a root node, because root nodes are single-tile
852 * nodes and can therefore have only one signal, if any. */
853 assert (node->m_parent != NULL);
856 /* Default safe position if no other, earlier one is found. */
857 res->node = node;
858 res->pos = node->GetLastPos();
860 /* Walk through the path back to the origin. */
861 CFollowTrackRail ft (m_veh, Allow90degTurns(), m_compatible_railtypes);
863 for (;;) {
864 Node *parent = node->m_parent;
865 assert (parent != NULL);
867 /* Search node for a safe position. */
868 ft.SetPos (node->GetPos());
869 for (;;) {
870 if (IsSafeWaitingPosition (m_veh, ft.m_new, !Allow90degTurns())) {
871 /* Found a safe position in this node. */
872 res->node = node;
873 res->pos = ft.m_new;
874 break;
877 if (ft.m_new == node->GetLastPos()) break; // no safe position found in node
879 bool follow = ft.FollowNext();
880 assert (follow);
881 assert (ft.m_new.is_single());
884 /* Stop at node after initial node. */
885 if (parent->m_parent == NULL) return node;
886 node = parent;
891 * Try to reserve a single track/platform
892 * @param pos The position to reserve (last tile for platforms)
893 * @param origin If pos is a platform, consider the platform to begin right after this tile
894 * @return Whether reservation succeeded
896 static bool ReserveSingleTrack (const RailPathPos &pos, TileIndex origin = INVALID_TILE)
898 if (pos.in_wormhole() || !IsRailStationTile(pos.tile)) {
899 return TryReserveRailTrack(pos);
902 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos.td)));
903 TileIndex t = pos.tile;
905 do {
906 if (HasStationReservation(t)) {
907 /* Platform could not be reserved, undo. */
908 diff = -diff;
909 while (t != pos.tile) {
910 t = TILE_ADD(t, diff);
911 SetRailStationReservation(t, false);
913 return false;
916 SetRailStationReservation(t, true);
917 MarkTileDirtyByTile(t);
918 t = TILE_ADD(t, diff);
919 } while (IsCompatibleTrainStationTile(t, pos.tile) && t != origin);
921 TriggerStationRandomisation(NULL, pos.tile, SRT_PATH_RESERVATION);
923 return true;
927 * Unreserve a single track/platform
928 * @param pos The position to reserve (last tile for platforms)
929 * @param origin If pos is a platform, consider the platform to begin right after this tile
931 static void UnreserveSingleTrack (const RailPathPos &pos, TileIndex origin = INVALID_TILE)
933 if (pos.in_wormhole() || !IsRailStationTile(pos.tile)) {
934 UnreserveRailTrack(pos);
935 return;
938 TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(pos.td)));
939 TileIndex t = pos.tile;
940 while (IsCompatibleTrainStationTile(t, pos.tile) && t != origin) {
941 assert(HasStationReservation(t));
942 SetRailStationReservation(t, false);
943 t = TILE_ADD(t, diff);
948 * Try to reserve the path up to a given position.
949 * @param origin Starting tile for the reservation.
950 * @param res Target tile for the reservation.
951 * @return Whether reservation succeeded.
953 template <class TAstar>
954 bool CYapfRailBaseT<TAstar>::TryReservePath (TileIndex origin, const NodePos *res)
956 /* Don't bother if the target is reserved. */
957 if (!IsWaitingPositionFree(m_veh, res->pos)) return false;
959 CFollowTrackRail ft (m_veh, Allow90degTurns(), m_compatible_railtypes);
961 for (Node *node = res->node; node->m_parent != NULL; node = node->m_parent) {
962 ft.SetPos (node->GetPos());
963 for (;;) {
964 if (!ReserveSingleTrack (ft.m_new, origin)) {
965 /* Reservation failed, undo. */
966 Node *failed_node = node;
967 RailPathPos res_fail = ft.m_new;
968 for (node = res->node; node != failed_node; node = node->m_parent) {
969 ft.SetPos (node->GetPos());
970 for (;;) {
971 UnreserveSingleTrack (ft.m_new, origin);
972 if (ft.m_new == res->pos) break;
973 if (ft.m_new == node->GetLastPos()) break;
974 ft.FollowNext();
977 ft.SetPos (failed_node->GetPos());
978 while (ft.m_new != res_fail) {
979 assert (ft.m_new != res->pos);
980 assert (ft.m_new != node->GetLastPos());
981 UnreserveSingleTrack (ft.m_new, origin);
982 ft.FollowNext();
984 return false;
987 if (ft.m_new == res->pos) break;
988 if (ft.m_new == node->GetLastPos()) break;
989 bool follow = ft.FollowNext();
990 assert (follow);
991 assert (ft.m_new.is_single());
995 if (CanUseGlobalCache(*res->node)) {
996 YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
999 return true;
1003 template <class Tpf, class TAstar, bool Tmask_reserved_tracks>
1004 class CYapfRailT : public CYapfRailBaseT <TAstar>
1006 public:
1007 typedef CYapfRailBaseT <TAstar> Base;
1008 typedef typename TAstar::Node Node; ///< this will be our node type
1009 typedef typename Node::Key Key; ///< key to hash tables
1010 typedef typename Node::CachedData CachedData;
1011 typedef typename CachedData::Key CacheKey;
1012 typedef CSegmentCostCacheT<CachedData> Cache;
1013 typedef SmallArray<CachedData> LocalCache;
1015 protected:
1016 CYapfRailT (const Train *v, bool allow_90deg, bool override_rail_type = false)
1017 : Base (v, allow_90deg, override_rail_type, Tmask_reserved_tracks)
1021 /** to access inherited path finder */
1022 Tpf& Yapf()
1024 return *static_cast<Tpf*>(this);
1027 public:
1028 /** Called by the A-star underlying class to find the neighbours of a node. */
1029 inline void Follow (Node *old_node)
1031 if (!Base::tf.Follow(old_node->GetLastPos())) return;
1032 if (Tmask_reserved_tracks && !Base::tf.MaskReservedTracks()) return;
1034 bool is_choice = !Base::tf.m_new.is_single();
1035 RailPathPos pos = Base::tf.m_new;
1036 for (TrackdirBits rtds = Base::tf.m_new.trackdirs; rtds != TRACKDIR_BIT_NONE; rtds = KillFirstBit(rtds)) {
1037 pos.set_trackdir (FindFirstTrackdir(rtds));
1038 Node *n = TAstar::CreateNewNode(old_node, pos, is_choice);
1040 /* evaluate the node */
1041 bool cached = Base::AttachSegmentToNode(n);
1042 if (!cached) {
1043 Yapf().m_stats_cost_calcs++;
1044 } else {
1045 Yapf().m_stats_cache_hits++;
1048 CPerfStart perf_cost(Yapf().m_perf_cost);
1050 EndSegmentReasonBits end_reason;
1051 if (cached) {
1052 end_reason = Base::RestoreCachedNode (n);
1053 } else {
1054 end_reason = Base::CalcSegment (n, &this->tf);
1057 assert (((end_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) || !Yapf().PfDetectDestination(n->GetLastPos()));
1059 if (((end_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) &&
1060 Yapf().PfDetectDestination(n->GetLastPos())) {
1061 /* Special costs for the case we have reached our target. */
1062 Base::AddTargetCost (n, (end_reason & ESRB_STATION) != ESRB_NONE);
1063 perf_cost.Stop();
1064 n->m_estimate = n->m_cost;
1065 this->FoundTarget(n);
1067 } else if ((end_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) {
1068 /* Reason to not continue. Stop this PF branch. */
1069 continue;
1071 } else {
1072 perf_cost.Stop();
1073 Yapf().PfCalcEstimate(*n);
1074 this->InsertNode(n);
1079 /** call the node follower */
1080 static inline void Follow (Tpf *pf, Node *n)
1082 pf->Follow(n);
1086 * Main pathfinder routine:
1087 * - set startup node(s)
1088 * - main loop that stops if:
1089 * - the destination was found
1090 * - or the open list is empty (no route to destination).
1091 * - or the maximum amount of loops reached - m_max_search_nodes (default = 10000)
1092 * @return true if the path was found
1094 inline bool FindPath (void)
1096 #ifndef NO_DEBUG_MESSAGES
1097 CPerformanceTimer perf;
1098 perf.Start();
1099 #endif /* !NO_DEBUG_MESSAGES */
1101 bool bDestFound = TAstar::FindPath (Follow, Base::m_settings->max_search_nodes);
1103 #ifndef NO_DEBUG_MESSAGES
1104 perf.Stop();
1105 if (_debug_yapf_level >= 2) {
1106 int t = perf.Get(1000000);
1107 _total_pf_time_us += t;
1109 if (_debug_yapf_level >= 3) {
1110 UnitID veh_idx = (Base::m_veh != NULL) ? Base::m_veh->unitnumber : 0;
1111 float cache_hit_ratio = (Base::m_stats_cache_hits == 0) ? 0.0f : ((float)Base::m_stats_cache_hits / (float)(Base::m_stats_cache_hits + Base::m_stats_cost_calcs) * 100.0f);
1112 int cost = bDestFound ? TAstar::best->m_cost : -1;
1113 int dist = bDestFound ? TAstar::best->m_estimate - TAstar::best->m_cost : -1;
1115 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) -- ",
1116 bDestFound ? '-' : '!', veh_idx, t, TAstar::num_steps, TAstar::OpenCount(), TAstar::ClosedCount(),
1117 cache_hit_ratio, cost, dist, Base::m_perf_cost.Get(1000000), Base::m_perf_slope_cost.Get(1000000),
1118 Base::m_perf_ts_cost.Get(1000000), Base::m_perf_other_cost.Get(1000000)
1122 #endif /* !NO_DEBUG_MESSAGES */
1123 return bDestFound;
1128 struct CYapfRail
1129 : CYapfRailT <CYapfRail, AstarRailTrackDir, false>
1131 public:
1132 typedef CYapfRailT <CYapfRail, AstarRailTrackDir, false> Base;
1133 typedef AstarRailTrackDir TAstar;
1134 typedef TAstar::Node Node;
1136 protected:
1137 TileIndex m_dest_tile;
1138 StationID m_dest_station_id;
1140 public:
1141 CYapfRail (const Train *v, bool allow_90deg)
1142 : Base (v, allow_90deg)
1144 switch (v->current_order.GetType()) {
1145 case OT_GOTO_WAYPOINT:
1146 if (!Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) {
1147 /* In case of 'complex' waypoints we need to do a look
1148 * ahead. This look ahead messes a bit about, which
1149 * means that it 'corrupts' the cache. To prevent this
1150 * we disable caching when we're looking for a complex
1151 * waypoint. */
1152 Base::DisableCache(true);
1154 /* FALL THROUGH */
1155 case OT_GOTO_STATION:
1156 m_dest_station_id = v->current_order.GetDestination();
1157 m_dest_tile = BaseStation::Get(m_dest_station_id)->GetClosestTile(v->tile, v->current_order.IsType(OT_GOTO_STATION) ? STATION_RAIL : STATION_WAYPOINT);
1158 break;
1160 default:
1161 m_dest_station_id = INVALID_STATION;
1162 m_dest_tile = v->dest_tile;
1163 break;
1167 /** Called by YAPF to detect if node ends in the desired destination */
1168 inline bool PfDetectDestination (const RailPathPos &pos) const
1170 if (m_dest_station_id != INVALID_STATION) {
1171 return !pos.in_wormhole() && HasStationTileRail(pos.tile)
1172 && (GetStationIndex(pos.tile) == m_dest_station_id)
1173 && (GetRailStationTrack(pos.tile) == TrackdirToTrack(pos.td));
1174 } else {
1175 return !pos.in_wormhole() && (pos.tile == m_dest_tile);
1180 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
1181 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate
1183 inline void PfCalcEstimate (Node &n) const
1185 static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
1186 static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
1188 TileIndex tile = n.GetLastPos().tile;
1189 DiagDirection exitdir = TrackdirToExitdir(n.GetLastPos().td);
1190 int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
1191 int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
1192 int x2 = 2 * TileX(m_dest_tile);
1193 int y2 = 2 * TileY(m_dest_tile);
1194 int dx = abs(x1 - x2);
1195 int dy = abs(y1 - y2);
1196 int dmin = min(dx, dy);
1197 int dxy = abs(dx - dy);
1198 int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
1199 n.m_estimate = n.m_cost + d;
1200 assert(n.m_estimate >= n.m_parent->m_estimate);
1203 inline Trackdir ChooseRailTrack(const RailPathPos &origin, bool reserve_track, PFResult *target)
1205 if (target != NULL) target->pos.tile = INVALID_TILE;
1207 /* set origin and destination nodes */
1208 Base::SetOrigin(origin);
1210 /* find the best path */
1211 bool path_found = Base::FindPath();
1213 /* if path not found - return INVALID_TRACKDIR */
1214 Trackdir next_trackdir = INVALID_TRACKDIR;
1215 Node *pNode = Base::GetBestNode();
1216 if (pNode != NULL) {
1217 if (reserve_track && path_found) {
1218 typename Base::NodePos res;
1219 Node *best_next_node = Base::FindSafePositionOnPath (pNode, &res);
1220 if (target != NULL) target->pos = res.pos;
1221 /* return trackdir from the best origin node (one of start nodes) */
1222 next_trackdir = best_next_node->GetPos().td;
1224 assert (best_next_node->m_parent->GetPos() == origin);
1225 assert (best_next_node->m_parent->GetLastPos() == origin);
1226 bool okay = Base::TryReservePath (origin.tile, &res);
1227 if (target != NULL) target->okay = okay;
1228 } else {
1229 while (pNode->m_parent->m_parent != NULL) {
1230 pNode = pNode->m_parent;
1232 assert (pNode->m_parent->GetPos() == origin);
1233 assert (pNode->m_parent->GetLastPos() == origin);
1234 /* return trackdir from the best origin node (one of start nodes) */
1235 next_trackdir = pNode->GetPos().td;
1239 /* Treat the path as found if stopped on the first two way signal(s). */
1240 if (target != NULL) target->found = path_found | Base::m_stopped_on_first_two_way_signal;
1241 return next_trackdir;
1244 inline bool CheckReverseTrain(const RailPathPos &pos1, const RailPathPos &pos2, int reverse_penalty)
1246 /* create pathfinder instance
1247 * set origin and destination nodes */
1248 Base::SetOrigin(pos1, pos2, reverse_penalty, false);
1250 /* find the best path */
1251 bool bFound = Base::FindPath();
1253 if (!bFound) return false;
1255 /* path was found
1256 * walk through the path back to the origin */
1257 Node *pNode = Base::GetBestNode();
1258 while (pNode->m_parent != NULL) {
1259 pNode = pNode->m_parent;
1262 /* check if it was reversed origin */
1263 Node& best_org_node = *pNode;
1264 bool reversed = (best_org_node.m_cost != 0);
1265 return reversed;
1269 Trackdir YapfTrainChooseTrack(const Train *v, const RailPathPos &origin, bool reserve_track, PFResult *target)
1271 /* create pathfinder instance */
1272 CYapfRail pf1 (v, !_settings_game.pf.forbid_90_deg);
1273 #if !DEBUG_YAPF_CACHE
1274 Trackdir result1 = pf1.ChooseRailTrack(origin, reserve_track, target);
1276 #else
1277 Trackdir result1 = pf1.ChooseRailTrack(origin, false, NULL);
1278 CYapfRail pf2 (v, !_settings_game.pf.forbid_90_deg);
1279 pf2.DisableCache(true);
1280 Trackdir result2 = pf2.ChooseRailTrack(origin, reserve_track, target);
1281 if (result1 != result2) {
1282 DEBUG(yapf, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1, result2);
1283 DumpState(pf1, pf2);
1285 #endif
1287 return result1;
1290 bool YapfTrainCheckReverse(const Train *v)
1292 const Train *last_veh = v->Last();
1294 /* tiles where front and back are */
1295 RailPathPos pos = v->GetPos();
1296 RailPathPos rev = last_veh->GetReversePos();
1298 int reverse_penalty = 0;
1300 if (pos.in_wormhole()) {
1301 /* front in tunnel / on bridge */
1302 assert(TrackdirToExitdir(pos.td) == ReverseDiagDir(GetTunnelBridgeDirection(pos.wormhole)));
1304 /* Current position of the train in the wormhole */
1305 TileIndex cur_tile = TileVirtXY(v->x_pos, v->y_pos);
1307 /* Add distance to drive in the wormhole as penalty for the forward path, i.e. bonus for the reverse path
1308 * Note: Negative penalties are ok for the start tile. */
1309 reverse_penalty -= DistanceManhattan(cur_tile, pos.tile) * YAPF_TILE_LENGTH;
1312 if (rev.in_wormhole()) {
1313 /* back in tunnel / on bridge */
1314 assert(TrackdirToExitdir(rev.td) == ReverseDiagDir(GetTunnelBridgeDirection(rev.wormhole)));
1316 /* Current position of the last wagon in the wormhole */
1317 TileIndex cur_tile = TileVirtXY(last_veh->x_pos, last_veh->y_pos);
1319 /* Add distance to drive in the wormhole as penalty for the revere path. */
1320 reverse_penalty += DistanceManhattan(cur_tile, rev.tile) * YAPF_TILE_LENGTH;
1323 /* slightly hackish: If the pathfinders finds a path, the cost of the first node is tested to distinguish between forward- and reverse-path. */
1324 if (reverse_penalty == 0) reverse_penalty = 1;
1326 CYapfRail pf1 (v, !_settings_game.pf.forbid_90_deg);
1327 bool result1 = pf1.CheckReverseTrain(pos, rev, reverse_penalty);
1329 #if DEBUG_YAPF_CACHE
1330 CYapfRail pf2 (v, !_settings_game.pf.forbid_90_deg);
1331 pf2.DisableCache(true);
1332 bool result2 = pf2.CheckReverseTrain(pos, rev, reverse_penalty);
1333 if (result1 != result2) {
1334 DEBUG(yapf, 0, "CACHE ERROR: CheckReverseTrain() = [%s, %s]", result1 ? "T" : "F", result2 ? "T" : "F");
1335 DumpState(pf1, pf2);
1337 #endif
1339 return result1;
1343 struct CYapfAnyDepotRail
1344 : CYapfRailT <CYapfAnyDepotRail, AstarRailTrackDir, false>
1346 public:
1347 typedef CYapfRailT <CYapfAnyDepotRail, AstarRailTrackDir, false> Base;
1348 typedef AstarRailTrackDir TAstar;
1349 typedef TAstar::Node Node;
1351 CYapfAnyDepotRail (const Train *v, bool allow_90deg)
1352 : Base (v, allow_90deg)
1356 /** Called by YAPF to detect if node ends in the desired destination */
1357 inline bool PfDetectDestination (const RailPathPos &pos) const
1359 return !pos.in_wormhole() && IsRailDepotTile(pos.tile);
1362 /** Called by YAPF to calculate cost estimate. */
1363 inline void PfCalcEstimate (Node &n) const
1365 n.m_estimate = n.m_cost;
1368 inline bool FindNearestDepotTwoWay(const RailPathPos &pos1, const RailPathPos &pos2, int max_penalty, int reverse_penalty, TileIndex *depot_tile, bool *reversed)
1370 /* set origin and destination nodes */
1371 Base::SetOrigin (pos1, pos2, reverse_penalty, true);
1372 Base::SetMaxCost(max_penalty);
1374 /* find the best path */
1375 bool bFound = Base::FindPath();
1376 if (!bFound) return false;
1378 /* some path found
1379 * get found depot tile */
1380 Node *n = Base::GetBestNode();
1381 *depot_tile = n->GetLastPos().tile;
1383 /* walk through the path back to the origin */
1384 while (n->m_parent != NULL) {
1385 n = n->m_parent;
1388 /* if the origin node is our front vehicle tile/Trackdir then we didn't reverse
1389 * but we can also look at the cost (== 0 -> not reversed, == reverse_penalty -> reversed) */
1390 *reversed = (n->m_cost != 0);
1392 return true;
1396 bool YapfTrainFindNearestDepot(const Train *v, uint max_penalty, FindDepotData *res)
1398 RailPathPos origin;
1399 FollowTrainReservation(v, &origin);
1400 RailPathPos rev = v->Last()->GetReversePos();
1402 CYapfAnyDepotRail pf1 (v, !_settings_game.pf.forbid_90_deg);
1404 * With caching enabled it simply cannot get a reliable result when you
1405 * have limited the distance a train may travel. This means that the
1406 * cached result does not match uncached result in all cases and that
1407 * causes desyncs. So disable caching when finding for a depot that is
1408 * nearby. This only happens with automatic servicing of vehicles,
1409 * so it will only impact performance when you do not manually set
1410 * depot orders and you do not disable automatic servicing.
1412 if (max_penalty != 0) pf1.DisableCache(true);
1413 bool result1 = pf1.FindNearestDepotTwoWay (origin, rev, max_penalty, YAPF_INFINITE_PENALTY, &res->tile, &res->reverse);
1415 #if DEBUG_YAPF_CACHE
1416 CYapfAnyDepotRail pf2 (v, !_settings_game.pf.forbid_90_deg);
1417 TileIndex depot_tile2 = INVALID_TILE;
1418 bool reversed2 = false;
1419 pf2.DisableCache(true);
1420 bool result2 = pf2.FindNearestDepotTwoWay (origin, rev, max_penalty, YAPF_INFINITE_PENALTY, &depot_tile2, &reversed2);
1421 if (result1 != result2 || (result1 && (res->tile != depot_tile2 || res->reverse != reversed2))) {
1422 DEBUG(yapf, 0, "CACHE ERROR: FindNearestDepotTwoWay() = [%s, %s]", result1 ? "T" : "F", result2 ? "T" : "F");
1423 DumpState(pf1, pf2);
1425 #endif
1427 return result1;
1431 struct CYapfAnySafeTileRail
1432 : CYapfRailT <CYapfAnySafeTileRail, AstarRailTrackDir, true>
1434 public:
1435 typedef CYapfRailT <CYapfAnySafeTileRail, AstarRailTrackDir, true> Base;
1436 typedef AstarRailTrackDir TAstar;
1437 typedef TAstar::Node Node;
1439 CYapfAnySafeTileRail (const Train *v, bool allow_90deg, bool override_railtype)
1440 : Base (v, allow_90deg, override_railtype)
1444 /** Called by YAPF to detect if node ends in the desired destination */
1445 inline bool PfDetectDestination (const RailPathPos &pos) const
1447 return IsFreeSafeWaitingPosition(Base::m_veh, pos, Base::Allow90degTurns());
1450 /** Called by YAPF to calculate cost estimate. */
1451 inline void PfCalcEstimate (Node &n) const
1453 n.m_estimate = n.m_cost;
1456 bool FindNearestSafeTile(const RailPathPos &pos, bool override_railtype, bool dont_reserve)
1458 /* Set origin and destination. */
1459 Base::SetOrigin(pos);
1461 bool bFound = Base::FindPath();
1462 if (!bFound) return false;
1464 if (dont_reserve) return true;
1466 /* Found a destination, search for a reservation target. */
1467 Node *pNode = Base::GetBestNode();
1468 typename Base::NodePos res;
1469 pNode = Base::FindSafePositionOnPath(pNode, &res)->m_parent;
1470 assert (pNode->GetPos() == pos);
1471 assert (pNode->GetLastPos() == pos);
1473 return Base::TryReservePath (pos.tile, &res);
1477 bool YapfTrainFindNearestSafeTile(const Train *v, const RailPathPos &pos, bool override_railtype)
1479 /* Create pathfinder instance */
1480 CYapfAnySafeTileRail pf1 (v, !_settings_game.pf.forbid_90_deg, override_railtype);
1481 #if !DEBUG_YAPF_CACHE
1482 bool result1 = pf1.FindNearestSafeTile(pos, override_railtype, false);
1484 #else
1485 bool result2 = pf1.FindNearestSafeTile(pos, override_railtype, true);
1486 CYapfAnySafeTileRail pf2 (v, !_settings_game.pf.forbid_90_deg, override_railtype);
1487 pf2.DisableCache(true);
1488 bool result1 = pf2.FindNearestSafeTile(pos, override_railtype, false);
1489 if (result1 != result2) {
1490 DEBUG(yapf, 0, "CACHE ERROR: FindSafeTile() = [%s, %s]", result2 ? "T" : "F", result1 ? "T" : "F");
1491 DumpState(pf1, pf2);
1493 #endif
1495 return result1;