diff options
Diffstat (limited to 'src/yapf/yapf_costrail.hpp')
-rw-r--r-- | src/yapf/yapf_costrail.hpp | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp new file mode 100644 index 000000000..93062b5c2 --- /dev/null +++ b/src/yapf/yapf_costrail.hpp @@ -0,0 +1,381 @@ +/* $Id$ */ + +#ifndef YAPF_COSTRAIL_HPP +#define YAPF_COSTRAIL_HPP + + +template <class Types> +class CYapfCostRailT + : public CYapfCostBase + , public CostRailSettings +{ +public: + typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class) + typedef typename Types::TrackFollower TrackFollower; + typedef typename Types::NodeList::Titem Node; ///< this will be our node type + typedef typename Node::Key Key; ///< key to hash tables + typedef typename Node::CachedData CachedData; + +protected: + int m_max_cost; + CBlobT<int> m_sig_look_ahead_costs; +public: + bool m_stopped_on_first_two_way_signal; +protected: + + static const int s_max_segment_cost = 10000; + + CYapfCostRailT() + : m_max_cost(0) + , m_stopped_on_first_two_way_signal(false) + { + // pre-compute look-ahead penalties into array + int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0; + int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1; + int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2; + int *pen = m_sig_look_ahead_costs.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals); + for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) + pen[i] = p0 + i * (p1 + i * p2); + } + + /// to access inherited path finder + Tpf& Yapf() {return *static_cast<Tpf*>(this);} + +public: + FORCEINLINE int SlopeCost(TileIndex tile, Trackdir td) + { + CPerfStart perf_cost(Yapf().m_perf_slope_cost); + if (!stSlopeCost(tile, td)) return 0; + return Yapf().PfGetSettings().rail_slope_penalty; + } + + FORCEINLINE int CurveCost(Trackdir td1, Trackdir td2) + { + int cost = 0; + if (TrackFollower::Allow90degTurns() + && ((TrackdirToTrackdirBits(td2) & (TrackdirBits)TrackdirCrossesTrackdirs(td1)) != 0)) { + // 90-deg curve penalty + cost += Yapf().PfGetSettings().rail_curve90_penalty; + } else if (td2 != NextTrackdir(td1)) { + // 45-deg curve penalty + cost += Yapf().PfGetSettings().rail_curve45_penalty; + } + return cost; + } + + /** return one tile cost. If tile is a tunnel entry, it is moved to the end of tunnel */ + FORCEINLINE int OneTileCost(TileIndex& tile, Trackdir trackdir) + { + int cost = 0; + // set base cost + if (IsDiagonalTrackdir(trackdir)) { + cost += YAPF_TILE_LENGTH; + switch (GetTileType(tile)) { + case MP_STREET: + /* Increase the cost for level crossings */ + if (IsLevelCrossing(tile)) + cost += Yapf().PfGetSettings().rail_crossing_penalty; + break; + + case MP_STATION: + // penalty for passing station tiles + cost += Yapf().PfGetSettings().rail_station_penalty; + break; + + default: + break; + } + } else { + // non-diagonal trackdir + cost = YAPF_TILE_CORNER_LENGTH; + } + return cost; + } + + int SignalCost(Node& n, TileIndex tile, Trackdir trackdir) + { + int cost = 0; + // if there is one-way signal in the opposite direction, then it is not our way + CPerfStart perf_cost(Yapf().m_perf_other_cost); + if (IsTileType(tile, MP_RAILWAY)) { + bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir)); + bool has_signal_along = HasSignalOnTrackdir(tile, trackdir); + if (has_signal_against && !has_signal_along) { + // one-way signal in opposite direction + n.m_segment->flags_u.flags_s.m_end_of_line = true; + } else if (has_signal_along) { + SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir); + // cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is + 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; + if (sig_state != SIGNAL_STATE_RED) { + // green signal + n.flags_u.flags_s.m_last_signal_was_red = false; + // negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal + if (look_ahead_cost < 0) { + // add its negation to the cost + cost -= look_ahead_cost; + } + } else { + // we have a red signal in our direction + // was it first signal which is two-way? + if (Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) { + // yes, the first signal is two-way red signal => DEAD END + n.m_segment->flags_u.flags_s.m_end_of_line = true; + Yapf().m_stopped_on_first_two_way_signal = true; + return -1; + } + SignalType sig_type = GetSignalType(tile); + n.m_last_red_signal_type = sig_type; + n.flags_u.flags_s.m_last_signal_was_red = true; + + // look-ahead signal penalty + if (look_ahead_cost > 0) { + // add the look ahead penalty only if it is positive + cost += look_ahead_cost; + } + + // special signal penalties + if (n.m_num_signals_passed == 0) { + switch (sig_type) { + case SIGTYPE_COMBO: + case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit + case SIGTYPE_NORMAL: + case SIGTYPE_ENTRY: cost += Yapf().PfGetSettings().rail_firstred_penalty; break; + }; + } + } + n.m_num_signals_passed++; + n.m_segment->m_last_signal_tile = tile; + n.m_segment->m_last_signal_td = trackdir; + } + } + return cost; + } + + FORCEINLINE int PlatformLengthPenalty(int platform_length) + { + int cost = 0; + const Vehicle* v = Yapf().GetVehicle(); + assert(v != NULL); + assert(v->type == VEH_Train); + assert(v->u.rail.cached_total_length != 0); + int needed_platform_length = (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE; + if (platform_length > needed_platform_length) { + // apply penalty for longer platform than needed + cost += Yapf().PfGetSettings().rail_longer_platform_penalty; + } else if (needed_platform_length > platform_length) { + // apply penalty for shorter platform than needed + cost += Yapf().PfGetSettings().rail_shorter_platform_penalty; + } + return cost; + } + +public: + FORCEINLINE void SetMaxCost(int max_cost) {m_max_cost = max_cost;} + + /** Called by YAPF to calculate the cost from the origin to the given node. + * Calculates only the cost of given node, adds it to the parent node cost + * and stores the result into Node::m_cost member */ + FORCEINLINE bool PfCalcCost(Node& n) + { + assert(!n.flags_u.flags_s.m_targed_seen); + CPerfStart perf_cost(Yapf().m_perf_cost); + int parent_cost = (n.m_parent != NULL) ? n.m_parent->m_cost : 0; + int first_tile_cost = 0; + int segment_cost = 0; + int extra_cost = 0; + const Vehicle* v = Yapf().GetVehicle(); + + // start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment + TileIndex prev_tile = (n.m_parent != NULL) ? n.m_parent->GetLastTile() : INVALID_TILE; + Trackdir prev_trackdir = (n.m_parent != NULL) ? n.m_parent->GetLastTrackdir() : INVALID_TRACKDIR; + TileType prev_tile_type = (n.m_parent != NULL) ? GetTileType(n.m_parent->GetLastTile()) : MP_VOID; + + TileIndex tile = n.m_key.m_tile; + Trackdir trackdir = n.m_key.m_td; + TileType tile_type = GetTileType(tile); + + RailType rail_type = GetTileRailType(tile, trackdir); + + bool target_seen = Yapf().PfDetectDestination(tile, trackdir); + + while (true) { + segment_cost += Yapf().OneTileCost(tile, trackdir); + segment_cost += Yapf().CurveCost(prev_trackdir, trackdir); + segment_cost += Yapf().SlopeCost(tile, trackdir); + segment_cost += Yapf().SignalCost(n, tile, trackdir); + if (n.m_segment->flags_u.flags_s.m_end_of_line) { + break; + } + + // finish if we have reached the destination + if (target_seen) { + break; + } + + // finish on first station tile - segment should end here to avoid target skipping + // when cached segments are used + if (tile_type == MP_STATION && prev_tile_type != MP_STATION) { + break; + } + + // finish also on waypoint - same workaround as for first station tile + if (tile_type == MP_RAILWAY && IsRailWaypoint(tile)) { + break; + } + + // if there are no reachable trackdirs on the next tile, we have end of road + TrackFollower F(v, &Yapf().m_perf_ts_cost); + if (!F.Follow(tile, trackdir)) { + // we can't continue? + // n.m_segment->flags_u.flags_s.m_end_of_line = true; + break; + } + + // if there are more trackdirs available & reachable, we are at the end of segment + if (KillFirstBit2x64(F.m_new_td_bits) != 0) { + break; + } + + Trackdir new_td = (Trackdir)FindFirstBit2x64(F.m_new_td_bits); + + { + // end segment if train is about to enter simple loop with no junctions + // so next time it should stop on the next if + if (segment_cost > s_max_segment_cost && IsTileType(F.m_new_tile, MP_RAILWAY)) + break; + + // stop if train is on simple loop with no junctions + if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) + return false; + } + + // if tail type changes, finish segment (cached segment can't contain more rail types) + { + RailType new_rail_type = GetTileRailType(F.m_new_tile, (Trackdir)FindFirstBit2x64(F.m_new_td_bits)); + if (new_rail_type != rail_type) { + break; + } + rail_type = new_rail_type; + } + + // move to the next tile + prev_tile = tile; + prev_trackdir = trackdir; + prev_tile_type = tile_type; + + tile = F.m_new_tile; + trackdir = new_td; + tile_type = GetTileType(tile); + + target_seen = Yapf().PfDetectDestination(tile, trackdir); + + // reversing in depot penalty + if (tile == prev_tile) { + segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty; + break; + } + + // if we skipped some tunnel tiles, add their cost + segment_cost += YAPF_TILE_LENGTH * F.m_tiles_skipped; + + // add penalty for skipped station tiles + if (F.m_is_station) + { + if (target_seen) { + // it is our destination station + uint platform_length = F.m_tiles_skipped + 1; + segment_cost += PlatformLengthPenalty(platform_length); + } else { + // station is not our destination station, apply penalty for skipped platform tiles + segment_cost += Yapf().PfGetSettings().rail_station_penalty * F.m_tiles_skipped; + } + } + + // add min/max speed penalties + int min_speed = 0; + int max_speed = F.GetSpeedLimit(&min_speed); + if (max_speed < v->max_speed) + segment_cost += YAPF_TILE_LENGTH * (v->max_speed - max_speed) / v->max_speed; + if (min_speed > v->max_speed) + segment_cost += YAPF_TILE_LENGTH * (min_speed - v->max_speed); + + // finish if we already exceeded the maximum cost + if (m_max_cost > 0 && (parent_cost + first_tile_cost + segment_cost) > m_max_cost) { + return false; + } + + if (first_tile_cost == 0) { + // we just have done first tile + first_tile_cost = segment_cost; + segment_cost = 0; + + // look if we can reuse existing (cached) segment cost + if (n.m_segment->m_cost >= 0) { + // reuse the cached segment cost + break; + } + } + // segment cost was not filled yes, we have not cached it yet + n.SetLastTileTrackdir(tile, trackdir); + + } // while (true) + + if (first_tile_cost == 0) { + // we have just finished first tile + first_tile_cost = segment_cost; + segment_cost = 0; + } + + // do we have cached segment cost? + if (n.m_segment->m_cost >= 0) { + // reuse the cached segment cost + segment_cost = n.m_segment->m_cost; + } else { + // save segment cost + n.m_segment->m_cost = segment_cost; + + // save end of segment back to the node + n.SetLastTileTrackdir(tile, trackdir); + } + + // special costs for the case we have reached our target + if (target_seen) { + n.flags_u.flags_s.m_targed_seen = true; + if (n.flags_u.flags_s.m_last_signal_was_red) { + if (n.m_last_red_signal_type == SIGTYPE_EXIT) { + // last signal was red pre-signal-exit + extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty; + } else { + // last signal was red, but not exit + extra_cost += Yapf().PfGetSettings().rail_lastred_penalty; + } + } + } + + // total node cost + n.m_cost = parent_cost + first_tile_cost + segment_cost + extra_cost; + + return !n.m_segment->flags_u.flags_s.m_end_of_line; + } + + FORCEINLINE bool CanUseGlobalCache(Node& n) const + { + return (n.m_parent != NULL) + && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.Size()); + } + + FORCEINLINE void ConnectNodeToCachedData(Node& n, CachedData& ci) + { + n.m_segment = &ci; + if (n.m_segment->m_cost < 0) { + n.m_segment->m_last_tile = n.m_key.m_tile; + n.m_segment->m_last_td = n.m_key.m_td; + } + } + +}; + + + +#endif /* YAPF_COSTRAIL_HPP */ |