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

Point in pathfinding #32845

Merged
merged 2 commits into from
Aug 2, 2019
Merged
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
5 changes: 5 additions & 0 deletions src/line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ float trig_dist( const int x1, const int y1, const int x2, const int y2 )
return trig_dist( tripoint( x1, y1, 0 ), tripoint( x2, y2, 0 ) );
}

float trig_dist( const point &loc1, const point &loc2 )
{
return trig_dist( tripoint( loc1, 0 ), tripoint( loc2, 0 ) );
}

float trig_dist( const tripoint &loc1, const tripoint &loc2 )
{
return sqrt( static_cast<double>( ( loc1.x - loc2.x ) * ( loc1.x - loc2.x ) ) +
Expand Down
1 change: 1 addition & 0 deletions src/line.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ std::vector<point> line_to( const point &p1, const point &p2, int t = 0 );
std::vector<tripoint> line_to( const tripoint &loc1, const tripoint &loc2, int t = 0, int t2 = 0 );
// sqrt(dX^2 + dY^2)
float trig_dist( int x1, int y1, int x2, int y2 );
float trig_dist( const point &loc1, const point &loc2 );
float trig_dist( const tripoint &loc1, const tripoint &loc2 );
// Roguelike distance; minimum of dX and dY
int square_dist( int x1, int y1, int x2, int y2 );
Expand Down
27 changes: 13 additions & 14 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2919,19 +2919,19 @@ void overmap::build_city_street( const overmap_connection &connection, const poi
right++;
}

build_city_street( connection, iter->pos(), left, om_direction::turn_left( dir ),
build_city_street( connection, iter->pos, left, om_direction::turn_left( dir ),
town, new_width );

build_city_street( connection, iter->pos(), right, om_direction::turn_right( dir ),
build_city_street( connection, iter->pos, right, om_direction::turn_right( dir ),
town, new_width );

auto &oter = ter( tripoint( iter->pos(), 0 ) );
auto &oter = ter( tripoint( iter->pos, 0 ) );
// TODO: Get rid of the hardcoded terrain ids.
if( one_in( 2 ) && oter->get_line() == 15 && oter->type_is( oter_type_id( "road" ) ) ) {
oter = oter_id( "road_nesw_manhole" );
}
}
const tripoint rp( iter->x, iter->y, 0 );
const tripoint rp( iter->pos, 0 );

if( !one_in( BUILDINGCHANCE ) ) {
place_building( rp, om_direction::turn_left( dir ), town );
Expand All @@ -2948,9 +2948,9 @@ void overmap::build_city_street( const overmap_connection &connection, const poi
if( cs >= 2 && c == 0 ) {
const auto &last_node = street_path.nodes.back();
const auto rnd_dir = om_direction::turn_random( dir );
build_city_street( connection, last_node.pos(), cs, rnd_dir, town );
build_city_street( connection, last_node.pos, cs, rnd_dir, town );
if( one_in( 5 ) ) {
build_city_street( connection, last_node.pos(), cs, om_direction::opposite( rnd_dir ),
build_city_street( connection, last_node.pos, cs, om_direction::opposite( rnd_dir ),
town, new_width );
}
}
Expand Down Expand Up @@ -3263,7 +3263,7 @@ pf::path overmap::lay_out_connection( const overmap_connection &connection, cons
const point &dest, int z, const bool must_be_unexplored ) const
{
const auto estimate = [&]( const pf::node & cur, const pf::node * prev ) {
const auto &id( get_ter( tripoint( cur.pos(), z ) ) );
const auto &id( get_ter( tripoint( cur.pos, z ) ) );

const overmap_connection::subtype *subtype = connection.pick_subtype_for( id );

Expand All @@ -3276,7 +3276,7 @@ pf::path overmap::lay_out_connection( const overmap_connection &connection, cons
// Only do this check if it needs to be unexplored and there isn't already a connection.
if( must_be_unexplored && !existing_connection ) {
// If this must be unexplored, check if we've already got a submap generated.
const bool existing_submap = is_omt_generated( tripoint( cur.x, cur.y, z ) );
const bool existing_submap = is_omt_generated( tripoint( cur.pos, z ) );

// If there is an existing submap, this area has already been explored and this
// isn't a valid placement.
Expand All @@ -3291,18 +3291,17 @@ pf::path overmap::lay_out_connection( const overmap_connection &connection, cons
}

if( prev && prev->dir != cur.dir ) { // Direction has changed.
const auto &prev_id( get_ter( tripoint( prev->pos(), z ) ) );
const auto &prev_id( get_ter( tripoint( prev->pos, z ) ) );
const overmap_connection::subtype *prev_subtype = connection.pick_subtype_for( prev_id );

if( !prev_subtype || !prev_subtype->allows_turns() ) {
return pf::rejected;
}
}

const int dx = dest.x - cur.x;
const int dy = dest.y - cur.y;
const int dist = subtype->is_orthogonal() ? std::abs( dx ) + std::abs( dy ) : std::sqrt(
dx * dx + dy * dy );
const int dist = subtype->is_orthogonal() ?
manhattan_dist( dest, cur.pos ) :
trig_dist( dest, cur.pos );
const int existency_mult = existing_connection ? 1 : 5; // Prefer existing connections.

return existency_mult * dist + subtype->basic_cost;
Expand Down Expand Up @@ -3380,7 +3379,7 @@ void overmap::build_connection( const overmap_connection &connection, const pf::
om_direction::type prev_dir = initial_dir;

for( const auto &node : path.nodes ) {
const tripoint pos( node.x, node.y, z );
const tripoint pos( node.pos, z );
auto &ter_id( ter( pos ) );
// TODO: Make 'node' support 'om_direction'.
const om_direction::type new_dir( static_cast<om_direction::type>( node.dir ) );
Expand Down
14 changes: 6 additions & 8 deletions src/overmapbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ std::vector<tripoint> overmapbuffer::get_npc_path( const tripoint &src, const tr

const auto estimate = [&]( const pf::node & cur, const pf::node * ) {
int res = 0;
const oter_id oter = get_ter_at( { cur.x, cur.y } );
const oter_id oter = get_ter_at( cur.pos );
int travel_cost = static_cast<int>( oter->get_travel_cost() );
if( ( road_only && ( oter->get_name() != "road" && oter->get_name() != "bridge" ) ) ||
( oter->get_name() == "solid rock" ||
Expand All @@ -710,15 +710,14 @@ std::vector<tripoint> overmapbuffer::get_npc_path( const tripoint &src, const tr
travel_cost = 20;
}
res += travel_cost;
res += std::abs( finish.x - cur.x ) +
std::abs( finish.y - cur.y );
res += manhattan_dist( finish, cur.pos );

return res;
};
pf::path route = pf::find_path( point( start.x, start.y ), point( finish.x, finish.y ), 2 * OX,
2 * OY, estimate );
for( auto node : route.nodes ) {
tripoint convert_result = base + tripoint( node.x, node.y, base.z );
tripoint convert_result = base + tripoint( node.pos, base.z );
path.push_back( convert_result );
}
return path;
Expand Down Expand Up @@ -756,7 +755,7 @@ bool overmapbuffer::reveal_route( const tripoint &source, const tripoint &dest,
const auto estimate = [&]( const pf::node & cur, const pf::node * ) {
int res = 0;

const oter_id oter = get_ter_at( { cur.x, cur.y } );
const oter_id oter = get_ter_at( cur.pos );

if( !connection->has( oter ) ) {
if( road_only ) {
Expand All @@ -771,8 +770,7 @@ bool overmapbuffer::reveal_route( const tripoint &source, const tripoint &dest,
res += 250;
}

res += std::abs( finish.x - cur.x ) +
std::abs( finish.y - cur.y );
res += manhattan_dist( finish, cur.pos );

return res;
};
Expand All @@ -781,7 +779,7 @@ bool overmapbuffer::reveal_route( const tripoint &source, const tripoint &dest,
2 * OY, estimate );

for( const auto &node : path.nodes ) {
reveal( base + node.pos(), radius );
reveal( base + node.pos, radius );
}
return !path.nodes.empty();
}
Expand Down
18 changes: 8 additions & 10 deletions src/pathfinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ struct path_data_layer {
std::array< int, MAPSIZE_X *MAPSIZE_Y > gscore;
std::array< tripoint, MAPSIZE_X *MAPSIZE_Y > parent;

void init( const int minx, const int miny, const int maxx, const int maxy ) {
for( int x = minx; x <= maxx; x++ ) {
for( int y = miny; y <= maxy; y++ ) {
void init( const point &min, const point &max ) {
for( int x = min.x; x <= max.x; x++ ) {
for( int y = min.y; y <= max.y; y++ ) {
const int ind = flat_index( x, y );
state[ind] = ASL_NONE; // Mark as unvisited
}
Expand All @@ -55,26 +55,24 @@ struct path_data_layer {
};

struct pathfinder {
int minx;
int miny;
int maxx;
int maxy;
point min;
point max;
pathfinder( int _minx, int _miny, int _maxx, int _maxy ) :
minx( _minx ), miny( _miny ), maxx( _maxx ), maxy( _maxy ) {
min( _minx, _miny ), max( _maxx, _maxy ) {
}

std::priority_queue< std::pair<int, tripoint>, std::vector< std::pair<int, tripoint> >, pair_greater_cmp_first >
open;
std::array< std::unique_ptr< path_data_layer >, OVERMAP_LAYERS > path_data;

path_data_layer &get_layer( const int z ) {
auto &ptr = path_data[z + OVERMAP_DEPTH];
std::unique_ptr< path_data_layer > &ptr = path_data[z + OVERMAP_DEPTH];
if( ptr != nullptr ) {
return *ptr;
}

ptr = std::make_unique<path_data_layer>();
ptr->init( minx, miny, maxx, maxy );
ptr->init( min, max );
return *ptr;
}

Expand Down
83 changes: 33 additions & 50 deletions src/simple_pathfinding.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,27 @@
#include <vector>

#include "enums.h"
#include "point.h"

namespace pf
{

static const int rejected = std::numeric_limits<int>::min();

struct node {
int x;
int y;
point pos;
int dir;
int priority;

node( int x, int y, int dir, int priority = 0 ) :
x( x ),
y( y ),
node( const point &p, int dir, int priority = 0 ) :
pos( p ),
dir( dir ),
priority( priority ) {}

// Operator overload required by priority queue interface.
bool operator< ( const node &n ) const {
return priority > n.priority;
}

point pos() const {
return point( x, y );
}
};

struct path {
Expand All @@ -55,15 +50,14 @@ path find_path( const point &source,
const int max_y,
BinaryPredicate estimator )
{
static const int dx[4] = { 0, 1, 0, -1 };
static const int dy[4] = { -1, 0, 1, 0 };
static constexpr point d[4] = { point( 0, -1 ), point( 1, 0 ), point( 0, 1 ), point( -1, 0 ) };

const auto inbounds = [ max_x, max_y ]( const int x, const int y ) {
return x >= 0 && x < max_x && y >= 0 && y <= max_y;
const auto inbounds = [ max_x, max_y ]( const point & p ) {
return p.x >= 0 && p.x < max_x && p.y >= 0 && p.y <= max_y;
};

const auto map_index = [ max_x ]( const int x, const int y ) {
return y * max_x + x;
const auto map_index = [ max_x ]( const point & p ) {
return p.y * max_x + p.x;
};

path res;
Expand All @@ -72,16 +66,11 @@ path find_path( const point &source,
return res;
}

const int x1 = source.x;
const int y1 = source.y;
const int x2 = dest.x;
const int y2 = dest.y;

if( !inbounds( x1, y1 ) || !inbounds( x2, y2 ) ) {
if( !inbounds( source ) || !inbounds( dest ) ) {
return res;
}

const node first_node( x1, y1, 5, 1000 );
const node first_node( source, 5, 1000 );

if( estimator( first_node, nullptr ) == rejected ) {
return res;
Expand All @@ -96,59 +85,56 @@ path find_path( const point &source,

int i = 0;
nodes[i].push( first_node );
open[map_index( x1, y1 )] = std::numeric_limits<int>::max();
open[map_index( source )] = std::numeric_limits<int>::max();

// use A* to find the shortest path from (x1,y1) to (x2,y2)
while( !nodes[i].empty() ) {
const node mn( nodes[i].top() ); // get the best-looking node

nodes[i].pop();
// mark it visited
closed[map_index( mn.x, mn.y )] = true;
closed[map_index( mn.pos )] = true;

// if we've reached the end, draw the path and return
if( mn.x == x2 && mn.y == y2 ) {
int x = mn.x;
int y = mn.y;
if( mn.pos == dest ) {
point p = mn.pos;

res.nodes.reserve( nodes[i].size() );

while( x != x1 || y != y1 ) {
const int n = map_index( x, y );
const int d = dirs[n];
res.nodes.emplace_back( x, y, d );
x += dx[d];
y += dy[d];
while( p != source ) {
const int n = map_index( p );
const int dir = dirs[n];
res.nodes.emplace_back( p, dir );
p += d[dir];
}

res.nodes.emplace_back( x, y, -1 );
res.nodes.emplace_back( p, -1 );

return res;
}

for( int d = 0; d < 4; d++ ) {
const int x = mn.x + dx[d];
const int y = mn.y + dy[d];
const int n = map_index( x, y );
for( int dir = 0; dir < 4; dir++ ) {
const point p = mn.pos + d[dir];
const int n = map_index( p );
// don't allow:
// * out of bounds
// * already traversed tiles
if( x < 1 || x + 1 >= max_x || y < 1 || y + 1 >= max_y || closed[n] ) {
if( p.x < 1 || p.x + 1 >= max_x || p.y < 1 || p.y + 1 >= max_y || closed[n] ) {
continue;
}

node cn( x, y, d );
node cn( p, dir );
cn.priority = estimator( cn, &mn );

if( cn.priority == rejected ) {
continue;
}
// record direction to shortest path
if( open[n] == 0 || open[n] > cn.priority ) {
dirs[n] = ( d + 2 ) % 4;
dirs[n] = ( dir + 2 ) % 4;

if( open[n] != 0 ) {
while( nodes[i].top().x != x || nodes[i].top().y != y ) {
while( nodes[i].top().pos != p ) {
nodes[1 - i].push( nodes[i].top() );
nodes[i].pop();
}
Expand Down Expand Up @@ -176,28 +162,25 @@ inline path straight_path( const point &source,
int dir,
size_t len )
{
static const int dx[4] = { 0, 1, 0, -1 };
static const int dy[4] = { -1, 0, 1, 0 };
static constexpr point d[4] = { point( 0, -1 ), point( 1, 0 ), point( 0, 1 ), point( -1, 0 ) };

path res;

if( len == 0 ) {
return res;
}

int x = source.x;
int y = source.y;
point p = source;

res.nodes.reserve( len );

for( size_t i = 0; i + 1 < len; ++i ) {
res.nodes.emplace_back( x, y, dir );
res.nodes.emplace_back( p, dir );

x += dx[dir];
y += dy[dir];
p += d[dir];
}

res.nodes.emplace_back( x, y, -1 );
res.nodes.emplace_back( p, -1 );

return res;
}
Expand Down