OpenTTD Source 20260421-master-gc2fbc6fdeb
yapf_costrail.hpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#ifndef YAPF_COSTRAIL_HPP
11#define YAPF_COSTRAIL_HPP
12
13
14#include "../../pbs.h"
15#include "../follow_track.hpp"
16#include "../pathfinder_type.h"
17#include "yapf_type.hpp"
18#include "yapf_costbase.hpp"
19
20template <class Types>
21class CYapfCostRailT : public CYapfCostBase {
22public:
23 typedef typename Types::Tpf Tpf;
24 typedef typename Types::TrackFollower TrackFollower;
25 typedef typename Types::NodeList::Item Node;
26 typedef typename Node::Key Key;
27 typedef typename Node::CachedData CachedData;
28
29protected:
30
32 struct TILE {
33 TileIndex tile;
34 Trackdir td;
35 TileType tile_type;
36 RailType rail_type;
37
38 TILE() : tile(INVALID_TILE), td(INVALID_TRACKDIR), tile_type(TileType::Void), rail_type(INVALID_RAILTYPE) { }
39
40 TILE(TileIndex tile, Trackdir td) : tile(tile), td(td), tile_type(GetTileType(tile)), rail_type(GetTileRailType(tile)) { }
41 };
42
43protected:
48 int max_cost = 0;
49 bool disable_cache = false;
50 std::vector<int> sig_look_ahead_costs = {};
51 bool treat_first_red_two_way_signal_as_eol = false;
52
53public:
54 bool stopped_on_first_two_way_signal = false;
55
56protected:
57 static constexpr int MAX_SEGMENT_COST = 10000;
58
60 {
61 /* pre-compute look-ahead penalties into array */
62 int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0;
63 int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1;
64 int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2;
65 this->sig_look_ahead_costs.clear();
66 this->sig_look_ahead_costs.reserve(Yapf().PfGetSettings().rail_look_ahead_max_signals);
67 for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) {
68 this->sig_look_ahead_costs.push_back(p0 + i * (p1 + i * p2));
69 }
70 }
71
74 {
75 return *static_cast<Tpf *>(this);
76 }
77
78public:
84 {
85 this->treat_first_red_two_way_signal_as_eol = enabled;
86 }
87
93 {
94 return Yapf().PfGetSettings().rail_firstred_twoway_eol && this->treat_first_red_two_way_signal_as_eol;
95 }
96
97 inline int SlopeCost(TileIndex tile, Trackdir td)
98 {
99 if (!stSlopeCost(tile, td)) return 0;
100 return Yapf().PfGetSettings().rail_slope_penalty;
101 }
102
103 inline int CurveCost(Trackdir td1, Trackdir td2)
104 {
105 assert(IsValidTrackdir(td1));
106 assert(IsValidTrackdir(td2));
107 int cost = 0;
108 if (TrackFollower::Allow90degTurns()
110 /* 90-deg curve penalty */
111 cost += Yapf().PfGetSettings().rail_curve90_penalty;
112 } else if (td2 != NextTrackdir(td1)) {
113 /* 45-deg curve penalty */
114 cost += Yapf().PfGetSettings().rail_curve45_penalty;
115 }
116 return cost;
117 }
118
119 inline int SwitchCost(TileIndex tile1, TileIndex tile2, DiagDirection exitdir)
120 {
121 if (IsPlainRailTile(tile1) && IsPlainRailTile(tile2)) {
123 bool t2 = KillFirstBit(GetTrackBits(tile2) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE;
124 if (t1 && t2) return Yapf().PfGetSettings().rail_doubleslip_penalty;
125 }
126 return 0;
127 }
128
135 inline int OneTileCost(TileIndex tile, Trackdir trackdir)
136 {
137 int cost = 0;
138 /* set base cost */
139 if (IsDiagonalTrackdir(trackdir)) {
140 cost += YAPF_TILE_LENGTH;
141 switch (GetTileType(tile)) {
142 case TileType::Road:
143 /* Increase the cost for level crossings */
144 if (IsLevelCrossing(tile)) {
145 cost += Yapf().PfGetSettings().rail_crossing_penalty;
146 }
147 break;
148
149 default:
150 break;
151 }
152 } else {
153 /* non-diagonal trackdir */
155 }
156 return cost;
157 }
158
166 inline bool IsAnyStationTileReserved(TileIndex tile, Trackdir trackdir, int distance)
167 {
169 for (; distance >= 0; distance--, tile += diff) {
170 if (HasStationReservation(tile)) return true;
171 }
172 return false;
173 }
174
183 inline int ReservationCost(Node &n, TileIndex tile, Trackdir trackdir, int skipped)
184 {
185 if (n.num_signals_passed >= this->sig_look_ahead_costs.size() / 2) return 0;
186 if (!IsPbsSignal(n.last_signal_type)) return 0;
187
188 if (IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) {
189 return Yapf().PfGetSettings().rail_pbs_station_penalty * (skipped + 1);
190 } else if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) {
191 int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty;
192 if (!IsDiagonalTrackdir(trackdir)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH;
193 return cost * (skipped + 1);
194 }
195 return 0;
196 }
197
198 int SignalCost(Node &n, TileIndex tile, Trackdir trackdir)
199 {
200 int cost = 0;
201 /* if there is one-way signal in the opposite direction, then it is not our way */
202 if (IsTileType(tile, TileType::Railway)) {
203 bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir));
204 bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
205 if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
206 /* one-way signal in opposite direction */
207 n.segment->end_segment_reason.Set(EndSegmentReason::DeadEnd);
208 } else {
209 if (has_signal_along) {
210 SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
211 SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
212
213 n.last_signal_type = sig_type;
214
215 /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */
216 int look_ahead_cost = (n.num_signals_passed < this->sig_look_ahead_costs.size()) ? this->sig_look_ahead_costs[n.num_signals_passed] : 0;
217 if (sig_state != SIGNAL_STATE_RED) {
218 /* green signal */
219 n.flags_u.flags_s.last_signal_was_red = false;
220 /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */
221 if (look_ahead_cost < 0) {
222 /* add its negation to the cost */
223 cost -= look_ahead_cost;
224 }
225 } else {
226 /* we have a red signal in our direction
227 * was it first signal which is two-way? */
228 if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.choice_seen && has_signal_against && n.num_signals_passed == 0) {
229 /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
230 Yapf().PruneIntermediateNodeBranch(&n);
231 n.segment->end_segment_reason.Set(EndSegmentReason::DeadEnd);
232 Yapf().stopped_on_first_two_way_signal = true;
233 return -1;
234 }
235 n.last_red_signal_type = sig_type;
236 n.flags_u.flags_s.last_signal_was_red = true;
237
238 /* look-ahead signal penalty */
239 if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
240 /* add the look ahead penalty only if it is positive */
241 cost += look_ahead_cost;
242 }
243
244 /* special signal penalties */
245 if (n.num_signals_passed == 0) {
246 switch (sig_type) {
247 case SIGTYPE_COMBO:
248 case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
249 case SIGTYPE_BLOCK:
250 case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
251 default: break;
252 }
253 }
254 }
255
256 n.num_signals_passed++;
257 n.segment->last_signal_tile = tile;
258 n.segment->last_signal_td = trackdir;
259 }
260
261 if (has_signal_against && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) {
262 cost += n.num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
263 }
264 }
265 }
266 return cost;
267 }
268
269 inline int PlatformLengthPenalty(int platform_length)
270 {
271 int cost = 0;
272 const Train *v = Yapf().GetVehicle();
273 assert(v != nullptr);
274 assert(v->type == VEH_TRAIN);
275 assert(v->gcache.cached_total_length != 0);
276 int missing_platform_length = CeilDiv(v->gcache.cached_total_length, TILE_SIZE) - platform_length;
277 if (missing_platform_length < 0) {
278 /* apply penalty for longer platform than needed */
279 cost += Yapf().PfGetSettings().rail_longer_platform_penalty + Yapf().PfGetSettings().rail_longer_platform_per_tile_penalty * -missing_platform_length;
280 } else if (missing_platform_length > 0) {
281 /* apply penalty for shorter platform than needed */
282 cost += Yapf().PfGetSettings().rail_shorter_platform_penalty + Yapf().PfGetSettings().rail_shorter_platform_per_tile_penalty * missing_platform_length;
283 }
284 return cost;
285 }
286
287public:
288 inline void SetMaxCost(int max_cost)
289 {
290 this->max_cost = max_cost;
291 }
292
294 inline bool PfCalcCost(Node &n, const TrackFollower *follower)
295 {
296 assert(!n.flags_u.flags_s.target_seen);
297 assert(follower->new_tile == n.key.tile);
298 assert((HasTrackdir(follower->new_td_bits, n.key.td)));
299
300 /* Does the node have some parent node? */
301 bool has_parent = (n.parent != nullptr);
302
303 /* Do we already have a cached segment? */
304 CachedData &segment = *n.segment;
305 bool is_cached_segment = (segment.cost >= 0);
306
307 int parent_cost = has_parent ? n.parent->cost : 0;
308
309 /* Each node cost contains 2 or 3 main components:
310 * 1. Transition cost - cost of the move from previous node (tile):
311 * - curve cost (or zero for straight move)
312 * 2. Tile cost:
313 * - base tile cost
314 * - YAPF_TILE_LENGTH for diagonal tiles
315 * - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
316 * - tile penalties
317 * - tile slope penalty (upward slopes)
318 * - red signal penalty
319 * - level crossing penalty
320 * - speed-limit penalty (bridges)
321 * - station platform penalty
322 * - penalty for reversing in the depot
323 * - etc.
324 * 3. Extra cost (applies to the last node only)
325 * - last red signal penalty
326 * - penalty for too long or too short platform on the destination station
327 */
328 int transition_cost = 0;
329 int extra_cost = 0;
330
331 /* Segment: one or more tiles connected by contiguous tracks of the same type.
332 * Each segment cost includes 'Tile cost' for all its tiles (including the first
333 * and last), and the 'Transition cost' between its tiles. The first transition
334 * cost of segment entry (move from the 'parent' node) is not included!
335 */
336 int segment_entry_cost = 0;
337 int segment_cost = 0;
338
339 const Train *v = Yapf().GetVehicle();
340
341 /* start at n.key.tile / n.key.td and walk to the end of segment */
342 TILE cur(n.key.tile, n.key.td);
343
344 /* the previous tile will be needed for transition cost calculations */
345 TILE prev = !has_parent ? TILE() : TILE(n.parent->GetLastTile(), n.parent->GetLastTrackdir());
346
347 EndSegmentReasons end_segment_reason{};
348
349 TrackFollower follower_local{v, Yapf().GetCompatibleRailTypes()};
350
351 if (!has_parent) {
352 /* We will jump to the middle of the cost calculator assuming that segment cache is not used. */
353 assert(!is_cached_segment);
354 /* Skip the first transition cost calculation. */
355 goto no_entry_cost;
356 }
357
358 for (;;) {
359 /* Transition cost (cost of the move from previous tile) */
360 transition_cost = Yapf().CurveCost(prev.td, cur.td);
361 transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td));
362
363 /* First transition cost counts against segment entry cost, other transitions
364 * inside segment will come to segment cost (and will be cached) */
365 if (segment_cost == 0) {
366 /* We just entered the loop. First transition cost goes to segment entry cost)*/
367 segment_entry_cost = transition_cost;
368
369 /* It is the right time now to look if we can reuse the cached segment cost. */
370 if (is_cached_segment) {
371 /* Yes, we already know the segment cost. */
372 segment_cost = segment.cost;
373 /* We know also the reason why the segment ends. */
374 end_segment_reason = segment.end_segment_reason;
375 /* We will need also some information about the last signal (if it was red). */
376 if (segment.last_signal_tile != INVALID_TILE) {
377 assert(HasSignalOnTrackdir(segment.last_signal_tile, segment.last_signal_td));
378 SignalState sig_state = GetSignalStateByTrackdir(segment.last_signal_tile, segment.last_signal_td);
379 bool is_red = (sig_state == SIGNAL_STATE_RED);
380 n.flags_u.flags_s.last_signal_was_red = is_red;
381 if (is_red) {
382 n.last_red_signal_type = GetSignalType(segment.last_signal_tile, TrackdirToTrack(segment.last_signal_td));
383 }
384 }
385 /* No further calculation needed. */
386 cur = TILE(n.GetLastTile(), n.GetLastTrackdir());
387 break;
388 }
389 } else {
390 /* Other than first transition cost count as the regular segment cost. */
391 segment_cost += transition_cost;
392 }
393
394no_entry_cost: // jump here at the beginning if the node has no parent (it is the first node)
395
396 /* All other tile costs will be calculated here. */
397 segment_cost += Yapf().OneTileCost(cur.tile, cur.td);
398
399 /* If we skipped some tunnel/bridge/station tiles, add their base cost */
400 segment_cost += YAPF_TILE_LENGTH * follower->tiles_skipped;
401
402 /* Slope cost. */
403 segment_cost += Yapf().SlopeCost(cur.tile, cur.td);
404
405 /* Signal cost (routine can modify segment data). */
406 segment_cost += Yapf().SignalCost(n, cur.tile, cur.td);
407
408 /* Reserved tiles. */
409 segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, follower->tiles_skipped);
410
411 end_segment_reason = segment.end_segment_reason;
412
413 /* Tests for 'potential target' reasons to close the segment. */
414 if (cur.tile == prev.tile) {
415 /* Penalty for reversing in a depot. */
416 assert(IsRailDepot(cur.tile));
417 segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty;
418
419 } else if (IsRailDepotTile(cur.tile)) {
420 /* We will end in this pass (depot is possible target) */
421 end_segment_reason.Set(EndSegmentReason::Depot);
422
423 } else if (cur.tile_type == TileType::Station && IsRailWaypoint(cur.tile)) {
424 if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
425 GetStationIndex(cur.tile) == v->current_order.GetDestination() &&
426 !Waypoint::Get(v->current_order.GetDestination().ToStationID())->IsSingleTile()) {
427 /* This waypoint is our destination; maybe this isn't an unreserved
428 * one, so check that and if so see that as the last signal being
429 * red. This way waypoints near stations should work better. */
430 CFollowTrackRail ft(v);
431 TileIndex t = cur.tile;
432 Trackdir td = cur.td;
433 /* Arbitrary maximum tiles to follow to avoid infinite loops. */
434 uint max_tiles = 20;
435 while (ft.Follow(t, td)) {
436 assert(t != ft.new_tile);
437 t = ft.new_tile;
438 if (t == cur.tile || --max_tiles == 0) {
439 /* We looped back on ourself or found another loop, bail out. */
440 td = INVALID_TRACKDIR;
441 break;
442 }
444 /* We encountered a junction; it's going to be too complex to
445 * handle this perfectly, so just bail out. There is no simple
446 * free path, so try the other possibilities. */
447 td = INVALID_TRACKDIR;
448 break;
449 }
451 /* If this is a safe waiting position we're done searching for it */
452 if (IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg)) break;
453 }
454
455 /* In the case this platform is (possibly) occupied we add penalty so the
456 * other platforms of this waypoint are evaluated as well, i.e. we assume
457 * that there is a red signal in the waypoint when it's occupied. */
458 if (td == INVALID_TRACKDIR ||
459 !IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg) ||
460 !IsWaitingPositionFree(v, t, td, _settings_game.pf.forbid_90_deg)) {
461 extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
462 }
463 }
464 /* Waypoint is also a good reason to finish. */
465 end_segment_reason.Set(EndSegmentReason::Waypoint);
466
467 } else if (follower->is_station) {
468 /* Station penalties. */
469 uint platform_length = follower->tiles_skipped + 1;
470 /* We don't know yet if the station is our target or not. Act like
471 * if it is pass-through station (not our destination). */
472 segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
473 /* We will end in this pass (station is possible target) */
474 end_segment_reason.Set(EndSegmentReason::Station);
475
476 } else if (TrackFollower::DoTrackMasking() && cur.tile_type == TileType::Railway) {
477 /* Searching for a safe tile? */
478 if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) {
479 end_segment_reason.Set(EndSegmentReason::SafeTile);
480 }
481 }
482
483 /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
484 * it would cause desync in MP. */
485 if (n.num_signals_passed < this->sig_look_ahead_costs.size())
486 {
487 int min_speed = 0;
488 int max_speed = follower->GetSpeedLimit(&min_speed);
489 int max_veh_speed = std::min<int>(v->GetDisplayMaxSpeed(), v->current_order.GetMaxSpeed());
490 if (max_speed < max_veh_speed) {
491 extra_cost += YAPF_TILE_LENGTH * (max_veh_speed - max_speed) * (4 + follower->tiles_skipped) / max_veh_speed;
492 }
493 if (min_speed > max_veh_speed) {
494 extra_cost += YAPF_TILE_LENGTH * (min_speed - max_veh_speed);
495 }
496 }
497
498 /* Finish if we already exceeded the maximum path cost (i.e. when
499 * searching for the nearest depot). */
500 if (this->max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > this->max_cost) {
501 end_segment_reason.Set(EndSegmentReason::PathTooLong);
502 }
503
504 /* Move to the next tile/trackdir. */
505 follower = &follower_local;
506 follower_local.Init(v, Yapf().GetCompatibleRailTypes());
507
508 if (!follower_local.Follow(cur.tile, cur.td)) {
509 assert(follower_local.err != TrackFollower::EC_NONE);
510 /* Can't move to the next tile (EOL?). */
511 if (follower_local.err == TrackFollower::EC_RAIL_ROAD_TYPE) {
512 end_segment_reason.Set(EndSegmentReason::RailType);
513 } else {
514 end_segment_reason.Set(EndSegmentReason::DeadEnd);
515 }
516
517 if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
518 end_segment_reason.Set(EndSegmentReason::SafeTile);
519 }
520 break;
521 }
522
523 /* Check if the next tile is not a choice. */
524 if (KillFirstBit(follower_local.new_td_bits) != TRACKDIR_BIT_NONE) {
525 /* More than one segment will follow. Close this one. */
526 end_segment_reason.Set(EndSegmentReason::ChoiceFollows);
527 break;
528 }
529
530 /* Gather the next tile/trackdir/tile_type/rail_type. */
531 TILE next(follower_local.new_tile, (Trackdir)FindFirstBit(follower_local.new_td_bits));
532
533 if (TrackFollower::DoTrackMasking() && IsTileType(next.tile, TileType::Railway)) {
534 if (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) {
535 /* Possible safe tile. */
536 end_segment_reason.Set(EndSegmentReason::SafeTile);
537 } else if (HasSignalOnTrackdir(next.tile, ReverseTrackdir(next.td)) && GetSignalType(next.tile, TrackdirToTrack(next.td)) == SIGTYPE_PBS_ONEWAY) {
538 /* Possible safe tile, but not so good as it's the back of a signal... */
540 extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
541 }
542 }
543
544 /* Check the next tile for the rail type. */
545 if (next.rail_type != cur.rail_type) {
546 /* Segment must consist from the same rail_type tiles. */
547 end_segment_reason.Set(EndSegmentReason::RailType);
548 break;
549 }
550
551 /* Avoid infinite looping. */
552 if (next.tile == n.key.tile && next.td == n.key.td) {
553 end_segment_reason.Set(EndSegmentReason::InfiniteLoop);
554 break;
555 }
556
557 if (segment_cost > MAX_SEGMENT_COST) {
558 /* Potentially in the infinite loop (or only very long segment?). We should
559 * not force it to finish prematurely unless we are on a regular tile. */
560 if (IsTileType(follower->new_tile, TileType::Railway)) {
561 end_segment_reason.Set(EndSegmentReason::SegmentTooLong);
562 break;
563 }
564 }
565
566 /* Any other reason bit set? */
567 if (end_segment_reason.Any()) {
568 break;
569 }
570
571 /* For the next loop set new prev and cur tile info. */
572 prev = cur;
573 cur = next;
574
575 } // for (;;)
576
577 /* Don't consider path any further it if exceeded max_cost. */
578 if (end_segment_reason.Test(EndSegmentReason::PathTooLong)) return false;
579
580 bool target_seen = false;
581 if (end_segment_reason.Any(ESRF_POSSIBLE_TARGET)) {
582 /* Depot, station or waypoint. */
583 if (Yapf().PfDetectDestination(cur.tile, cur.td)) {
584 /* Destination found. */
585 target_seen = true;
586 }
587 }
588
589 /* Update the segment if needed. */
590 if (!is_cached_segment) {
591 /* Write back the segment information so it can be reused the next time. */
592 segment.cost = segment_cost;
593 segment.end_segment_reason = end_segment_reason & ESRF_CACHED_MASK;
594 /* Save end of segment back to the node. */
595 n.SetLastTileTrackdir(cur.tile, cur.td);
596 }
597
598 /* Do we have an excuse why not to continue pathfinding in this direction? */
599 if (!target_seen && end_segment_reason.Any(ESRF_ABORT_PF_MASK)) {
600 /* Reason to not continue. Stop this PF branch. */
601 return false;
602 }
603
604 /* Special costs for the case we have reached our target. */
605 if (target_seen) {
606 n.flags_u.flags_s.target_seen = true;
607 /* Last-red and last-red-exit penalties. */
608 if (n.flags_u.flags_s.last_signal_was_red) {
609 if (n.last_red_signal_type == SIGTYPE_EXIT) {
610 /* last signal was red pre-signal-exit */
611 extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
612 } else if (!IsPbsSignal(n.last_red_signal_type)) {
613 /* Last signal was red, but not exit or path signal. */
614 extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
615 }
616 }
617
618 /* Station platform-length penalty. */
619 if (end_segment_reason.Test(EndSegmentReason::Station)) {
620 const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
621 assert(st != nullptr);
622 uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));
623 /* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
624 extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length;
625 /* Add penalty for the inappropriate platform length. */
626 extra_cost += PlatformLengthPenalty(platform_length);
627 }
628 }
629
630 /* total node cost */
631 n.cost = parent_cost + segment_entry_cost + segment_cost + extra_cost;
632
633 return true;
634 }
635
636 inline bool CanUseGlobalCache(Node &n) const
637 {
638 return !this->disable_cache
639 && (n.parent != nullptr)
640 && (n.parent->num_signals_passed >= this->sig_look_ahead_costs.size());
641 }
642
643 inline void ConnectNodeToCachedData(Node &n, CachedData &ci)
644 {
645 n.segment = &ci;
646 if (n.segment->cost < 0) {
647 n.segment->last_tile = n.key.tile;
648 n.segment->last_td = n.key.td;
649 }
650 }
651
652 void DisableCache(bool disable)
653 {
654 this->disable_cache = disable;
655 }
656};
657
658#endif /* YAPF_COSTRAIL_HPP */
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Tpf & Yapf()
Access the inherited path finder.
bool TreatFirstRedTwoWaySignalAsEOL()
Returns whether the first two-way signal should be treated as a dead end.
Types::Tpf Tpf
the pathfinder class (derived from THIS class)
int ReservationCost(Node &n, TileIndex tile, Trackdir trackdir, int skipped)
Calculate the cost for reserved tiles, including skipped ones.
int OneTileCost(TileIndex tile, Trackdir trackdir)
Return one tile cost (base cost + level crossing penalty).
void SetTreatFirstRedTwoWaySignalAsEOL(bool enabled)
Sets whether the first two-way signal should be treated as a dead end.
Node::Key Key
key to hash tables
bool IsAnyStationTileReserved(TileIndex tile, Trackdir trackdir, int distance)
Check for a reserved station platform.
bool PfCalcCost(Node &n, const TrackFollower *follower)
Called by YAPF to calculate the cost from the origin to the given node.
Types::NodeList::Item Node
this will be our node type
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirection
Enumeration for diagonal directions.
Template function for track followers.
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:574
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
General types related to pathfinders.
static const int YAPF_TILE_CORNER_LENGTH
Length (penalty) of a corner with YAPF.
static const int YAPF_TILE_LENGTH
Length (penalty) of one tile with YAPF.
TrackBits GetReservedTrackbits(TileIndex t)
Get the reserved trackbits for any tile, regardless of type.
Definition pbs.cpp:24
bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg)
Check if a safe position is free.
Definition pbs.cpp:441
bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
Determine whether a certain track on a tile is a safe position to end a path.
Definition pbs.cpp:395
PBS support routines.
RailType GetTileRailType(Tile tile)
Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile.
Definition rail.cpp:39
bool HasOnewaySignalBlockingTrackdir(Tile tile, Trackdir td)
Is a one-way signal blocking the trackdir?
Definition rail_map.h:548
bool HasSignalOnTrackdir(Tile tile, Trackdir trackdir)
Checks for the presence of signals along the given trackdir on the given rail tile.
Definition rail_map.h:491
static bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
static bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
bool IsPbsSignal(SignalType s)
Checks whether the given signal is a path based signal.
Definition rail_map.h:292
bool IsOnewaySignal(Tile t, Track track)
Is the signal at the given track on a tile a one way signal?
Definition rail_map.h:358
SignalType GetSignalType(Tile t, Track track)
Get the signal type for a track on a tile.
Definition rail_map.h:304
SignalState GetSignalStateByTrackdir(Tile tile, Trackdir trackdir)
Gets the state of the signal along the given trackdir.
Definition rail_map.h:506
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
bool IsLevelCrossing(Tile t)
Return whether a tile is a level crossing.
Definition road_map.h:69
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
SignalType
Type of signal, i.e.
Definition signal_type.h:23
@ SIGTYPE_PBS_ONEWAY
no-entry signal
Definition signal_type.h:29
@ SIGTYPE_ENTRY
presignal block entry
Definition signal_type.h:25
@ SIGTYPE_COMBO
presignal inter-block
Definition signal_type.h:27
@ SIGTYPE_EXIT
presignal block exit
Definition signal_type.h:26
@ SIGTYPE_BLOCK
block signal
Definition signal_type.h:24
SignalState
These are states in which a signal can be.
Definition signal_type.h:42
@ SIGNAL_STATE_RED
The signal is red.
Definition signal_type.h:43
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
bool IsRailWaypoint(Tile t)
Is this station tile a rail waypoint?
bool HasStationReservation(Tile t)
Get the reservation state of the rail station.
Base class for all station-ish types.
virtual uint GetPlatformLength(TileIndex tile) const =0
Obtain the length of a platform.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
VehicleType type
Type of vehicle.
bool Follow(TileIndex old_tile, Trackdir old_td)
Main follower routine.
TrackdirBits new_td_bits
the new set of available trackdirs
TileIndex new_tile
the new tile (the vehicle has entered)
Base implementation for cost accounting.
static bool stSlopeCost(TileIndex tile, Trackdir td)
Does the given track direction on the given tile yield an uphill penalty?
Structure used inside PfCalcCost() to keep basic tile information.
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
GroundVehicleCache gcache
Cache of often calculated values.
uint16_t GetMaxSpeed() const
Get the maxmimum speed in km-ish/h a vehicle is allowed to reach on the way to the destination.
Definition order_base.h:307
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:100
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
static Waypoint * Get(auto index)
'Train' is either a loco or a wagon.
Definition train.h:97
int GetDisplayMaxSpeed() const override
Gets the maximum speed in km-ish/h that can be sent into string parameters for string processing.
Definition train.h:127
Order current_order
The current order (+ status, like: loading).
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
TileType
The different types of tiles.
Definition tile_type.h:48
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Railway
A tile with railway.
Definition tile_type.h:50
@ Void
Invisible tiles at the SW and SE border.
Definition tile_type.h:56
@ Road
A tile with road and/or tram tracks.
Definition tile_type.h:51
Trackdir RemoveFirstTrackdir(TrackdirBits *trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:156
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:262
bool TrackOverlapsTracks(TrackBits tracks, Track track)
Check if a given track is contained within or overlaps some other tracks.
Definition track_func.h:667
Trackdir NextTrackdir(Trackdir trackdir)
Maps a trackdir to the trackdir that you will end up on if you go straight ahead.
Definition track_func.h:405
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
bool IsValidTrackdir(Trackdir trackdir)
Checks if a Trackdir is valid for non-road vehicles.
Definition track_func.h:52
TrackdirBits TrackdirCrossesTrackdirs(Trackdir trackdir)
Maps a trackdir to all trackdirs that make 90 deg turns with it.
Definition track_func.h:611
bool IsDiagonalTrackdir(Trackdir trackdir)
Checks if a given Trackdir is diagonal.
Definition track_func.h:636
bool HasTrackdir(TrackdirBits trackdirs, Trackdir trackdir)
Checks whether a TrackdirBits has a given Trackdir.
Definition track_func.h:342
TrackBits DiagdirReachesTracks(DiagDirection diagdir)
Returns all tracks that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:578
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:441
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:85
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
@ VEH_TRAIN
Train vehicle type.
Handling of cost determination.
Types used by YAPF.
static constexpr EndSegmentReasons ESRF_ABORT_PF_MASK
Reasons to abort pathfinding in this direction.
Definition yapf_type.hpp:60
static constexpr EndSegmentReasons ESRF_CACHED_MASK
What reasons can be stored back into cached segment.
Definition yapf_type.hpp:47
@ SegmentTooLong
the segment is too long (possible infinite loop)
Definition yapf_type.hpp:22
@ ChoiceFollows
the next tile contains a choice (the track splits to more than one segments)
Definition yapf_type.hpp:23
@ Waypoint
waypoint encountered (could be a target next time)
Definition yapf_type.hpp:25
@ Station
station encountered (could be a target next time)
Definition yapf_type.hpp:26
@ RailType
the next tile has a different rail type than our tiles
Definition yapf_type.hpp:20
@ InfiniteLoop
infinite loop detected
Definition yapf_type.hpp:21
@ DeadEnd
track ends here
Definition yapf_type.hpp:19
@ Depot
stop in the depot (could be a target next time)
Definition yapf_type.hpp:24
@ SafeTile
safe waiting position found (could be a target)
Definition yapf_type.hpp:27
@ PathTooLong
the path is too long (searching for the nearest depot in the given radius)
Definition yapf_type.hpp:31
static constexpr EndSegmentReasons ESRF_POSSIBLE_TARGET
What reasons mean that the target can be found and needs to be detected.
Definition yapf_type.hpp:39