OpenTTD
yapf_road.cpp
Go to the documentation of this file.
1 /* $Id: yapf_road.cpp 27362 2015-08-08 10:06:24Z alberth $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #include "../../stdafx.h"
13 #include "yapf.hpp"
14 #include "yapf_node_road.hpp"
15 #include "../../roadstop_base.h"
16 
17 #include "../../safeguards.h"
18 
19 
20 template <class Types>
22 {
23 public:
24  typedef typename Types::Tpf Tpf;
25  typedef typename Types::TrackFollower TrackFollower;
26  typedef typename Types::NodeList::Titem Node;
27  typedef typename Node::Key Key;
28 
29 protected:
31  Tpf& Yapf()
32  {
33  return *static_cast<Tpf *>(this);
34  }
35 
36  int SlopeCost(TileIndex tile, TileIndex next_tile, Trackdir trackdir)
37  {
38  /* height of the center of the current tile */
39  int x1 = TileX(tile) * TILE_SIZE;
40  int y1 = TileY(tile) * TILE_SIZE;
41  int z1 = GetSlopePixelZ(x1 + TILE_SIZE / 2, y1 + TILE_SIZE / 2);
42 
43  /* height of the center of the next tile */
44  int x2 = TileX(next_tile) * TILE_SIZE;
45  int y2 = TileY(next_tile) * TILE_SIZE;
46  int z2 = GetSlopePixelZ(x2 + TILE_SIZE / 2, y2 + TILE_SIZE / 2);
47 
48  if (z2 - z1 > 1) {
49  /* Slope up */
50  return Yapf().PfGetSettings().road_slope_penalty;
51  }
52  return 0;
53  }
54 
56  inline int OneTileCost(TileIndex tile, Trackdir trackdir)
57  {
58  int cost = 0;
59  /* set base cost */
60  if (IsDiagonalTrackdir(trackdir)) {
61  cost += YAPF_TILE_LENGTH;
62  switch (GetTileType(tile)) {
63  case MP_ROAD:
64  /* Increase the cost for level crossings */
65  if (IsLevelCrossing(tile)) {
66  cost += Yapf().PfGetSettings().road_crossing_penalty;
67  }
68  break;
69 
70  case MP_STATION: {
71  const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
72  if (IsDriveThroughStopTile(tile)) {
73  /* Increase the cost for drive-through road stops */
74  cost += Yapf().PfGetSettings().road_stop_penalty;
75  DiagDirection dir = TrackdirToExitdir(trackdir);
77  /* When we're the first road stop in a 'queue' of them we increase
78  * cost based on the fill percentage of the whole queue. */
79  const RoadStop::Entry *entry = rs->GetEntry(dir);
80  cost += entry->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength();
81  }
82  } else {
83  /* Increase cost for filled road stops */
84  cost += Yapf().PfGetSettings().road_stop_bay_occupied_penalty * (!rs->IsFreeBay(0) + !rs->IsFreeBay(1)) / 2;
85  }
86  break;
87  }
88 
89  default:
90  break;
91  }
92  } else {
93  /* non-diagonal trackdir */
94  cost = YAPF_TILE_CORNER_LENGTH + Yapf().PfGetSettings().road_curve_penalty;
95  }
96  return cost;
97  }
98 
99 public:
105  inline bool PfCalcCost(Node &n, const TrackFollower *tf)
106  {
107  int segment_cost = 0;
108  uint tiles = 0;
109  /* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */
110  TileIndex tile = n.m_key.m_tile;
111  Trackdir trackdir = n.m_key.m_td;
112  for (;;) {
113  /* base tile cost depending on distance between edges */
114  segment_cost += Yapf().OneTileCost(tile, trackdir);
115 
116  const RoadVehicle *v = Yapf().GetVehicle();
117  /* we have reached the vehicle's destination - segment should end here to avoid target skipping */
118  if (Yapf().PfDetectDestinationTile(tile, trackdir)) break;
119 
120  /* stop if we have just entered the depot */
122  /* next time we will reverse and leave the depot */
123  break;
124  }
125 
126  /* if there are no reachable trackdirs on new tile, we have end of road */
127  TrackFollower F(Yapf().GetVehicle());
128  if (!F.Follow(tile, trackdir)) break;
129 
130  /* if there are more trackdirs available & reachable, we are at the end of segment */
131  if (KillFirstBit(F.m_new_td_bits) != TRACKDIR_BIT_NONE) break;
132 
133  Trackdir new_td = (Trackdir)FindFirstBit2x64(F.m_new_td_bits);
134 
135  /* stop if RV is on simple loop with no junctions */
136  if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) return false;
137 
138  /* if we skipped some tunnel tiles, add their cost */
139  segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH;
140  tiles += F.m_tiles_skipped + 1;
141 
142  /* add hilly terrain penalty */
143  segment_cost += Yapf().SlopeCost(tile, F.m_new_tile, trackdir);
144 
145  /* add min/max speed penalties */
146  int min_speed = 0;
147  int max_veh_speed = v->GetDisplayMaxSpeed();
148  int max_speed = F.GetSpeedLimit(&min_speed);
149  if (max_speed < max_veh_speed) segment_cost += 1 * (max_veh_speed - max_speed);
150  if (min_speed > max_veh_speed) segment_cost += 10 * (min_speed - max_veh_speed);
151 
152  /* move to the next tile */
153  tile = F.m_new_tile;
154  trackdir = new_td;
155  if (tiles > MAX_MAP_SIZE) break;
156  }
157 
158  /* save end of segment back to the node */
159  n.m_segment_last_tile = tile;
160  n.m_segment_last_td = trackdir;
161 
162  /* save also tile cost */
163  int parent_cost = (n.m_parent != NULL) ? n.m_parent->m_cost : 0;
164  n.m_cost = parent_cost + segment_cost;
165  return true;
166  }
167 };
168 
169 
170 template <class Types>
172 {
173 public:
174  typedef typename Types::Tpf Tpf;
175  typedef typename Types::TrackFollower TrackFollower;
176  typedef typename Types::NodeList::Titem Node;
177  typedef typename Node::Key Key;
178 
181  {
182  return *static_cast<Tpf *>(this);
183  }
184 
186  inline bool PfDetectDestination(Node &n)
187  {
188  bool bDest = IsRoadDepotTile(n.m_segment_last_tile);
189  return bDest;
190  }
191 
192  inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
193  {
194  return IsRoadDepotTile(tile);
195  }
196 
201  inline bool PfCalcEstimate(Node &n)
202  {
203  n.m_estimate = n.m_cost;
204  return true;
205  }
206 };
207 
208 
209 template <class Types>
211 {
212 public:
213  typedef typename Types::Tpf Tpf;
214  typedef typename Types::TrackFollower TrackFollower;
215  typedef typename Types::NodeList::Titem Node;
216  typedef typename Node::Key Key;
217 
218 protected:
219  TileIndex m_destTile;
220  TrackdirBits m_destTrackdirs;
221  StationID m_dest_station;
222  bool m_bus;
223  bool m_non_artic;
224 
225 public:
226  void SetDestination(const RoadVehicle *v)
227  {
228  if (v->current_order.IsType(OT_GOTO_STATION)) {
229  m_dest_station = v->current_order.GetDestination();
230  m_bus = v->IsBus();
231  m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_bus ? STATION_BUS : STATION_TRUCK);
232  m_non_artic = !v->HasArticulatedPart();
233  m_destTrackdirs = INVALID_TRACKDIR_BIT;
234  } else {
235  m_dest_station = INVALID_STATION;
236  m_destTile = v->dest_tile;
237  m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, v->compatible_roadtypes));
238  }
239  }
240 
241 protected:
244  {
245  return *static_cast<Tpf *>(this);
246  }
247 
248 public:
250  inline bool PfDetectDestination(Node &n)
251  {
252  return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td);
253  }
254 
255  inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
256  {
257  if (m_dest_station != INVALID_STATION) {
258  return IsTileType(tile, MP_STATION) &&
259  GetStationIndex(tile) == m_dest_station &&
260  (m_bus ? IsBusStop(tile) : IsTruckStop(tile)) &&
261  (m_non_artic || IsDriveThroughStopTile(tile));
262  }
263 
264  return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
265  }
266 
271  inline bool PfCalcEstimate(Node &n)
272  {
273  static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
274  static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
275  if (PfDetectDestination(n)) {
276  n.m_estimate = n.m_cost;
277  return true;
278  }
279 
280  TileIndex tile = n.m_segment_last_tile;
281  DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
282  int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
283  int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
284  int x2 = 2 * TileX(m_destTile);
285  int y2 = 2 * TileY(m_destTile);
286  int dx = abs(x1 - x2);
287  int dy = abs(y1 - y2);
288  int dmin = min(dx, dy);
289  int dxy = abs(dx - dy);
290  int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
291  n.m_estimate = n.m_cost + d;
292  assert(n.m_estimate >= n.m_parent->m_estimate);
293  return true;
294  }
295 };
296 
297 
298 
299 template <class Types>
301 {
302 public:
303  typedef typename Types::Tpf Tpf;
304  typedef typename Types::TrackFollower TrackFollower;
305  typedef typename Types::NodeList::Titem Node;
306  typedef typename Node::Key Key;
307 
308 protected:
310  inline Tpf& Yapf()
311  {
312  return *static_cast<Tpf *>(this);
313  }
314 
315 public:
316 
322  inline void PfFollowNode(Node &old_node)
323  {
324  TrackFollower F(Yapf().GetVehicle());
325  if (F.Follow(old_node.m_segment_last_tile, old_node.m_segment_last_td)) {
326  Yapf().AddMultipleNodes(&old_node, F);
327  }
328  }
329 
331  inline char TransportTypeChar() const
332  {
333  return 'r';
334  }
335 
336  static Trackdir stChooseRoadTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, bool &path_found)
337  {
338  Tpf pf;
339  return pf.ChooseRoadTrack(v, tile, enterdir, path_found);
340  }
341 
342  inline Trackdir ChooseRoadTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, bool &path_found)
343  {
344  /* Handle special case - when next tile is destination tile.
345  * However, when going to a station the (initial) destination
346  * tile might not be a station, but a junction, in which case
347  * this method forces the vehicle to jump in circles. */
348  if (tile == v->dest_tile && !v->current_order.IsType(OT_GOTO_STATION)) {
349  /* choose diagonal trackdir reachable from enterdir */
350  return DiagDirToDiagTrackdir(enterdir);
351  }
352  /* our source tile will be the next vehicle tile (should be the given one) */
353  TileIndex src_tile = tile;
354  /* get available trackdirs on the start tile */
355  TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes));
356  /* select reachable trackdirs only */
357  src_trackdirs &= DiagdirReachesTrackdirs(enterdir);
358 
359  /* set origin and destination nodes */
360  Yapf().SetOrigin(src_tile, src_trackdirs);
361  Yapf().SetDestination(v);
362 
363  /* find the best path */
364  path_found = Yapf().FindPath(v);
365 
366  /* if path not found - return INVALID_TRACKDIR */
367  Trackdir next_trackdir = INVALID_TRACKDIR;
368  Node *pNode = Yapf().GetBestNode();
369  if (pNode != NULL) {
370  /* path was found or at least suggested
371  * walk through the path back to its origin */
372  while (pNode->m_parent != NULL) {
373  pNode = pNode->m_parent;
374  }
375  /* return trackdir from the best origin node (one of start nodes) */
376  Node &best_next_node = *pNode;
377  assert(best_next_node.GetTile() == tile);
378  next_trackdir = best_next_node.GetTrackdir();
379  }
380  return next_trackdir;
381  }
382 
383  static uint stDistanceToTile(const RoadVehicle *v, TileIndex tile)
384  {
385  Tpf pf;
386  return pf.DistanceToTile(v, tile);
387  }
388 
389  inline uint DistanceToTile(const RoadVehicle *v, TileIndex dst_tile)
390  {
391  /* handle special case - when current tile is the destination tile */
392  if (dst_tile == v->tile) {
393  /* distance is zero in this case */
394  return 0;
395  }
396 
397  if (!SetOriginFromVehiclePos(v)) return UINT_MAX;
398 
399  /* get available trackdirs on the destination tile */
400  Yapf().SetDestination(v);
401 
402  /* if path not found - return distance = UINT_MAX */
403  uint dist = UINT_MAX;
404 
405  /* find the best path */
406  if (!Yapf().FindPath(v)) return dist;
407 
408  Node *pNode = Yapf().GetBestNode();
409  if (pNode != NULL) {
410  /* path was found
411  * get the path cost estimate */
412  dist = pNode->GetCostEstimate();
413  }
414 
415  return dist;
416  }
417 
419  inline bool SetOriginFromVehiclePos(const RoadVehicle *v)
420  {
421  /* set origin (tile, trackdir) */
422  TileIndex src_tile = v->tile;
423  Trackdir src_td = v->GetVehicleTrackdir();
424  if ((TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(src_td)) == 0) {
425  /* sometimes the roadveh is not on the road (it resides on non-existing track)
426  * how should we handle that situation? */
427  return false;
428  }
429  Yapf().SetOrigin(src_tile, TrackdirToTrackdirBits(src_td));
430  return true;
431  }
432 
433  static bool stFindNearestDepot(const RoadVehicle *v, TileIndex tile, Trackdir td, int max_distance, TileIndex *depot_tile)
434  {
435  Tpf pf;
436  return pf.FindNearestDepot(v, tile, td, max_distance, depot_tile);
437  }
438 
439  inline bool FindNearestDepot(const RoadVehicle *v, TileIndex tile, Trackdir td, int max_distance, TileIndex *depot_tile)
440  {
441  /* set origin and destination nodes */
442  Yapf().SetOrigin(tile, TrackdirToTrackdirBits(td));
443 
444  /* find the best path */
445  bool bFound = Yapf().FindPath(v);
446  if (!bFound) return false;
447 
448  /* some path found
449  * get found depot tile */
450  Node *n = Yapf().GetBestNode();
451 
452  if (max_distance > 0 && n->m_cost > max_distance * YAPF_TILE_LENGTH) return false;
453 
454  *depot_tile = n->m_segment_last_tile;
455  return true;
456  }
457 };
458 
459 template <class Tpf_, class Tnode_list, template <class Types> class Tdestination>
461 {
463 
464  typedef Tpf_ Tpf;
466  typedef Tnode_list NodeList;
467  typedef RoadVehicle VehicleType;
468  typedef CYapfBaseT<Types> PfBase;
471  typedef Tdestination<Types> PfDestination;
474 };
475 
476 struct CYapfRoad1 : CYapfT<CYapfRoad_TypesT<CYapfRoad1 , CRoadNodeListTrackDir, CYapfDestinationTileRoadT > > {};
477 struct CYapfRoad2 : CYapfT<CYapfRoad_TypesT<CYapfRoad2 , CRoadNodeListExitDir , CYapfDestinationTileRoadT > > {};
478 
479 struct CYapfRoadAnyDepot1 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot1, CRoadNodeListTrackDir, CYapfDestinationAnyDepotRoadT> > {};
480 struct CYapfRoadAnyDepot2 : CYapfT<CYapfRoad_TypesT<CYapfRoadAnyDepot2, CRoadNodeListExitDir , CYapfDestinationAnyDepotRoadT> > {};
481 
482 
483 Trackdir YapfRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirs, bool &path_found)
484 {
485  /* default is YAPF type 2 */
486  typedef Trackdir (*PfnChooseRoadTrack)(const RoadVehicle*, TileIndex, DiagDirection, bool &path_found);
487  PfnChooseRoadTrack pfnChooseRoadTrack = &CYapfRoad2::stChooseRoadTrack; // default: ExitDir, allow 90-deg
488 
489  /* check if non-default YAPF type should be used */
491  pfnChooseRoadTrack = &CYapfRoad1::stChooseRoadTrack; // Trackdir, allow 90-deg
492  }
493 
494  Trackdir td_ret = pfnChooseRoadTrack(v, tile, enterdir, path_found);
495  return (td_ret != INVALID_TRACKDIR) ? td_ret : (Trackdir)FindFirstBit2x64(trackdirs);
496 }
497 
499 {
500  TileIndex tile = v->tile;
501  Trackdir trackdir = v->GetVehicleTrackdir();
502  if ((TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(trackdir)) == 0) {
503  return FindDepotData();
504  }
505 
506  /* default is YAPF type 2 */
507  typedef bool (*PfnFindNearestDepot)(const RoadVehicle*, TileIndex, Trackdir, int, TileIndex*);
508  PfnFindNearestDepot pfnFindNearestDepot = &CYapfRoadAnyDepot2::stFindNearestDepot;
509 
510  /* check if non-default YAPF type should be used */
512  pfnFindNearestDepot = &CYapfRoadAnyDepot1::stFindNearestDepot; // Trackdir, allow 90-deg
513  }
514 
515  FindDepotData fdd;
516  bool ret = pfnFindNearestDepot(v, tile, trackdir, max_distance, &fdd.tile);
517  fdd.best_length = ret ? max_distance / 2 : UINT_MAX; // some fake distance or NOT_FOUND
518  return fdd;
519 }