opf_ship.cpp

Go to the documentation of this file.
00001 /* $Id: opf_ship.cpp 19099 2010-02-11 20:52:56Z yexo $ */
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 
00121 static const byte _ship_search_directions[6][4] = {
00122   { 0, 9, 2, 9 },
00123   { 9, 1, 9, 3 },
00124   { 9, 0, 3, 9 },
00125   { 1, 9, 9, 2 },
00126   { 3, 2, 9, 9 },
00127   { 9, 9, 1, 0 },
00128 };
00129 
00130 static const byte _pick_shiptrack_table[6] = {1, 3, 2, 2, 0, 0};
00131 
00132 static uint FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
00133 {
00134   TrackPathFinder pfs;
00135   uint best_bird_dist = 0;
00136   uint best_length    = 0;
00137   byte ship_dir = v->direction & 3;
00138 
00139   pfs.dest_coords = v->dest_tile;
00140   pfs.skiptile = skiptile;
00141 
00142   Track best_track = INVALID_TRACK;
00143 
00144   do {
00145     Track i = RemoveFirstTrack(&bits);
00146 
00147     pfs.best_bird_dist = UINT_MAX;
00148     pfs.best_length = UINT_MAX;
00149 
00150     OPFShipFollowTrack(tile, (DiagDirection)_ship_search_directions[i][dir], &pfs);
00151 
00152     if (best_track != INVALID_TRACK) {
00153       if (pfs.best_bird_dist != 0) {
00154         /* neither reached the destination, pick the one with the smallest bird dist */
00155         if (pfs.best_bird_dist > best_bird_dist) goto bad;
00156         if (pfs.best_bird_dist < best_bird_dist) goto good;
00157       } else {
00158         if (pfs.best_length > best_length) goto bad;
00159         if (pfs.best_length < best_length) goto good;
00160       }
00161 
00162       /* if we reach this position, there's two paths of equal value so far.
00163        * pick one randomly. */
00164       uint r = GB(Random(), 0, 8);
00165       if (_pick_shiptrack_table[i] == ship_dir) r += 80;
00166       if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
00167       if (r <= 127) goto bad;
00168     }
00169 good:;
00170     best_track = i;
00171     best_bird_dist = pfs.best_bird_dist;
00172     best_length = pfs.best_length;
00173 bad:;
00174 
00175   } while (bits != 0);
00176 
00177   *track = best_track;
00178   return best_bird_dist;
00179 }
00180 
00184 Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
00185 {
00186   assert(IsValidDiagDirection(enterdir));
00187 
00188   TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
00189   Track track;
00190 
00191   /* Let's find out how far it would be if we would reverse first */
00192   TrackBits b = TrackStatusToTrackBits(GetTileTrackStatus(tile2, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(ReverseDiagDir(enterdir)) & v->state;
00193 
00194   uint distr = UINT_MAX; // distance if we reversed
00195   if (b != 0) {
00196     distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
00197     if (distr != UINT_MAX) distr++; // penalty for reversing
00198   }
00199 
00200   /* And if we would not reverse? */
00201   uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
00202 
00203   if (dist <= distr) return track;
00204   return INVALID_TRACK; // We could better reverse
00205 }

Generated on Wed Feb 17 23:06:50 2010 for OpenTTD by  doxygen 1.6.1