yapf/yapf_road.cpp
changeset 3900 4984308f9125
child 3914 f5118020cd84
equal deleted inserted replaced
3899:4c5b1de6cb17 3900:4984308f9125
       
     1 /* $Id$ */
       
     2 
       
     3 #include "../stdafx.h"
       
     4 
       
     5 #include "yapf.hpp"
       
     6 #include "yapf_node_road.hpp"
       
     7 
       
     8 
       
     9 template <class Types>
       
    10 class CYapfCostRoadT
       
    11 {
       
    12 public:
       
    13 	typedef typename Types::Tpf Tpf;
       
    14 	typedef typename Types::TrackFollower TrackFollower;
       
    15 	typedef typename Types::NodeList::Titem Node; ///< this will be our node type
       
    16 	typedef typename Node::Key Key;    ///< key to hash tables
       
    17 
       
    18 protected:
       
    19 	Tpf& Yapf() {return *static_cast<Tpf*>(this);}
       
    20 
       
    21 	int SlopeCost(TileIndex tile, TileIndex next_tile, Trackdir trackdir)
       
    22 	{
       
    23 		// height of the center of the current tile
       
    24 		int x1 = TileX(tile) * TILE_SIZE;
       
    25 		int y1 = TileY(tile) * TILE_SIZE;
       
    26 		int z1 = GetSlopeZ(x1 + TILE_HEIGHT, y1 + TILE_HEIGHT);
       
    27 
       
    28 		// height of the center of the next tile
       
    29 		int x2 = TileX(next_tile) * TILE_SIZE;
       
    30 		int y2 = TileY(next_tile) * TILE_SIZE;
       
    31 		int z2 = GetSlopeZ(x2 + TILE_HEIGHT, y2 + TILE_HEIGHT);
       
    32 
       
    33 		if (z2 - z1 > 1) {
       
    34 			/* Slope up */
       
    35 			return Yapf().PfGetSettings().rail_slope_penalty;
       
    36 		}
       
    37 		return 0;
       
    38 	}
       
    39 
       
    40 	/** return one tile cost */
       
    41 	FORCEINLINE int OneTileCost(TileIndex tile, Trackdir trackdir)
       
    42 	{
       
    43 		int cost = 0;
       
    44 		// set base cost
       
    45 		if (IsDiagonalTrackdir(trackdir)) {
       
    46 			cost += 10;
       
    47 			switch (GetTileType(tile)) {
       
    48 				case MP_STREET:
       
    49 					/* Increase the cost for level crossings */
       
    50 					if (IsLevelCrossing(tile))
       
    51 						cost += Yapf().PfGetSettings().rail_crossing_penalty;
       
    52 					break;
       
    53 
       
    54 				default:
       
    55 					break;
       
    56 			}
       
    57 		} else {
       
    58 			// non-diagonal trackdir
       
    59 			cost = 7;
       
    60 		}
       
    61 		return cost;
       
    62 	}
       
    63 
       
    64 public:
       
    65 	FORCEINLINE bool PfCalcCost(Node& n)
       
    66 	{
       
    67 		int segment_cost = 0;
       
    68 		// start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment
       
    69 		TileIndex tile = n.m_key.m_tile;
       
    70 		Trackdir trackdir = n.m_key.m_td;
       
    71 		while (true) {
       
    72 			// base tile cost depending on distance between edges
       
    73 			segment_cost += Yapf().OneTileCost(tile, trackdir);
       
    74 
       
    75 			// if there are no reachable trackdirs n new tile, we have end of road
       
    76 			TrackFollower F;
       
    77 			if (!F.Follow(tile, trackdir)) break;
       
    78 
       
    79 			// if there are more trackdirs available & reachable, we are at the end of segment
       
    80 			if (KillFirstBit2x64(F.m_new_td_bits) != 0) break;
       
    81 
       
    82 			Trackdir new_td = (Trackdir)FindFirstBit2x64(F.m_new_td_bits);
       
    83 
       
    84 			// stop if RV is on simple loop with no junctions
       
    85 			if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) return false;
       
    86 
       
    87 			// if we skipped some tunnel tiles, add their cost
       
    88 			segment_cost += 10 * F.m_tunnel_tiles_skipped;
       
    89 
       
    90 			// add hilly terrain penalty
       
    91 			segment_cost += Yapf().SlopeCost(tile, F.m_new_tile, trackdir);
       
    92 
       
    93 			// add min/max speed penalties
       
    94 			int min_speed = 0;
       
    95 			int max_speed = F.GetSpeedLimit(&min_speed);
       
    96 			Vehicle* v = Yapf().GetVehicle();
       
    97 			if (max_speed < v->max_speed) segment_cost += 1 * (v->max_speed - max_speed);
       
    98 			if (min_speed > v->max_speed) segment_cost += 10 * (min_speed - v->max_speed);
       
    99 
       
   100 			// move to the next tile
       
   101 			tile = F.m_new_tile;
       
   102 			trackdir = new_td;
       
   103 		};
       
   104 
       
   105 		// save end of segment back to the node
       
   106 		n.m_segment_last_tile = tile;
       
   107 		n.m_segment_last_td = trackdir;
       
   108 
       
   109 		// save also tile cost
       
   110 		int parent_cost = (n.m_parent != NULL) ? n.m_parent->m_cost : 0;
       
   111 		n.m_cost = parent_cost + segment_cost;
       
   112 		return true;
       
   113 	}
       
   114 };
       
   115 
       
   116 
       
   117 template <class Types>
       
   118 class CYapfDestinationAnyDepotRoadT
       
   119 {
       
   120 public:
       
   121 	typedef typename Types::Tpf Tpf;
       
   122 	typedef typename Types::TrackFollower TrackFollower;
       
   123 	typedef typename Types::NodeList::Titem Node; ///< this will be our node type
       
   124 	typedef typename Node::Key Key;    ///< key to hash tables
       
   125 
       
   126 	Tpf& Yapf() {return *static_cast<Tpf*>(this);}
       
   127 
       
   128 	FORCEINLINE bool PfDetectDestination(Node& n)
       
   129 	{
       
   130 		bool bDest = IsTileDepotType(n.m_segment_last_tile, TRANSPORT_ROAD);
       
   131 		return bDest;
       
   132 	}
       
   133 
       
   134 	FORCEINLINE bool PfCalcEstimate(Node& n)
       
   135 	{
       
   136 		n.m_estimate = n.m_cost;
       
   137 		return true;
       
   138 	}
       
   139 };
       
   140 
       
   141 
       
   142 template <class Types>
       
   143 class CYapfDestinationTileRoadT
       
   144 {
       
   145 public:
       
   146 	typedef typename Types::Tpf Tpf;
       
   147 	typedef typename Types::TrackFollower TrackFollower;
       
   148 	typedef typename Types::NodeList::Titem Node; ///< this will be our node type
       
   149 	typedef typename Node::Key Key;    ///< key to hash tables
       
   150 
       
   151 protected:
       
   152 	TileIndex    m_destTile;
       
   153 	TrackdirBits m_destTrackdirs;
       
   154 
       
   155 public:
       
   156 	void SetDestination(TileIndex tile, TrackdirBits trackdirs)
       
   157 	{
       
   158 		m_destTile = tile;
       
   159 		m_destTrackdirs = trackdirs;
       
   160 	}
       
   161 
       
   162 protected:
       
   163 	Tpf& Yapf() {return *static_cast<Tpf*>(this);}
       
   164 
       
   165 public:
       
   166 	FORCEINLINE bool PfDetectDestination(Node& n)
       
   167 	{
       
   168 		bool bDest = (n.m_segment_last_tile == m_destTile) && ((m_destTrackdirs & TrackdirToTrackdirBits(n.m_segment_last_td)) != TRACKDIR_BIT_NONE);
       
   169 		return bDest;
       
   170 	}
       
   171 
       
   172 	inline bool PfCalcEstimate(Node& n)
       
   173 	{
       
   174 		static int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
       
   175 		static int dg_dir_to_y_offs[] = {0, 1, 0, -1};
       
   176 		if (PfDetectDestination(n)) {
       
   177 			n.m_estimate = n.m_cost;
       
   178 			return true;
       
   179 		}
       
   180 
       
   181 		TileIndex tile = n.m_segment_last_tile;
       
   182 		DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
       
   183 		int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
       
   184 		int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
       
   185 		int x2 = 2 * TileX(m_destTile);
       
   186 		int y2 = 2 * TileY(m_destTile);
       
   187 		int dx = abs(x1 - x2);
       
   188 		int dy = abs(y1 - y2);
       
   189 		int dmin = min(dx, dy);
       
   190 		int dxy = abs(dx - dy);
       
   191 		int d = dmin * 7 + (dxy - 1) * 5;
       
   192 		n.m_estimate = n.m_cost + d;
       
   193 		assert(n.m_estimate >= n.m_parent->m_estimate);
       
   194 		return true;
       
   195 	}
       
   196 };
       
   197 
       
   198 
       
   199 
       
   200 template <class Types>
       
   201 class CYapfFollowRoadT
       
   202 {
       
   203 public:
       
   204 	typedef typename Types::Tpf Tpf;
       
   205 	typedef typename Types::TrackFollower TrackFollower;
       
   206 	typedef typename Types::NodeList::Titem Node; ///< this will be our node type
       
   207 	typedef typename Node::Key Key;    ///< key to hash tables
       
   208 
       
   209 protected:
       
   210 	FORCEINLINE Tpf& Yapf() {return *static_cast<Tpf*>(this);}
       
   211 
       
   212 public:
       
   213 
       
   214 	inline void PfFollowNode(Node& old_node)
       
   215 	{
       
   216 		TrackFollower F;
       
   217 		if (F.Follow(old_node.m_segment_last_tile, old_node.m_segment_last_td))
       
   218 			Yapf().AddMultipleNodes(&old_node, F.m_new_tile, F.m_new_td_bits);
       
   219 	}
       
   220 
       
   221 	FORCEINLINE char TransportTypeChar() const {return 'r';}
       
   222 
       
   223 	static Trackdir stChooseRoadTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir)
       
   224 	{
       
   225 		Tpf pf;
       
   226 		return pf.ChooseRoadTrack(v, tile, enterdir);
       
   227 	}
       
   228 
       
   229 	FORCEINLINE Trackdir ChooseRoadTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir)
       
   230 	{
       
   231 		// handle special case - when next tile is destination tile
       
   232 		if (tile == v->dest_tile) {
       
   233 			// choose diagonal trackdir reachable from enterdir
       
   234 			return (Trackdir)DiagdirToDiagTrackdir(enterdir);
       
   235 		}
       
   236 		// our source tile will be the next vehicle tile (should be the given one)
       
   237 		TileIndex src_tile = tile;
       
   238 		// get available trackdirs on the start tile
       
   239 		uint ts = GetTileTrackStatus(tile, TRANSPORT_ROAD);
       
   240 		TrackdirBits src_trackdirs = (TrackdirBits)(ts & TRACKDIR_BIT_MASK);
       
   241 		// select reachable trackdirs only
       
   242 		src_trackdirs &= DiagdirReachesTrackdirs(enterdir);
       
   243 
       
   244 		// get available trackdirs on the destination tile
       
   245 		TileIndex dest_tile = v->dest_tile;
       
   246 		uint dest_ts = GetTileTrackStatus(dest_tile, TRANSPORT_ROAD);
       
   247 		TrackdirBits dest_trackdirs = (TrackdirBits)(dest_ts & TRACKDIR_BIT_MASK);
       
   248 
       
   249 		// set origin and destination nodes
       
   250 		Yapf().SetOrigin(src_tile, src_trackdirs);
       
   251 		Yapf().SetDestination(dest_tile, dest_trackdirs);
       
   252 
       
   253 		// find the best path
       
   254 		Yapf().FindPath(v);
       
   255 
       
   256 		// if path not found - return INVALID_TRACKDIR
       
   257 		Trackdir next_trackdir = INVALID_TRACKDIR;
       
   258 		Node* pNode = &Yapf().GetBestNode();
       
   259 		if (pNode != NULL) {
       
   260 			// path was found or at least suggested
       
   261 			// walk through the path back to its origin
       
   262 			while (pNode->m_parent != NULL) {
       
   263 				pNode = pNode->m_parent;
       
   264 			}
       
   265 			// return trackdir from the best origin node (one of start nodes)
       
   266 			Node& best_next_node = *pNode;
       
   267 			assert(best_next_node.GetTile() == tile);
       
   268 			next_trackdir = best_next_node.GetTrackdir();
       
   269 		}
       
   270 		return next_trackdir;
       
   271 	}
       
   272 
       
   273 	static Depot* stFindNearestDepot(Vehicle* v, TileIndex tile, Trackdir td)
       
   274 	{
       
   275 		Tpf pf;
       
   276 		return pf.FindNearestDepot(v, tile, td);
       
   277 	}
       
   278 
       
   279 	FORCEINLINE Depot* FindNearestDepot(Vehicle* v, TileIndex tile, Trackdir td)
       
   280 	{
       
   281 		// set origin and destination nodes
       
   282 		Yapf().SetOrigin(tile, TrackdirToTrackdirBits(td));
       
   283 
       
   284 		// find the best path
       
   285 		bool bFound = Yapf().FindPath(v);
       
   286 		if (!bFound) return false;
       
   287 
       
   288 		// some path found
       
   289 		// get found depot tile
       
   290 		Node& n = Yapf().GetBestNode();
       
   291 		TileIndex depot_tile = n.m_segment_last_tile;
       
   292 		assert(IsTileDepotType(depot_tile, TRANSPORT_ROAD));
       
   293 		Depot* ret = GetDepotByTile(depot_tile);
       
   294 		return ret;
       
   295 	}
       
   296 };
       
   297 
       
   298 template <class Tpf_, class Tnode_list, template <class Types> class Tdestination>
       
   299 struct CYapfRoad_TypesT
       
   300 {
       
   301 	typedef CYapfRoad_TypesT<Tpf_, Tnode_list, Tdestination>  Types;
       
   302 
       
   303 	typedef Tpf_                              Tpf;
       
   304 	typedef CFollowTrackRoad                  TrackFollower;
       
   305 	typedef Tnode_list                        NodeList;
       
   306 	typedef CYapfBaseT<Types>                 PfBase;
       
   307 	typedef CYapfFollowRoadT<Types>           PfFollow;
       
   308 	typedef CYapfOriginTileT<Types>           PfOrigin;
       
   309 	typedef Tdestination<Types>               PfDestination;
       
   310 	typedef CYapfSegmentCostCacheNoneT<Types> PfCache;
       
   311 	typedef CYapfCostRoadT<Types>             PfCost;
       
   312 };
       
   313 
       
   314 struct CYapfRoad1         : CYapfT<CYapfRoad_TypesT<CYapfRoad1        , CRoadNodeListTrackDir, CYapfDestinationTileRoadT    > > {};
       
   315 struct CYapfRoad2         : CYapfT<CYapfRoad_TypesT<CYapfRoad2        , CRoadNodeListExitDir , CYapfDestinationTileRoadT    > > {};
       
   316 
       
   317 struct CYapfRoadAnyDepot1 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot1, CRoadNodeListTrackDir, CYapfDestinationAnyDepotRoadT> > {};
       
   318 struct CYapfRoadAnyDepot2 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot2, CRoadNodeListExitDir , CYapfDestinationAnyDepotRoadT> > {};
       
   319 
       
   320 
       
   321 Trackdir YapfChooseRoadTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir)
       
   322 {
       
   323 	// default is YAPF type 2
       
   324 	typedef Trackdir (*PfnChooseRoadTrack)(Vehicle*, TileIndex, DiagDirection);
       
   325 	PfnChooseRoadTrack pfnChooseRoadTrack = &CYapfRoad2::stChooseRoadTrack; // default: ExitDir, allow 90-deg
       
   326 
       
   327 	// check if non-default YAPF type should be used
       
   328 	if (_patches.yapf.disable_node_optimization)
       
   329 		pfnChooseRoadTrack = &CYapfRoad1::stChooseRoadTrack; // Trackdir, allow 90-deg
       
   330 
       
   331 	Trackdir td_ret = pfnChooseRoadTrack(v, tile, enterdir);
       
   332 	return td_ret;
       
   333 }
       
   334 
       
   335 Depot* YapfFindNearestRoadDepot(const Vehicle *v)
       
   336 {
       
   337 	TileIndex tile = v->tile;
       
   338 	if (v->u.road.state == 255) tile = GetVehicleOutOfTunnelTile(v);
       
   339 	Trackdir trackdir = GetVehicleTrackdir(v);
       
   340 	if ((GetTileTrackStatus(tile, TRANSPORT_ROAD) & TrackdirToTrackdirBits(trackdir)) == 0)
       
   341 		return NULL;
       
   342 
       
   343 	// default is YAPF type 2
       
   344 	typedef Depot* (*PfnFindNearestDepot)(Vehicle*, TileIndex, Trackdir);
       
   345 	PfnFindNearestDepot pfnFindNearestDepot = &CYapfRoadAnyDepot2::stFindNearestDepot;
       
   346 
       
   347 	// check if non-default YAPF type should be used
       
   348 	if (_patches.yapf.disable_node_optimization)
       
   349 		pfnFindNearestDepot = &CYapfRoadAnyDepot1::stFindNearestDepot; // Trackdir, allow 90-deg
       
   350 
       
   351 	Depot* ret = pfnFindNearestDepot(const_cast<Vehicle*>(v), tile, trackdir);
       
   352 	return ret;
       
   353 }