1
0
Fork 0

Codechange: Use EnumBitSet for EndSegmentReasons. (#13490)

pull/13494/head
Peter Nelson 2025-02-07 23:53:23 +00:00 committed by GitHub
parent 50b384032d
commit 11bfd5bb9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 96 additions and 74 deletions

View File

@ -86,6 +86,32 @@ inline std::string ComposeNameT(E value, T &t, const char *t_unk, E val_inv, con
return out; return out;
} }
/**
* Helper template function that returns compound bitfield name that is
* concatenation of names of each set bit in the given value
* or unknown_name when index is out of bounds.
*/
template <typename E>
inline std::string ComposeNameT(E value, std::span<const std::string_view> names, std::string_view unknown_name)
{
std::string out;
if (value.base() == 0) {
out = "<none>";
} else {
for (size_t i = 0; i < std::size(names); ++i) {
if (!value.Test(static_cast<E::EnumType>(i))) continue;
out += (!out.empty() ? "+" : "");
out += names[i];
value.Reset(static_cast<E::EnumType>(i));
}
if (value.base() != 0) {
out += (!out.empty() ? "+" : "");
out += unknown_name;
}
}
return out;
}
std::string ValueStr(Trackdir td); std::string ValueStr(Trackdir td);
std::string ValueStr(TrackdirBits td_bits); std::string ValueStr(TrackdirBits td_bits);
std::string ValueStr(DiagDirection dd); std::string ValueStr(DiagDirection dd);

View File

@ -196,7 +196,7 @@ public:
void PruneIntermediateNodeBranch(Node *n) void PruneIntermediateNodeBranch(Node *n)
{ {
bool intermediate_on_branch = false; bool intermediate_on_branch = false;
while (n != nullptr && (n->segment->end_segment_reason & ESRB_CHOICE_FOLLOWS) == 0) { while (n != nullptr && !n->segment->end_segment_reason.Test(EndSegmentReason::ChoiceFollows)) {
if (n == Yapf().best_intermediate_node) intermediate_on_branch = true; if (n == Yapf().best_intermediate_node) intermediate_on_branch = true;
n = n->parent; n = n->parent;
} }

View File

@ -180,7 +180,7 @@ public:
bool has_signal_along = HasSignalOnTrackdir(tile, trackdir); bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) { if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
/* one-way signal in opposite direction */ /* one-way signal in opposite direction */
n.segment->end_segment_reason |= ESRB_DEAD_END; n.segment->end_segment_reason.Set(EndSegmentReason::DeadEnd);
} else { } else {
if (has_signal_along) { if (has_signal_along) {
SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir); SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
@ -204,7 +204,7 @@ public:
if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.choice_seen && has_signal_against && n.num_signals_passed == 0) { if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.choice_seen && has_signal_against && n.num_signals_passed == 0) {
/* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */ /* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
Yapf().PruneIntermediateNodeBranch(&n); Yapf().PruneIntermediateNodeBranch(&n);
n.segment->end_segment_reason |= ESRB_DEAD_END; n.segment->end_segment_reason.Set(EndSegmentReason::DeadEnd);
Yapf().stopped_on_first_two_way_signal = true; Yapf().stopped_on_first_two_way_signal = true;
return -1; return -1;
} }
@ -324,7 +324,7 @@ public:
/* the previous tile will be needed for transition cost calculations */ /* the previous tile will be needed for transition cost calculations */
TILE prev = !has_parent ? TILE() : TILE(n.parent->GetLastTile(), n.parent->GetLastTrackdir()); TILE prev = !has_parent ? TILE() : TILE(n.parent->GetLastTile(), n.parent->GetLastTrackdir());
EndSegmentReasonBits end_segment_reason = ESRB_NONE; EndSegmentReasons end_segment_reason{};
TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes()); TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes());
@ -399,7 +399,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
} else if (IsRailDepotTile(cur.tile)) { } else if (IsRailDepotTile(cur.tile)) {
/* We will end in this pass (depot is possible target) */ /* We will end in this pass (depot is possible target) */
end_segment_reason |= ESRB_DEPOT; end_segment_reason.Set(EndSegmentReason::Depot);
} else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) { } else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) {
if (v->current_order.IsType(OT_GOTO_WAYPOINT) && if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
@ -443,7 +443,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
} }
} }
/* Waypoint is also a good reason to finish. */ /* Waypoint is also a good reason to finish. */
end_segment_reason |= ESRB_WAYPOINT; end_segment_reason.Set(EndSegmentReason::Waypoint);
} else if (tf->is_station) { } else if (tf->is_station) {
/* Station penalties. */ /* Station penalties. */
@ -452,12 +452,12 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
* if it is pass-through station (not our destination). */ * if it is pass-through station (not our destination). */
segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length; segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
/* We will end in this pass (station is possible target) */ /* We will end in this pass (station is possible target) */
end_segment_reason |= ESRB_STATION; end_segment_reason.Set(EndSegmentReason::Station);
} else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) { } else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) {
/* Searching for a safe tile? */ /* Searching for a safe tile? */
if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) { if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) {
end_segment_reason |= ESRB_SAFE_TILE; end_segment_reason.Set(EndSegmentReason::SafeTile);
} }
} }
@ -479,7 +479,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
/* Finish if we already exceeded the maximum path cost (i.e. when /* Finish if we already exceeded the maximum path cost (i.e. when
* searching for the nearest depot). */ * searching for the nearest depot). */
if (this->max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > this->max_cost) { if (this->max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > this->max_cost) {
end_segment_reason |= ESRB_PATH_TOO_LONG; end_segment_reason.Set(EndSegmentReason::PathTooLong);
} }
/* Move to the next tile/trackdir. */ /* Move to the next tile/trackdir. */
@ -490,13 +490,13 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
assert(tf_local.err != TrackFollower::EC_NONE); assert(tf_local.err != TrackFollower::EC_NONE);
/* Can't move to the next tile (EOL?). */ /* Can't move to the next tile (EOL?). */
if (tf_local.err == TrackFollower::EC_RAIL_ROAD_TYPE) { if (tf_local.err == TrackFollower::EC_RAIL_ROAD_TYPE) {
end_segment_reason |= ESRB_RAIL_TYPE; end_segment_reason.Set(EndSegmentReason::RailType);
} else { } else {
end_segment_reason |= ESRB_DEAD_END; end_segment_reason.Set(EndSegmentReason::DeadEnd);
} }
if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) { if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
end_segment_reason |= ESRB_SAFE_TILE; end_segment_reason.Set(EndSegmentReason::SafeTile);
} }
break; break;
} }
@ -504,7 +504,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
/* Check if the next tile is not a choice. */ /* Check if the next tile is not a choice. */
if (KillFirstBit(tf_local.new_td_bits) != TRACKDIR_BIT_NONE) { if (KillFirstBit(tf_local.new_td_bits) != TRACKDIR_BIT_NONE) {
/* More than one segment will follow. Close this one. */ /* More than one segment will follow. Close this one. */
end_segment_reason |= ESRB_CHOICE_FOLLOWS; end_segment_reason.Set(EndSegmentReason::ChoiceFollows);
break; break;
} }
@ -514,10 +514,11 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
if (TrackFollower::DoTrackMasking() && IsTileType(next.tile, MP_RAILWAY)) { if (TrackFollower::DoTrackMasking() && IsTileType(next.tile, MP_RAILWAY)) {
if (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) { if (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) {
/* Possible safe tile. */ /* Possible safe tile. */
end_segment_reason |= ESRB_SAFE_TILE; end_segment_reason.Set(EndSegmentReason::SafeTile);
} else if (HasSignalOnTrackdir(next.tile, ReverseTrackdir(next.td)) && GetSignalType(next.tile, TrackdirToTrack(next.td)) == SIGTYPE_PBS_ONEWAY) { } else if (HasSignalOnTrackdir(next.tile, ReverseTrackdir(next.td)) && GetSignalType(next.tile, TrackdirToTrack(next.td)) == SIGTYPE_PBS_ONEWAY) {
/* Possible safe tile, but not so good as it's the back of a signal... */ /* Possible safe tile, but not so good as it's the back of a signal... */
end_segment_reason |= ESRB_SAFE_TILE | ESRB_DEAD_END; end_segment_reason.Set(EndSegmentReason::SafeTile);
end_segment_reason.Set(EndSegmentReason::DeadEnd);
extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty; extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
} }
} }
@ -525,13 +526,13 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
/* Check the next tile for the rail type. */ /* Check the next tile for the rail type. */
if (next.rail_type != cur.rail_type) { if (next.rail_type != cur.rail_type) {
/* Segment must consist from the same rail_type tiles. */ /* Segment must consist from the same rail_type tiles. */
end_segment_reason |= ESRB_RAIL_TYPE; end_segment_reason.Set(EndSegmentReason::RailType);
break; break;
} }
/* Avoid infinite looping. */ /* Avoid infinite looping. */
if (next.tile == n.key.tile && next.td == n.key.td) { if (next.tile == n.key.tile && next.td == n.key.td) {
end_segment_reason |= ESRB_INFINITE_LOOP; end_segment_reason.Set(EndSegmentReason::InfiniteLoop);
break; break;
} }
@ -539,13 +540,13 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
/* Potentially in the infinite loop (or only very long segment?). We should /* Potentially in the infinite loop (or only very long segment?). We should
* not force it to finish prematurely unless we are on a regular tile. */ * not force it to finish prematurely unless we are on a regular tile. */
if (IsTileType(tf->new_tile, MP_RAILWAY)) { if (IsTileType(tf->new_tile, MP_RAILWAY)) {
end_segment_reason |= ESRB_SEGMENT_TOO_LONG; end_segment_reason.Set(EndSegmentReason::SegmentTooLong);
break; break;
} }
} }
/* Any other reason bit set? */ /* Any other reason bit set? */
if (end_segment_reason != ESRB_NONE) { if (end_segment_reason != EndSegmentReasons{}) {
break; break;
} }
@ -556,10 +557,10 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
} // for (;;) } // for (;;)
/* Don't consider path any further it if exceeded max_cost. */ /* Don't consider path any further it if exceeded max_cost. */
if (end_segment_reason & ESRB_PATH_TOO_LONG) return false; if (end_segment_reason.Test(EndSegmentReason::PathTooLong)) return false;
bool target_seen = false; bool target_seen = false;
if ((end_segment_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) { if (end_segment_reason.Any(ESRF_POSSIBLE_TARGET)) {
/* Depot, station or waypoint. */ /* Depot, station or waypoint. */
if (Yapf().PfDetectDestination(cur.tile, cur.td)) { if (Yapf().PfDetectDestination(cur.tile, cur.td)) {
/* Destination found. */ /* Destination found. */
@ -571,13 +572,13 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
if (!is_cached_segment) { if (!is_cached_segment) {
/* Write back the segment information so it can be reused the next time. */ /* Write back the segment information so it can be reused the next time. */
segment.cost = segment_cost; segment.cost = segment_cost;
segment.end_segment_reason = end_segment_reason & ESRB_CACHED_MASK; segment.end_segment_reason = end_segment_reason & ESRF_CACHED_MASK;
/* Save end of segment back to the node. */ /* Save end of segment back to the node. */
n.SetLastTileTrackdir(cur.tile, cur.td); n.SetLastTileTrackdir(cur.tile, cur.td);
} }
/* Do we have an excuse why not to continue pathfinding in this direction? */ /* Do we have an excuse why not to continue pathfinding in this direction? */
if (!target_seen && (end_segment_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) { if (!target_seen && end_segment_reason.Any(ESRF_ABORT_PF_MASK)) {
/* Reason to not continue. Stop this PF branch. */ /* Reason to not continue. Stop this PF branch. */
return false; return false;
} }
@ -597,7 +598,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
} }
/* Station platform-length penalty. */ /* Station platform-length penalty. */
if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) { if (end_segment_reason.Test(EndSegmentReason::Station)) {
const BaseStation *st = BaseStation::GetByTile(n.GetLastTile()); const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
assert(st != nullptr); assert(st != nullptr);
uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir()))); uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));

View File

@ -74,7 +74,7 @@ struct CYapfRailSegment
int cost = -1; int cost = -1;
TileIndex last_signal_tile = INVALID_TILE; TileIndex last_signal_tile = INVALID_TILE;
Trackdir last_signal_td = INVALID_TRACKDIR; Trackdir last_signal_td = INVALID_TRACKDIR;
EndSegmentReasonBits end_segment_reason = ESRB_NONE; EndSegmentReasons end_segment_reason{};
CYapfRailSegment *hash_next = nullptr; CYapfRailSegment *hash_next = nullptr;
inline CYapfRailSegment(const CYapfRailSegmentKey &key) : key(key) {} inline CYapfRailSegment(const CYapfRailSegmentKey &key) : key(key) {}

View File

@ -17,72 +17,67 @@
#include "../../misc/dbg_helpers.h" #include "../../misc/dbg_helpers.h"
/* Enum used in PfCalcCost() to see why was the segment closed. */ /* Enum used in PfCalcCost() to see why was the segment closed. */
enum EndSegmentReason : uint8_t { enum class EndSegmentReason : uint8_t {
/* The following reasons can be saved into cached segment */ /* The following reasons can be saved into cached segment */
ESR_DEAD_END = 0, ///< track ends here DeadEnd, ///< track ends here
ESR_RAIL_TYPE, ///< the next tile has a different rail type than our tiles RailType, ///< the next tile has a different rail type than our tiles
ESR_INFINITE_LOOP, ///< infinite loop detected InfiniteLoop, ///< infinite loop detected
ESR_SEGMENT_TOO_LONG, ///< the segment is too long (possible infinite loop) SegmentTooLong, ///< the segment is too long (possible infinite loop)
ESR_CHOICE_FOLLOWS, ///< the next tile contains a choice (the track splits to more than one segments) ChoiceFollows, ///< the next tile contains a choice (the track splits to more than one segments)
ESR_DEPOT, ///< stop in the depot (could be a target next time) Depot, ///< stop in the depot (could be a target next time)
ESR_WAYPOINT, ///< waypoint encountered (could be a target next time) Waypoint, ///< waypoint encountered (could be a target next time)
ESR_STATION, ///< station encountered (could be a target next time) Station, ///< station encountered (could be a target next time)
ESR_SAFE_TILE, ///< safe waiting position found (could be a target) SafeTile, ///< safe waiting position found (could be a target)
/* The following reasons are used only internally by PfCalcCost(). /* The following reasons are used only internally by PfCalcCost().
* They should not be found in the cached segment. */ * They should not be found in the cached segment. */
ESR_PATH_TOO_LONG, ///< the path is too long (searching for the nearest depot in the given radius) PathTooLong, ///< the path is too long (searching for the nearest depot in the given radius)
ESR_FIRST_TWO_WAY_RED, ///< first signal was 2-way and it was red FirstTwoWayRed, ///< first signal was 2-way and it was red
ESR_LOOK_AHEAD_END, ///< we have just passed the last look-ahead signal LookAheadEnd, ///< we have just passed the last look-ahead signal
ESR_TARGET_REACHED, ///< we have just reached the destination TargetReached, ///< we have just reached the destination
};
using EndSegmentReasons = EnumBitSet<EndSegmentReason, uint16_t>;
/* Special values */ /* What reasons mean that the target can be found and needs to be detected. */
ESR_NONE = 0xFF, ///< no reason to end the segment here static constexpr EndSegmentReasons ESRF_POSSIBLE_TARGET = {
EndSegmentReason::Depot,
EndSegmentReason::Waypoint,
EndSegmentReason::Station,
EndSegmentReason::SafeTile,
}; };
enum EndSegmentReasonBits : uint16_t { /* What reasons can be stored back into cached segment. */
ESRB_NONE = 0, static constexpr EndSegmentReasons ESRF_CACHED_MASK = {
EndSegmentReason::DeadEnd,
ESRB_DEAD_END = 1 << ESR_DEAD_END, EndSegmentReason::RailType,
ESRB_RAIL_TYPE = 1 << ESR_RAIL_TYPE, EndSegmentReason::InfiniteLoop,
ESRB_INFINITE_LOOP = 1 << ESR_INFINITE_LOOP, EndSegmentReason::SegmentTooLong,
ESRB_SEGMENT_TOO_LONG = 1 << ESR_SEGMENT_TOO_LONG, EndSegmentReason::ChoiceFollows,
ESRB_CHOICE_FOLLOWS = 1 << ESR_CHOICE_FOLLOWS, EndSegmentReason::Depot,
ESRB_DEPOT = 1 << ESR_DEPOT, EndSegmentReason::Waypoint,
ESRB_WAYPOINT = 1 << ESR_WAYPOINT, EndSegmentReason::Station,
ESRB_STATION = 1 << ESR_STATION, EndSegmentReason::SafeTile,
ESRB_SAFE_TILE = 1 << ESR_SAFE_TILE,
ESRB_PATH_TOO_LONG = 1 << ESR_PATH_TOO_LONG,
ESRB_FIRST_TWO_WAY_RED = 1 << ESR_FIRST_TWO_WAY_RED,
ESRB_LOOK_AHEAD_END = 1 << ESR_LOOK_AHEAD_END,
ESRB_TARGET_REACHED = 1 << ESR_TARGET_REACHED,
/* Additional (composite) values. */
/* What reasons mean that the target can be found and needs to be detected. */
ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
/* What reasons can be stored back into cached segment. */
ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
/* Reasons to abort pathfinding in this direction. */
ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED,
}; };
DECLARE_ENUM_AS_BIT_SET(EndSegmentReasonBits) /* Reasons to abort pathfinding in this direction. */
static constexpr EndSegmentReasons ESRF_ABORT_PF_MASK = {
EndSegmentReason::DeadEnd,
EndSegmentReason::PathTooLong,
EndSegmentReason::InfiniteLoop,
EndSegmentReason::FirstTwoWayRed,
};
inline std::string ValueStr(EndSegmentReasonBits bits) inline std::string ValueStr(EndSegmentReasons flags)
{ {
static const char * const end_segment_reason_names[] = { static const std::initializer_list<std::string_view> end_segment_reason_names = {
"DEAD_END", "RAIL_TYPE", "INFINITE_LOOP", "SEGMENT_TOO_LONG", "CHOICE_FOLLOWS", "DEAD_END", "RAIL_TYPE", "INFINITE_LOOP", "SEGMENT_TOO_LONG", "CHOICE_FOLLOWS",
"DEPOT", "WAYPOINT", "STATION", "SAFE_TILE", "DEPOT", "WAYPOINT", "STATION", "SAFE_TILE",
"PATH_TOO_LONG", "FIRST_TWO_WAY_RED", "LOOK_AHEAD_END", "TARGET_REACHED" "PATH_TOO_LONG", "FIRST_TWO_WAY_RED", "LOOK_AHEAD_END", "TARGET_REACHED"
}; };
std::stringstream ss; std::stringstream ss;
ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << bits; // 0x%04X ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << flags.base(); // 0x%04X
ss << " (" << ComposeNameT(bits, end_segment_reason_names, "UNK", ESRB_NONE, "NONE") << ")"; ss << " (" << ComposeNameT(flags, end_segment_reason_names, "UNK") << ")";
return ss.str(); return ss.str();
} }