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