Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #5713: Use pathfinder to find closest ship depot #6928

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/pathfinder/npf/npf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,17 @@ static int32 NPFFindDepot(AyStar *as, OpenListNode *current)
AyStarUserData *user = (AyStarUserData *)as->user_data;
/* It's not worth caching the result with NPF_FLAG_IS_TARGET here as below,
* since checking the cache not that much faster than the actual check */
return IsDepotTypeTile(current->path.node.tile, user->type) ?
AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
if (user->type != TRANSPORT_WATER) {
return IsDepotTypeTile(current->path.node.tile, user->type) ?
AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
} else {
/* The tile where the ship actually "enters" the depot is the north tile.
* Ship depots are also valid water tracks for ships from any company,
* unlike the other depot types, so the pathfinder has to do a blind
* search for a tile that matches these conditions. */
return IsShipDepotTile(current->path.node.tile) && GetShipDepotPart(current->path.node.tile) == DEPOT_PART_NORTH && IsTileOwner(current->path.node.tile, user->owner) ?
nielsmh marked this conversation as resolved.
Show resolved Hide resolved
AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
}
}

/** Find any safe and free tile. */
Expand Down Expand Up @@ -1154,6 +1163,20 @@ Trackdir NPFRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDir

/*** Ships ***/

FindDepotData NPFShipFindNearestDepot(const Ship *v, int max_penalty)
{
Trackdir trackdir = v->GetVehicleTrackdir();
Trackdir trackdir_rev = ReverseTrackdir(trackdir);

AyStarUserData user = { v->owner, TRANSPORT_WATER, INVALID_RAILTYPES, ROADTYPES_NONE };
NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, v->tile, trackdir_rev, false, NULL, &user, 0, max_penalty);
if (ftd.best_bird_dist != 0) return FindDepotData();

/* Found target */
/* Our caller wants the distance to depot. We provide it in distance manhattan */
return FindDepotData(ftd.node.tile, DistanceManhattan(v->tile, ftd.node.tile), NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE));
}

Track NPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
{
NPFFindStationOrTileData fstd;
Expand Down
10 changes: 10 additions & 0 deletions src/pathfinder/npf/npf_func.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ FindDepotData NPFRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_penal
*/
Trackdir NPFRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirs, bool &path_found);

/**
* Used when user sends ship to the nearest depot or if ship needs servicing using NPF
* @param v ship that needs to go to some depot
* @param max_penalty max distance (in pathfinder penalty) from the current ship position
* (used also as optimization - the pathfinder can stop path finding if max_penalty
* was reached and no depot was seen)
* @return the data about the depot
*/
FindDepotData NPFShipFindNearestDepot(const Ship *v, int max_penalty);

/**
* Finds the best path for given ship using NPF.
* @param v the ship that needs to find a path
Expand Down
168 changes: 125 additions & 43 deletions src/pathfinder/opf/opf_ship.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
#include "../../tunnelbridge.h"
#include "../../ship.h"
#include "../../core/random_func.hpp"
#include "../pathfinder_type.h"
#include "../../depot_base.h"

#include "../../safeguards.h"

static const uint OPF_MAX_LENGTH = 50; ///< The default maximum path length

struct RememberData {
uint16 cur_length;
byte depth;
Expand All @@ -28,29 +32,43 @@ struct TrackPathFinder {
TileIndex dest_coords;
uint best_bird_dist;
uint best_length;
uint max_path_length;
RememberData rd;
TrackdirByte the_dir;
Owner ship_owner;
};

static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length)
static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length, bool depot)
{
/* Found dest? */
if (tile == pfs->dest_coords) {
pfs->best_bird_dist = 0;
if (!depot) {
/* Found dest? */
if (tile == pfs->dest_coords) {
pfs->best_bird_dist = 0;

pfs->best_length = minu(pfs->best_length, length);
return true;
}
pfs->best_length = minu(pfs->best_length, length);
return true;
}

/* Skip this tile in the calculation */
if (tile != pfs->skiptile) {
pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
}
/* Skip this tile in the calculation */
if (tile != pfs->skiptile) {
pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
}

return false;
} else {
/* Found depot? */
if (IsShipDepotTile(tile) && GetShipDepotPart(tile) == DEPOT_PART_NORTH && IsTileOwner(tile, pfs->ship_owner)) {
pfs->best_bird_dist = 0;
pfs->dest_coords = tile;
pfs->best_length = minu(pfs->best_length, length);
return true;
}

return false;
return false;
}
}

static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection direction)
static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection direction, bool depot)
{
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
/* wrong track type */
Expand All @@ -75,7 +93,8 @@ static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection dire
* tile, and then this tile will be in the sentinel row/col, so GetTileTrackStatus will fail. */
tile = TILE_MASK(tile + TileOffsByDiagDir(direction));

if (++tpf->rd.cur_length > 50) return;
/* Finish if we already exceeded the maximum path cost */
if (++tpf->rd.cur_length > tpf->max_path_length) return;

TrackBits bits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(direction);
if (bits == TRACK_BIT_NONE) return;
Expand All @@ -99,15 +118,15 @@ static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection dire

tpf->the_dir = TrackEnterdirToTrackdir(track, direction);

if (!ShipTrackFollower(tile, tpf, tpf->rd.cur_length)) {
TPFModeShip(tpf, tile, TrackdirToExitdir(tpf->the_dir));
if (!ShipTrackFollower(tile, tpf, tpf->rd.cur_length, depot)) {
TPFModeShip(tpf, tile, TrackdirToExitdir(tpf->the_dir), depot);
}

tpf->rd = rd;
} while (bits != TRACK_BIT_NONE);
}

static void OPFShipFollowTrack(TileIndex tile, DiagDirection direction, TrackPathFinder *tpf)
static void OPFShipFollowTrack(TileIndex tile, DiagDirection direction, TrackPathFinder *tpf, bool depot)
{
assert(IsValidDiagDirection(direction));

Expand All @@ -116,8 +135,8 @@ static void OPFShipFollowTrack(TileIndex tile, DiagDirection direction, TrackPat
tpf->rd.depth = 0;
tpf->rd.last_choosen_track = INVALID_TRACK;

ShipTrackFollower(tile, tpf, 0);
TPFModeShip(tpf, tile, direction);
ShipTrackFollower(tile, tpf, 0, depot);
TPFModeShip(tpf, tile, direction, depot);
}

/** Directions to search towards given track bits and the ship's enter direction. */
Expand All @@ -133,17 +152,26 @@ static const DiagDirection _ship_search_directions[6][4] = {
/** Track to "direction (& 3)" mapping. */
static const byte _pick_shiptrack_table[6] = {DIR_NE, DIR_SE, DIR_E, DIR_E, DIR_N, DIR_N};

static uint FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
struct ShipTrackData {
Track best_track;
uint best_bird_dist;
uint best_length;
TileIndex dest_coords;
};

static void FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, ShipTrackData *fstd, uint max_path_length = OPF_MAX_LENGTH, bool depot = false)
{
TrackPathFinder pfs;
uint best_bird_dist = 0;
uint best_length = 0;
fstd->best_track = INVALID_TRACK;
fstd->best_bird_dist = 0;
fstd->best_length = 0;
fstd->dest_coords = depot ? INVALID_TILE : v->dest_tile;
byte ship_dir = v->direction & 3;

pfs.dest_coords = v->dest_tile;
TrackPathFinder pfs;
pfs.dest_coords = fstd->dest_coords;
pfs.skiptile = skiptile;

Track best_track = INVALID_TRACK;
pfs.max_path_length = max_path_length;
pfs.ship_owner = v->owner;

assert(bits != TRACK_BIT_NONE);
do {
Expand All @@ -152,35 +180,33 @@ static uint FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, Trac
pfs.best_bird_dist = UINT_MAX;
pfs.best_length = UINT_MAX;

OPFShipFollowTrack(tile, _ship_search_directions[i][dir], &pfs);
OPFShipFollowTrack(tile, _ship_search_directions[i][dir], &pfs, depot);

if (best_track != INVALID_TRACK) {
if (fstd->best_track != INVALID_TRACK) {
if (pfs.best_bird_dist != 0) {
/* neither reached the destination, pick the one with the smallest bird dist */
if (pfs.best_bird_dist > best_bird_dist) goto bad;
if (pfs.best_bird_dist < best_bird_dist) goto good;
if (pfs.best_bird_dist > fstd->best_bird_dist) goto bad;
if (pfs.best_bird_dist < fstd->best_bird_dist) goto good;
} else {
if (pfs.best_length > best_length) goto bad;
if (pfs.best_length < best_length) goto good;
if (pfs.best_length > fstd->best_length) goto bad;
if (pfs.best_length < fstd->best_length) goto good;
}

/* if we reach this position, there's two paths of equal value so far.
* pick one randomly. */
uint r = GB(Random(), 0, 8);
if (_pick_shiptrack_table[i] == ship_dir) r += 80;
if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
if (_pick_shiptrack_table[fstd->best_track] == ship_dir) r -= 80;
if (r <= 127) goto bad;
}
good:;
best_track = i;
best_bird_dist = pfs.best_bird_dist;
best_length = pfs.best_length;
fstd->best_track = i;
fstd->best_bird_dist = pfs.best_bird_dist;
fstd->best_length = pfs.best_length;
fstd->dest_coords = pfs.dest_coords;
bad:;

} while (bits != TRACK_BIT_NONE);

*track = best_track;
return best_bird_dist;
}

/**
Expand All @@ -198,7 +224,7 @@ Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir,
assert(IsValidDiagDirection(enterdir));

TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
Track track;
ShipTrackData fstd;

/* Let's find out how far it would be if we would reverse first */
uint rev_dist = UINT_MAX; // distance if we reverse
Expand All @@ -208,15 +234,71 @@ Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir,
DiagdirReachesTracks(rev_enterdir);

if (HasTrack(rev_tracks, cur_track)) {
rev_dist = FindShipTrack(v, tile2, rev_enterdir, TrackToTrackBits(cur_track), tile, &track);
FindShipTrack(v, tile2, rev_enterdir, TrackToTrackBits(cur_track), tile, &fstd);
rev_dist = fstd.best_bird_dist;
if (rev_dist != UINT_MAX) rev_dist++; // penalty for reversing
}

/* And if we would not reverse? */
uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
FindShipTrack(v, tile, enterdir, tracks, 0, &fstd);
uint dist = fstd.best_bird_dist;

/* Due to the way this pathfinder works we cannot determine whether we're lost or not. */
path_found = true;
if (dist <= rev_dist) return track;
if (dist <= rev_dist) return fstd.best_track;
return INVALID_TRACK; // We could better reverse
}

FindDepotData OPFShipFindNearestDepot(const Ship *v, int max_distance)
{
FindDepotData fdd;

Trackdir trackdir = v->GetVehicleTrackdir();

/* Argument values for FindShipTrack below, for the current ship direction */
DiagDirection enterdir = TrackdirToExitdir(trackdir);
TileIndex tile = TILE_ADD(v->tile, TileOffsByDiagDir(enterdir));
TrackBits tracks = TrackdirBitsToTrackBits(DiagdirReachesTrackdirs(enterdir));

/* Argument values for FindShipTrack below, for the reversed ship direction */
DiagDirection enterdir_rev = ReverseDiagDir(enterdir);
TileIndex tile_rev = v->tile;
TrackBits tracks_rev = TrackStatusToTrackBits(GetTileTrackStatus(tile_rev, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(enterdir_rev) & TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir));

ShipTrackData fstd;
if (max_distance == 0) max_distance = OPF_MAX_LENGTH;

/* Let's find the length it would be if we would reverse first */
uint length_rev = UINT_MAX;
TileIndex dest_tile_rev = INVALID_TILE;
if (tracks_rev != 0) {
FindShipTrack(v, tile_rev, enterdir_rev, tracks_rev, tile, &fstd, max_distance, true);
length_rev = fstd.best_length;
tile_rev = fstd.dest_coords;
if (length_rev != UINT_MAX) length_rev++; // penalty for reversing
}

/* And if we would not reverse? */
FindShipTrack(v, tile, enterdir, tracks, 0, &fstd, max_distance, true);
uint length = fstd.best_length;
TileIndex dest_tile = fstd.dest_coords;

if (IsValidTile(dest_tile_rev)) {
if (IsValidTile(dest_tile)) {
fdd.tile = length_rev < length ? dest_tile_rev : dest_tile; // tile location of ship depot
fdd.best_length = DistanceManhattan(v->tile, fdd.tile); // distance manhattan from ship to depot
fdd.reverse = !(length <= length_rev);
}
fdd.tile = dest_tile_rev;
fdd.best_length = DistanceManhattan(v->tile, fdd.tile); // distance manhattan from ship to depot
fdd.reverse = true;
} else {
if (IsValidTile(dest_tile)) {
fdd.tile = dest_tile;
fdd.best_length = DistanceManhattan(v->tile, fdd.tile); // distance manhattan from ship to depot
fdd.reverse = false;
}
}

return fdd;
}
10 changes: 10 additions & 0 deletions src/pathfinder/opf/opf_ship.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,14 @@
*/
Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found);

/**
* Used when user sends ship to the nearest depot or if ship needs servicing using OPF.
* @param v vehicle that needs to go to some depot
* @param max_distance max distance (in pathfinder penalty) from the current ship position
* (used also as optimization - the pathfinder can stop path finding if max_penalty
* was reached and no depot was seen)
* @return the data about the depot
*/
FindDepotData OPFShipFindNearestDepot(const Ship *v, int max_distance);

#endif /* OPF_SHIP_H */
10 changes: 10 additions & 0 deletions src/pathfinder/yapf/yapf.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir,
*/
bool YapfShipCheckReverse(const Ship *v);

/**
* Used when user sends ship to the nearest depot or if ship needs servicing using YAPF.
* @param v vehicle that needs to go to some depot
* @param max_penalty max distance (in pathfinder penalty) from the current ship position
* (used also as optimization - the pathfinder can stop path finding if max_penalty
* was reached and no depot was seen)
* @return the data about the depot
*/
FindDepotData YapfShipFindNearestDepot(const Ship *v, int max_penalty);

/**
* Finds the best path for given road vehicle using YAPF.
* @param v the RV that needs to find a path
Expand Down
Loading