mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Use EnumBitSet for EndSegmentReasons. (#13490)
parent
50b384032d
commit
11bfd5bb9e
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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())));
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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
|
||||
|
||||
/* Special values */
|
||||
ESR_NONE = 0xFF, ///< no reason to end the segment here
|
||||
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
|
||||
};
|
||||
|
||||
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. */
|
||||
using EndSegmentReasons = EnumBitSet<EndSegmentReason, uint16_t>;
|
||||
|
||||
/* 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,
|
||||
static constexpr EndSegmentReasons ESRF_POSSIBLE_TARGET = {
|
||||
EndSegmentReason::Depot,
|
||||
EndSegmentReason::Waypoint,
|
||||
EndSegmentReason::Station,
|
||||
EndSegmentReason::SafeTile,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_AS_BIT_SET(EndSegmentReasonBits)
|
||||
/* 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,
|
||||
};
|
||||
|
||||
inline std::string ValueStr(EndSegmentReasonBits bits)
|
||||
/* 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(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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue