Flatten CYapfReserveTrack::FindSafePositionOnNode
[openttd/fttd.git] / src / pathfinder / yapf / yapf_rail.cpp
blobb7bd351ae1a1678cbf373b2d40738705cbd01962
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 Tsegment& Get(Key& key, bool *found)
107 Tsegment *item = m_map.Find(key);
108 if (item == NULL) {
109 *found = false;
110 item = new (m_heap.Append()) Tsegment(key);
111 m_map.Push(*item);
112 } else {
113 *found = true;
115 return *item;
120 template <class Types>
121 class CYapfRailT : public Types::Astar
123 public:
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;
133 protected:
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)
146 int m_max_cost;
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
160 public:
161 bool m_stopped_on_first_two_way_signal;
163 protected:
164 static const int s_max_segment_cost = 10000;
166 CYapfRailT()
167 : m_settings(&_settings_game.pf.yapf)
168 , m_veh(NULL)
169 , m_max_cost(0)
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 */
187 Tpf& Yapf()
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;
196 static Cache C;
198 /* some statistics */
199 if (last_date != _date) {
200 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;
208 C.Flush();
211 return C;
214 public:
215 /** return current settings (can be custom - company based - but later) */
216 inline const YAPFSettings& PfGetSettings() const
218 return *m_settings;
221 const Train * GetVehicle() const
223 return m_veh;
226 /** set origin */
227 void SetOrigin(const PathPos &pos, const PathPos &rev = PathPos(), int reverse_penalty = 0, bool treat_first_red_two_way_signal_as_eol = true)
229 m_org = pos;
230 m_rev = rev;
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);
239 node->m_cost = cost;
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;
261 struct NodeData {
262 int parent_cost;
263 int entry_cost;
264 int segment_cost;
265 int extra_cost;
266 PathPos pos;
267 PathPos last_signal;
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;
280 bool uphill;
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);
289 } else {
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));
308 return 0;
309 } else {
310 return (pos1.td == pos2.td) ? 0 : Yapf().PfGetSettings().rail_curve45_penalty;
312 } else {
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 */
321 int cost = 0;
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;
336 return cost;
339 /** Return one tile cost (base cost + level crossing penalty). */
340 inline int OneTileCost(const PathPos &pos)
342 int cost = 0;
343 /* set base cost */
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;
350 } else {
351 /* non-diagonal trackdir */
352 cost = YAPF_TILE_CORNER_LENGTH;
354 return cost;
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;
364 return false;
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);
380 return 0;
383 int SignalCost(Node& n, const PathPos &pos, NodeData *data)
385 int cost = 0;
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) {
398 /* green signal */
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;
405 } else {
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;
413 return -1;
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) {
426 switch (sig_type) {
427 case SIGTYPE_COMBO:
428 case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
429 case SIGTYPE_NORMAL:
430 case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
431 default: 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;
442 } else {
443 cost += n.m_num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
447 return cost;
450 inline int PlatformLengthPenalty(int platform_length)
452 int cost = 0;
453 const Train *v = Yapf().GetVehicle();
454 assert(v != NULL);
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;
465 return cost;
468 public:
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);
491 for (;;) {
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;
498 /* Slope cost. */
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);
527 bool add_extra_cost;
528 for (;;) {
529 if (!ft.FollowNext()) {
530 /* end of line */
531 add_extra_cost = !IsWaitingPositionFree(v, ft.m_old, _settings_game.pf.forbid_90_deg);
532 break;
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;
541 break;
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;
548 break;
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())
580 int min_speed = 0;
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. */
598 tf = &tf_local;
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;
608 } else {
609 segment->end_reason |= ESRB_DEAD_END;
612 if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingPos(segment->pos)) {
613 segment->end_reason |= ESRB_SAFE_TILE;
615 break;
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;
622 break;
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;
645 break;
648 /* Avoid infinite looping. */
649 if (tf_local.m_new == n.GetPos()) {
650 segment->end_reason |= ESRB_INFINITE_LOOP;
651 break;
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;
659 break;
663 /* Any other reason bit set? */
664 if (segment->end_reason != ESRB_NONE) {
665 break;
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;
674 } // for (;;)
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)
699 * 2. Tile cost:
700 * - base tile cost
701 * - YAPF_TILE_LENGTH for diagonal tiles
702 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
703 * - tile penalties
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
710 * - etc.
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;
739 if (is_red) {
740 n.m_last_red_signal_type = GetSignalType(segment.m_last_signal);
743 /* No further calculation needed. */
744 } else {
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. */
760 target_seen = true;
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. */
767 return false;
770 /* Special costs for the case we have reached our target. */
771 if (target_seen) {
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);
787 assert(st != NULL);
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;
799 return true;
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)
826 n.m_segment = &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));
846 return false;
848 bool found;
849 CachedData& item = m_global_cache.Get(key, &found);
850 ConnectNodeToCachedData(n, item);
851 return found;
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);
874 if (!bCached) {
875 Yapf().m_stats_cost_calcs++;
876 } else {
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? */
883 if (!bValid) return;
885 /* detect the destination */
886 if (Yapf().PfDetectDestination(n)) {
887 this->FoundTarget(&n);
888 } else {
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)
910 m_veh = v;
912 #ifndef NO_DEBUG_MESSAGES
913 CPerformanceTimer perf;
914 perf.Start();
915 #endif /* !NO_DEBUG_MESSAGES */
917 Yapf().PfSetStartupNodes();
918 bool bDestFound = Types::Astar::FindPath (Follow, PfGetSettings().max_search_nodes);
920 #ifndef NO_DEBUG_MESSAGES
921 perf.Stop();
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 */
940 return bDestFound;
945 class CYapfDestinationRailBase
947 protected:
948 RailTypes m_compatible_railtypes;
950 public:
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
972 public:
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 */
978 Tpf& Yapf()
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;
1002 return true;
1006 template <class Types>
1007 class CYapfDestinationAnySafeTileRailT
1008 : public CYapfDestinationRailBase
1010 public:
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 */
1017 Tpf& Yapf()
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;
1041 return true;
1045 template <class Types>
1046 class CYapfDestinationTileOrStationRailT
1047 : public CYapfDestinationRailBase
1049 public:
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
1054 protected:
1055 TileIndex m_destTile;
1056 StationID m_dest_station_id;
1058 /** to access inherited path finder */
1059 Tpf& Yapf()
1061 return *static_cast<Tpf*>(this);
1064 public:
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
1074 * waypoint. */
1075 Yapf().DisableCache(true);
1077 /* FALL THROUGH */
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);
1081 break;
1083 default:
1084 m_destTile = v->dest_tile;
1085 m_dest_station_id = INVALID_STATION;
1086 break;
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)
1100 bool bDest;
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));
1105 } else {
1106 bDest = !pos.in_wormhole() && (pos.tile == m_destTile);
1108 return bDest;
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;
1121 return true;
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);
1137 return true;
1142 template <class Types>
1143 class CYapfReserveTrack
1145 public:
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
1150 protected:
1151 /** to access inherited pathfinder */
1152 inline Tpf& Yapf()
1154 return *static_cast<Tpf*>(this);
1157 private:
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;
1169 do {
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);
1177 *fail = pos;
1178 return 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);
1186 } else {
1187 if (!TryReserveRailTrack(pos)) {
1188 /* Tile couldn't be reserved, undo. */
1189 *fail = pos;
1190 return false;
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);
1210 } else {
1211 UnreserveRailTrack(pos);
1214 return pos != m_res_dest;
1217 public:
1218 /** Set the target to where the reservation should be extended. */
1219 inline void SetReservationTarget(Node *node, const PathPos &pos)
1221 m_res_node = node;
1222 m_res_dest = 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();
1240 assert (follow);
1241 assert (ft.m_new.is_single());
1244 m_res_dest = ft.m_new;
1245 m_res_node = node;
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;
1258 PathPos res_fail;
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);
1269 return false;
1273 if (Yapf().CanUseGlobalCache(*m_res_node)) {
1274 YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
1277 return true;
1281 template <class Types>
1282 class CYapfFollowAnyDepotRailT
1284 public:
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
1290 protected:
1291 /** to access inherited path finder */
1292 inline Tpf& Yapf()
1294 return *static_cast<Tpf*>(this);
1297 public:
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)
1313 Tpf pf1;
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
1327 Tpf pf2;
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);
1336 #endif
1338 return result1;
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;
1352 /* some path found
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 */
1358 Node *pNode = n;
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);
1367 return true;
1371 template <class Types>
1372 class CYapfFollowAnySafeTileRailT : public CYapfReserveTrack<Types>
1374 public:
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
1380 protected:
1381 /** to access inherited path finder */
1382 inline Tpf& Yapf()
1384 return *static_cast<Tpf*>(this);
1387 public:
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 */
1404 Tpf pf1;
1405 #if !DEBUG_YAPF_CACHE
1406 bool result1 = pf1.FindNearestSafeTile(v, pos, override_railtype, false);
1408 #else
1409 bool result2 = pf1.FindNearestSafeTile(v, pos, override_railtype, true);
1410 Tpf pf2;
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);
1417 #endif
1419 return result1;
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. */
1436 Node *pPrev = NULL;
1437 while (pNode->m_parent != NULL) {
1438 pPrev = pNode;
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>
1451 public:
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
1457 protected:
1458 /** to access inherited path finder */
1459 inline Tpf& Yapf()
1461 return *static_cast<Tpf*>(this);
1464 public:
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 */
1481 Tpf pf1;
1482 #if !DEBUG_YAPF_CACHE
1483 Trackdir result1 = pf1.ChooseRailTrack(v, origin, reserve_track, target);
1485 #else
1486 Trackdir result1 = pf1.ChooseRailTrack(v, origin, false, NULL);
1487 Tpf pf2;
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);
1494 #endif
1496 return result1;
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 */
1519 Node *pPrev = NULL;
1520 while (pNode->m_parent != NULL) {
1521 pPrev = pNode;
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)
1543 Tpf pf1;
1544 bool result1 = pf1.CheckReverseTrain(v, pos1, pos2, reverse_penalty);
1546 #if DEBUG_YAPF_CACHE
1547 Tpf pf2;
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);
1554 #endif
1556 return result1;
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;
1571 /* path was found
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);
1581 return reversed;
1585 template <class Tpf_, class Ttrack_follower>
1586 struct CYapfRail_TypesT
1588 typedef CYapfRail_TypesT<Tpf_, Ttrack_follower> Types;
1590 typedef Tpf_ Tpf;
1591 typedef Ttrack_follower TrackFollower;
1592 typedef AstarRailTrackDir Astar;
1595 struct CYapfRail1
1596 : CYapfRailT<CYapfRail_TypesT<CYapfRail1, CFollowTrackRail90> >
1597 , CYapfDestinationTileOrStationRailT<CYapfRail_TypesT<CYapfRail1, CFollowTrackRail90> >
1598 , CYapfFollowRailT<CYapfRail_TypesT<CYapfRail1, CFollowTrackRail90> >
1602 struct CYapfRail2
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);
1698 return reverse;
1701 bool YapfTrainFindNearestDepot(const Train *v, uint max_penalty, FindDepotData *res)
1703 PathPos origin;
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);