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;
}
/**
* 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(TrackdirBits td_bits);
std::string ValueStr(DiagDirection dd);

View File

@ -196,7 +196,7 @@ public:
void PruneIntermediateNodeBranch(Node *n)
{
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;
n = n->parent;
}

View File

@ -180,7 +180,7 @@ public:
bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
/* one-way signal in opposite direction */
n.segment->end_segment_reason |= ESRB_DEAD_END;
n.segment->end_segment_reason.Set(EndSegmentReason::DeadEnd);
} else {
if (has_signal_along) {
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) {
/* yes, the first signal is two-way red signal => DEAD END. Prune this branch... */
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;
return -1;
}
@ -324,7 +324,7 @@ public:
/* the previous tile will be needed for transition cost calculations */
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());
@ -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)) {
/* 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)) {
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. */
end_segment_reason |= ESRB_WAYPOINT;
end_segment_reason.Set(EndSegmentReason::Waypoint);
} else if (tf->is_station) {
/* 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). */
segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
/* 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) {
/* Searching for a safe tile? */
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
* searching for the nearest depot). */
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. */
@ -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);
/* Can't move to the next tile (EOL?). */
if (tf_local.err == TrackFollower::EC_RAIL_ROAD_TYPE) {
end_segment_reason |= ESRB_RAIL_TYPE;
end_segment_reason.Set(EndSegmentReason::RailType);
} else {
end_segment_reason |= ESRB_DEAD_END;
end_segment_reason.Set(EndSegmentReason::DeadEnd);
}
if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
end_segment_reason |= ESRB_SAFE_TILE;
end_segment_reason.Set(EndSegmentReason::SafeTile);
}
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. */
if (KillFirstBit(tf_local.new_td_bits) != TRACKDIR_BIT_NONE) {
/* More than one segment will follow. Close this one. */
end_segment_reason |= ESRB_CHOICE_FOLLOWS;
end_segment_reason.Set(EndSegmentReason::ChoiceFollows);
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 (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) {
/* 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) {
/* 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;
}
}
@ -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. */
if (next.rail_type != cur.rail_type) {
/* Segment must consist from the same rail_type tiles. */
end_segment_reason |= ESRB_RAIL_TYPE;
end_segment_reason.Set(EndSegmentReason::RailType);
break;
}
/* Avoid infinite looping. */
if (next.tile == n.key.tile && next.td == n.key.td) {
end_segment_reason |= ESRB_INFINITE_LOOP;
end_segment_reason.Set(EndSegmentReason::InfiniteLoop);
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
* not force it to finish prematurely unless we are on a regular tile. */
if (IsTileType(tf->new_tile, MP_RAILWAY)) {
end_segment_reason |= ESRB_SEGMENT_TOO_LONG;
end_segment_reason.Set(EndSegmentReason::SegmentTooLong);
break;
}
}
/* Any other reason bit set? */
if (end_segment_reason != ESRB_NONE) {
if (end_segment_reason != EndSegmentReasons{}) {
break;
}
@ -556,10 +557,10 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
} // for (;;)
/* 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;
if ((end_segment_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) {
if (end_segment_reason.Any(ESRF_POSSIBLE_TARGET)) {
/* Depot, station or waypoint. */
if (Yapf().PfDetectDestination(cur.tile, cur.td)) {
/* 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) {
/* Write back the segment information so it can be reused the next time. */
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. */
n.SetLastTileTrackdir(cur.tile, cur.td);
}
/* 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. */
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. */
if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) {
if (end_segment_reason.Test(EndSegmentReason::Station)) {
const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
assert(st != nullptr);
uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));

View File

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

View File

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