Skip to content

Commit

Permalink
Merge pull request #75961 from CLIDragon/closest_points_first
Browse files Browse the repository at this point in the history
Add a variant of closest_points_first that takes a predicate
  • Loading branch information
akrieger authored Feb 3, 2025
2 parents c4a84a2 + f5ce0cd commit 32ab239
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 56 deletions.
3 changes: 1 addition & 2 deletions src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3809,8 +3809,7 @@ int get_auto_consume_moves( Character &you, const bool food )
bool try_fuel_fire( player_activity &act, Character &you, const bool starting_fire )
{
const tripoint_bub_ms pos = you.pos_bub();
std::vector<tripoint_bub_ms> adjacent = closest_points_first( pos, PICKUP_RANGE );
adjacent.erase( adjacent.begin() );
std::vector<tripoint_bub_ms> adjacent = closest_points_first( pos, 1, PICKUP_RANGE );

map &here = get_map();
std::optional<tripoint_bub_ms> best_fire =
Expand Down
3 changes: 1 addition & 2 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5162,8 +5162,7 @@ std::pair<item *, tripoint_bub_ms> map::_add_item_or_charges( const tripoint_bub
if( overflow && copies_remaining > 0 ) {
// ...otherwise try to overflow to adjacent tiles (if permitted)
const int max_dist = 2;
std::vector<tripoint_bub_ms> tiles = closest_points_first( pos, max_dist );
tiles.erase( tiles.begin() ); // we already tried this position
std::vector<tripoint_bub_ms> tiles = closest_points_first( pos, 1, max_dist );
const int max_path_length = 4 * max_dist;
const pathfinding_settings setting( 0, max_dist, max_path_length, 0, false, false, true, false,
false, false );
Expand Down
4 changes: 1 addition & 3 deletions src/monattack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2153,9 +2153,7 @@ bool mattack::formblob( monster *z )
}

bool didit = false;
std::vector<tripoint_bub_ms> pts = closest_points_first( z->pos_bub(), 1 );
// Don't check own tile
pts.erase( pts.begin() );
std::vector<tripoint_bub_ms> pts = closest_points_first( z->pos_bub(), 1, 1 );
creature_tracker &creatures = get_creature_tracker();
for( const tripoint_bub_ms &dest : pts ) {
Creature *critter = creatures.creature_at( dest );
Expand Down
14 changes: 9 additions & 5 deletions src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "options.h"
#include "pathfinding.h"
#include "pimpl.h"
#include "point.h"
#include "rng.h"
#include "scent_map.h"
#include "sounds.h"
Expand Down Expand Up @@ -1800,11 +1801,14 @@ bool monster::attack_at( const tripoint_bub_ms &p )
static tripoint_bub_ms find_closest_stair( const tripoint_bub_ms &near_this,
const ter_furn_flag stair_type )
{
map &here = get_map();
for( const tripoint_bub_ms &candidate : closest_points_first( near_this, 10 ) ) {
if( here.has_flag( stair_type, candidate ) ) {
return candidate;
}
const map &here = get_map();
std::optional<tripoint_bub_ms> candidate = find_point_closest_first( near_this, 0, 10, [&here,
stair_type]( const tripoint_bub_ms & candidate ) {
return here.has_flag( stair_type, candidate );
} );

if( candidate != std::nullopt ) {
return *candidate;
}
// we didn't find it
return near_this;
Expand Down
6 changes: 3 additions & 3 deletions src/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,9 +1175,9 @@ void npc::place_on_map( map *here )
return;
}

for( const tripoint_abs_ms &p : closest_points_first( pos_abs(), SEEX + 1 ) ) {
if( g->is_empty( here, p ) ) {
setpos( here, here->get_bub( p ) );
for( const tripoint_bub_ms &p : closest_points_first( pos_bub(), 1, SEEX + 1 ) ) {
if( g->is_empty( p ) ) {
setpos( p );
return;
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/npcmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3176,8 +3176,7 @@ void npc::avoid_friendly_fire()

tripoint_bub_ms center = midpoint_round_to_nearest( fr_pts );

std::vector<tripoint_bub_ms> candidates = closest_points_first( pos_bub(), 1 );
candidates.erase( candidates.begin() );
std::vector<tripoint_bub_ms> candidates = closest_points_first( pos_bub(), 1, 1 );
std::sort( candidates.begin(), candidates.end(),
[&tar, &center]( const tripoint_bub_ms & l, const tripoint_bub_ms & r ) {
return ( rl_dist( l, tar ) - rl_dist( l, center ) ) <
Expand Down
75 changes: 40 additions & 35 deletions src/point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,42 @@ std::istream &operator>>( std::istream &is, tripoint &pos )
return is;
}

std::optional<int> rectangle_size( int min_dist, int max_dist )
{
min_dist = std::max( min_dist, 0 );
max_dist = std::max( max_dist, 0 );

if( min_dist > max_dist ) {
return std::nullopt;
}

const int min_edge = min_dist * 2 + 1;
const int max_edge = max_dist * 2 + 1;

const int n = max_edge * max_edge - ( min_edge - 2 ) * ( min_edge - 2 ) + ( min_dist == 0 ? 1 : 0 );
return n;
}

std::vector<tripoint> closest_points_first( const tripoint &center, int max_dist )
{
return closest_points_first( center, 0, max_dist );
}

std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist, int max_dist )
{
const std::vector<point> points = closest_points_first( center.xy(), min_dist, max_dist );
std::optional<int> n = rectangle_size( min_dist, max_dist );

if( n == std::nullopt ) {
return {};
}

std::vector<tripoint> result;
result.reserve( points.size() );
result.reserve( *n );

for( const point &p : points ) {
result.emplace_back( p, center.z );
}
find_point_closest_first( center, min_dist, max_dist, [&result]( const tripoint & p ) {
result.push_back( p );
return false;
} );

return result;
}
Expand All @@ -143,42 +164,26 @@ std::vector<point> closest_points_first( const point &center, int max_dist )

std::vector<point> closest_points_first( const point &center, int min_dist, int max_dist )
{
min_dist = std::max( min_dist, 0 );
max_dist = std::max( max_dist, 0 );
std::optional<int> n = rectangle_size( min_dist, max_dist );

if( min_dist > max_dist ) {
if( n == std::nullopt ) {
return {};
}

const int min_edge = min_dist * 2 + 1;
const int max_edge = max_dist * 2 + 1;

const int n = max_edge * max_edge - ( min_edge - 2 ) * ( min_edge - 2 );
const bool is_center_included = min_dist == 0;

std::vector<point> result;
result.reserve( n + ( is_center_included ? 1 : 0 ) );

if( is_center_included ) {
result.push_back( center );
}
result.reserve( *n );

int x_init = std::max( min_dist, 1 );
point p( x_init, 1 - x_init );

point d( point::east );

for( int i = 0; i < n; i++ ) {
result.push_back( center + p );

if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) {
std::swap( d.x, d.y );
d.x = -d.x;
}

p.x += d.x;
p.y += d.y;
}
find_point_closest_first( center, min_dist, max_dist, [&result]( const point & p ) {
result.push_back( p );
return false;
} );

return result;
}

template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int max_dist,
PredicateFn &&fn )
{
return find_point_closest_first( center, 0, max_dist, fn );
}
65 changes: 65 additions & 0 deletions src/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <optional>
#include <ostream>
#include <vector>

Expand Down Expand Up @@ -358,6 +359,18 @@ std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist
std::vector<point> closest_points_first( const point &center, int max_dist );
std::vector<point> closest_points_first( const point &center, int min_dist, int max_dist );

template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int min_dist, int max_dist,
PredicateFn &&fn );

template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int max_dist,
PredicateFn &&fn );


// Calculate the number of tiles in a square from min_dist to max_dist about an arbitrary centre.
std::optional<int> rectangle_size( int min_dist, int max_dist );

// Make point hashable so it can be used as an unordered_set or unordered_map key,
// or a component of one.
namespace std
Expand Down Expand Up @@ -442,4 +455,56 @@ inline constexpr const std::array<tripoint, 8> eight_horizontal_neighbors = { {
}
};

/* Return points in a clockwise spiral from min_dist to max_dist, inclusive, ordered by
* the closest points first. The distance is calculated using roguelike distances, so
* movement in any direction only counts as 1.
*
* The predicate function is evaluated on each point. If the function returns true, the
* point is returned. If the predicate function never returns true then std::nullopt is
* returned once max_dist is reached.
*
* @param center The center of the spiral.
* @param min_dist minimum distance to start the spiral from.
* @param max_dist greatest distance from the centre that the spiral will go to.
* @returns std::nullopt if min_dist > max_dist or predicate_fn evaluated to true for
* no points.
*/
template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int min_dist, int max_dist,
PredicateFn &&predicate_fn )
{
const std::optional<int> n = rectangle_size( min_dist, max_dist );

if( n == std::nullopt ) {
return {};
}

const bool is_center_included = min_dist == 0;

if( is_center_included && predicate_fn( center ) ) {
return center;
}

int x_init = std::max( min_dist, 1 );
point p {x_init, 1 - x_init};

point d = point::east;

for( int i = is_center_included; i < *n; i++ ) {
const Point next = Point{ center.raw() + p.raw() };
if( predicate_fn( next ) ) {
return next;
}

if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) {
std::swap( d.x, d.y );
d.x = -d.x;
}

p += d;
}

return std::nullopt;
}

#endif // CATA_SRC_POINT_H
9 changes: 5 additions & 4 deletions src/ranged.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,7 @@ static void cycle_action( item &weap, const itype_id &ammo, map *here, const tri
} ), tiles.end() );
tripoint_bub_ms eject{ tiles.empty() ? pos : random_entry( tiles ) };


// for turrets try and drop casings or linkages directly to any CARGO part on the same tile
const std::optional<vpart_reference> ovp_cargo = weap.has_flag( flag_VEHICLE )
? here->veh_at( pos ).cargo()
Expand Down Expand Up @@ -3161,12 +3162,12 @@ tripoint_bub_ms target_ui::choose_initial_target()

// Try closest practice target
map &here = get_map();
const std::vector<tripoint_bub_ms> nearby = closest_points_first( src, range );
const auto target_spot = std::find_if( nearby.begin(), nearby.end(),
[this, &here]( const tripoint_bub_ms & pt ) {
std::optional<tripoint_bub_ms> target_spot = find_point_closest_first( src, 0, range, [this,
&here]( const tripoint_bub_ms & pt ) {
return here.tr_at( pt ).id == tr_practice_target && this->you->sees( pt );
} );
if( target_spot != nearby.end() ) {

if( target_spot != std::nullopt ) {
return *target_spot;
}

Expand Down

0 comments on commit 32ab239

Please sign in to comment.