# opf_ship.cpp

Go to the documentation of this file.

    00001 /* $Id: opf_ship.cpp 24481 2012-08-18 11:37:47Z frosch $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "../../stdafx.h"
    00013 #include "../../tunnelbridge_map.h"
    00014 #include "../../tunnelbridge.h"
    00015 #include "../../ship.h"
    00016 #include "../../core/random_func.hpp"
    00017
    00018 struct RememberData {
    00019   uint16 cur_length;
    00020   byte depth;
    00021   Track last_choosen_track;
    00022 };
    00023
    00024 struct TrackPathFinder {
    00025   TileIndex skiptile;
    00026   TileIndex dest_coords;
    00027   uint best_bird_dist;
    00028   uint best_length;
    00029   RememberData rd;
    00030   TrackdirByte the_dir;
    00031 };
    00032
    00033 static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length)
    00034 {
    00035   /* Found dest? */
    00036   if (tile == pfs->dest_coords) {
    00037     pfs->best_bird_dist = 0;
    00038
    00039     pfs->best_length = minu(pfs->best_length, length);
    00040     return true;
    00041   }
    00042
    00043   /* Skip this tile in the calculation */
    00044   if (tile != pfs->skiptile) {
    00045     pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
    00046   }
    00047
    00048   return false;
    00049 }
    00050
    00051 static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection direction)
    00052 {
    00053   if (IsTileType(tile, MP_TUNNELBRIDGE)) {
    00054     /* wrong track type */
    00055     if (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) return;
    00056
    00057     DiagDirection dir = GetTunnelBridgeDirection(tile);
    00058     /* entering tunnel / bridge? */
    00059     if (dir == direction) {
    00060       TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
    00061
    00062       tpf->rd.cur_length += GetTunnelBridgeLength(tile, endtile) + 1;
    00063
    00064       tile = endtile;
    00065     } else {
    00066       /* leaving tunnel / bridge? */
    00067       if (ReverseDiagDir(dir) != direction) return;
    00068     }
    00069   }
    00070
    00071   /* This addition will sometimes overflow by a single tile.
    00072    * The use of TILE_MASK here makes sure that we still point at a valid
    00073    * tile, and then this tile will be in the sentinel row/col, so GetTileTrackStatus will fail. */
    00074   tile = TILE_MASK(tile + TileOffsByDiagDir(direction));
    00075
    00076   if (++tpf->rd.cur_length > 50) return;
    00077
    00078   TrackBits bits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(direction);
    00079   if (bits == TRACK_BIT_NONE) return;
    00080
    00081   assert(TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY());
    00082
    00083   bool only_one_track = true;
    00084   do {
    00085     Track track = RemoveFirstTrack(&bits);
    00086     if (bits != TRACK_BIT_NONE) only_one_track = false;
    00087     RememberData rd = tpf->rd;
    00088
    00089     /* Change direction 4 times only */
    00090     if (!only_one_track && track != tpf->rd.last_choosen_track) {
    00091       if (++tpf->rd.depth > 4) {
    00092         tpf->rd = rd;
    00093         return;
    00094       }
    00095       tpf->rd.last_choosen_track = track;
    00096     }
    00097
    00098     tpf->the_dir = TrackEnterdirToTrackdir(track, direction);
    00099
    00100     if (!ShipTrackFollower(tile, tpf, tpf->rd.cur_length)) {
    00101       TPFModeShip(tpf, tile, TrackdirToExitdir(tpf->the_dir));
    00102     }
    00103
    00104     tpf->rd = rd;
    00105   } while (bits != TRACK_BIT_NONE);
    00106 }
    00107
    00108 static void OPFShipFollowTrack(TileIndex tile, DiagDirection direction, TrackPathFinder *tpf)
    00109 {
    00110   assert(IsValidDiagDirection(direction));
    00111
    00112   /* initialize path finder variables */
    00113   tpf->rd.cur_length = 0;
    00114   tpf->rd.depth = 0;
    00115   tpf->rd.last_choosen_track = INVALID_TRACK;
    00116
    00117   ShipTrackFollower(tile, tpf, 0);
    00118   TPFModeShip(tpf, tile, direction);
    00119 }
    00120
    00122 static const DiagDirection _ship_search_directions[6][4] = {
    00123   { DIAGDIR_NE,      INVALID_DIAGDIR, DIAGDIR_SW,      INVALID_DIAGDIR },
    00124   { INVALID_DIAGDIR, DIAGDIR_SE,      INVALID_DIAGDIR, DIAGDIR_NW      },
    00125   { INVALID_DIAGDIR, DIAGDIR_NE,      DIAGDIR_NW,      INVALID_DIAGDIR },
    00126   { DIAGDIR_SE,      INVALID_DIAGDIR, INVALID_DIAGDIR, DIAGDIR_SW      },
    00127   { DIAGDIR_NW,      DIAGDIR_SW,      INVALID_DIAGDIR, INVALID_DIAGDIR },
    00128   { INVALID_DIAGDIR, INVALID_DIAGDIR, DIAGDIR_SE,      DIAGDIR_NE      },
    00129 };
    00130
    00132 static const byte _pick_shiptrack_table[6] = {DIR_NE, DIR_SE, DIR_E, DIR_E, DIR_N, DIR_N};
    00133
    00134 static uint FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
    00135 {
    00136   TrackPathFinder pfs;
    00137   uint best_bird_dist = 0;
    00138   uint best_length    = 0;
    00139   byte ship_dir = v->direction & 3;
    00140
    00141   pfs.dest_coords = v->dest_tile;
    00142   pfs.skiptile = skiptile;
    00143
    00144   Track best_track = INVALID_TRACK;
    00145
    00146   do {
    00147     Track i = RemoveFirstTrack(&bits);
    00148
    00149     pfs.best_bird_dist = UINT_MAX;
    00150     pfs.best_length = UINT_MAX;
    00151
    00152     OPFShipFollowTrack(tile, _ship_search_directions[i][dir], &pfs);
    00153
    00154     if (best_track != INVALID_TRACK) {
    00155       if (pfs.best_bird_dist != 0) {
    00156         /* neither reached the destination, pick the one with the smallest bird dist */
    00157         if (pfs.best_bird_dist > best_bird_dist) goto bad;
    00158         if (pfs.best_bird_dist < best_bird_dist) goto good;
    00159       } else {
    00160         if (pfs.best_length > best_length) goto bad;
    00161         if (pfs.best_length < best_length) goto good;
    00162       }
    00163
    00164       /* if we reach this position, there's two paths of equal value so far.
    00165        * pick one randomly. */
    00166       uint r = GB(Random(), 0, 8);
    00167       if (_pick_shiptrack_table[i] == ship_dir) r += 80;
    00168       if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
    00169       if (r <= 127) goto bad;
    00170     }
    00171 good:;
    00172     best_track = i;
    00173     best_bird_dist = pfs.best_bird_dist;
    00174     best_length = pfs.best_length;
    00175 bad:;
    00176
    00177   } while (bits != 0);
    00178
    00179   *track = best_track;
    00180   return best_bird_dist;
    00181 }
    00182
    00188 Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
    00189 {
    00190   assert(IsValidDiagDirection(enterdir));
    00191
    00192   TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
    00193   Track track;
    00194
    00195   /* Let's find out how far it would be if we would reverse first */
    00196   Trackdir trackdir = v->GetVehicleTrackdir();
    00197   TrackBits b = TrackStatusToTrackBits(GetTileTrackStatus(tile2, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(ReverseDiagDir(enterdir)) & TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir));
    00198
    00199   uint distr = UINT_MAX; // distance if we reversed
    00200   if (b != 0) {
    00201     distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
    00202     if (distr != UINT_MAX) distr++; // penalty for reversing
    00203   }
    00204
    00205   /* And if we would not reverse? */
    00206   uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
    00207
    00208   /* Due to the way this pathfinder works we cannot determine whether we're lost or not. */
    00209   path_found = true;
    00210   if (dist <= distr) return track;
    00211   return INVALID_TRACK; // We could better reverse
    00212 }