backport: nodes flooder (analyzer) from cs-ebot
analyze: allow to disable goal marking analyze: add cvars descriptions and bounds nav: added optional post path smoothing for astar algorithm nav: now bots will use Dijkstra algo instead of floyd-warshall if memory usage too high (controlled via yb_path_floyd_memory_limit cvar) (fixes #434) nav: vistable are now calculated every frame to prevent game-freeze during loading the game (fixes #434) graph: pracrice reworked to hash table so memory footprint is as low as possible (at cost 5-10% performance loss on practice) (fixes #434) control: bots commands now is case-insensitive bot: major refactoring of bot's code nav: issue warnings about path fail only with debug practice: check for visibility when updating danger index analyzer: suspend any analyzing on change level control: add kickall_ct/kickall_t nav: increase blocked distance in stuck check
This commit is contained in:
parent
bb2e93a539
commit
e7712a551a
31 changed files with 3114 additions and 1722 deletions
277
inc/planner.h
Normal file
277
inc/planner.h
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2023 YaPB Project <yapb@jeefo.net>.
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
const float kInfiniteHeuristic = 65535.0f; // max out heuristic value
|
||||
|
||||
// a* route state
|
||||
CR_DECLARE_SCOPED_ENUM (RouteState,
|
||||
Open = 0,
|
||||
Closed,
|
||||
New
|
||||
)
|
||||
|
||||
// a * find path result
|
||||
CR_DECLARE_SCOPED_ENUM (AStarResult,
|
||||
Success = 0,
|
||||
Failed,
|
||||
InternalError,
|
||||
)
|
||||
|
||||
// node added
|
||||
using NodeAdderFn = Lambda <bool (int)>;
|
||||
|
||||
// route twin node
|
||||
template <typename HT> struct RouteTwin final {
|
||||
public:
|
||||
int32_t index {};
|
||||
HT heuristic {};
|
||||
|
||||
constexpr RouteTwin () = default;
|
||||
~RouteTwin () = default;
|
||||
|
||||
public:
|
||||
constexpr RouteTwin (const int32_t &ri, const HT &rh) : index (ri), heuristic (rh) {}
|
||||
|
||||
public:
|
||||
constexpr bool operator < (const RouteTwin &rhs) const {
|
||||
return heuristic < rhs.heuristic;
|
||||
}
|
||||
|
||||
constexpr bool operator > (const RouteTwin &rhs) const {
|
||||
return heuristic > rhs.heuristic;
|
||||
}
|
||||
};
|
||||
|
||||
// bot heuristic functions for astar planner
|
||||
class Heuristic final {
|
||||
public:
|
||||
using Func = Lambda <float (int, int, int)>;
|
||||
|
||||
public:
|
||||
// least kills and number of nodes to goal for a team
|
||||
static float gfunctionKillsDist (int team, int currentIndex, int parentIndex);;
|
||||
|
||||
// least kills and number of nodes to goal for a team (when with hostage)
|
||||
static float gfunctionKillsDistCTWithHostage (int team, int currentIndex, int parentIndex);;
|
||||
|
||||
// least kills to goal for a team
|
||||
static float gfunctionKills (int team, int currentIndex, int);;
|
||||
|
||||
// least kills to goal for a team (when with hostage)
|
||||
static auto gfunctionKillsCTWithHostage (int team, int currentIndex, int parentIndex) -> float;;
|
||||
|
||||
// least distance for a team
|
||||
static float gfunctionPathDist (int, int currentIndex, int parentIndex);;
|
||||
|
||||
// least distance for a team (when with hostage)
|
||||
static float gfunctionPathDistWithHostage (int, int currentIndex, int parentIndex);;
|
||||
|
||||
public:
|
||||
// square distance heuristic
|
||||
static float hfunctionPathDist (int index, int, int goalIndex);;
|
||||
|
||||
// square distance heuristic with hostages
|
||||
static float hfunctionPathDistWithHostage (int index, int, int goalIndex);;
|
||||
|
||||
// none heuristic
|
||||
static float hfunctionNone (int index, int, int goalIndex);;
|
||||
};
|
||||
|
||||
// A* algorithm for bots
|
||||
class AStarAlgo final {
|
||||
public:
|
||||
using HeuristicFn = Heuristic::Func;
|
||||
|
||||
public:
|
||||
struct Route {
|
||||
float g {}, f {};
|
||||
int parent { kInvalidNodeIndex };
|
||||
RouteState state { RouteState::New };
|
||||
};
|
||||
|
||||
private:
|
||||
BinaryHeap <RouteTwin <float>> m_routeQue {};
|
||||
Array <Route> m_routes {};
|
||||
|
||||
HeuristicFn m_hcalc;
|
||||
HeuristicFn m_gcalc;
|
||||
|
||||
int m_length {};
|
||||
|
||||
Array <int> m_constructedPath;
|
||||
Array <int> m_smoothedPath;
|
||||
|
||||
private:
|
||||
// cleares the currently built route
|
||||
void clearRoute ();
|
||||
|
||||
// can the node can be skipped?
|
||||
bool cantSkipNode (const int a, const int b);
|
||||
|
||||
// do a post-smoothing after a* finished constructing path
|
||||
void postSmooth (NodeAdderFn onAddedNode);
|
||||
|
||||
public:
|
||||
AStarAlgo () = default;
|
||||
~AStarAlgo () = default;
|
||||
|
||||
public:
|
||||
// do the pathfinding
|
||||
AStarResult find (int botTeam, int srcIndex, int destIndex, NodeAdderFn onAddedNode);
|
||||
|
||||
public:
|
||||
// initialize astar with valid path length
|
||||
void init (const int length) {
|
||||
m_length = length;
|
||||
clearRoute ();
|
||||
|
||||
m_constructedPath.reserve (getMaxLength ());
|
||||
m_smoothedPath.reserve (getMaxLength ());
|
||||
}
|
||||
|
||||
// set the g heuristic
|
||||
void setG (HeuristicFn fn) {
|
||||
m_gcalc = fn;
|
||||
}
|
||||
|
||||
// set the h heuristic
|
||||
void setH (HeuristicFn fn) {
|
||||
m_hcalc = fn;
|
||||
}
|
||||
|
||||
// get route max length, route length should not be larger than half of map nodes
|
||||
size_t getMaxLength () const {
|
||||
return m_length / 2;
|
||||
}
|
||||
};
|
||||
|
||||
// floyd-warshall shortest path algorithm
|
||||
class FloydWarshallAlgo final {
|
||||
private:
|
||||
int m_length {};
|
||||
|
||||
public:
|
||||
|
||||
// floyd-warshall matrices
|
||||
struct Matrix {
|
||||
int16_t index { kInvalidNodeIndex };
|
||||
int16_t dist { SHRT_MAX };
|
||||
|
||||
public:
|
||||
Matrix () = default;
|
||||
~Matrix () = default;
|
||||
|
||||
public:
|
||||
Matrix (const int index, const int dist) : index (static_cast <int16_t> (index)), dist (static_cast <int16_t> (dist)) {}
|
||||
};
|
||||
|
||||
private:
|
||||
SmallArray <Matrix> m_matrix {};
|
||||
|
||||
public:
|
||||
FloydWarshallAlgo () = default;
|
||||
~FloydWarshallAlgo () = default;
|
||||
|
||||
private:
|
||||
// create floyd matrics
|
||||
void rebuild ();
|
||||
|
||||
public:
|
||||
// load matrices from disk
|
||||
bool load ();
|
||||
|
||||
// flush matrices to disk, so we will not rebuild them on load same map
|
||||
void save ();
|
||||
|
||||
// do the pathfinding
|
||||
bool find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, int *pathDistance = nullptr);
|
||||
|
||||
public:
|
||||
// distance between two nodes with pathfinder
|
||||
int dist (int srcIndex, int destIndex) {
|
||||
return static_cast <int> ((m_matrix.data () + (srcIndex * m_length) + destIndex)->dist);
|
||||
}
|
||||
};
|
||||
|
||||
// dijkstra shortest path algorithm
|
||||
class DijkstraAlgo final {
|
||||
private:
|
||||
using Route = Twin <int, int>;
|
||||
|
||||
private:
|
||||
Array <int> m_distance {};
|
||||
Array <int> m_parent {};
|
||||
|
||||
BinaryHeap <Route> m_queue {};
|
||||
int m_length {};
|
||||
|
||||
public:
|
||||
DijkstraAlgo () = default;
|
||||
~DijkstraAlgo () = default;
|
||||
|
||||
|
||||
private:
|
||||
// reset pathfinder state to defaults
|
||||
void resetState ();
|
||||
|
||||
public:
|
||||
// initialize dijkstra with valid path length
|
||||
void init (const int length);
|
||||
|
||||
// do the pathfinding
|
||||
bool find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, int *pathDistance = nullptr);
|
||||
|
||||
// distance between two nodes with pathfinder
|
||||
int dist (int srcIndex, int destIndex);
|
||||
};
|
||||
|
||||
// the bot path planner
|
||||
class PathPlanner : public Singleton <PathPlanner> {
|
||||
private:
|
||||
UniquePtr <DijkstraAlgo> m_dijkstra;
|
||||
UniquePtr <FloydWarshallAlgo> m_floyd;
|
||||
UniquePtr <AStarAlgo > m_astar;
|
||||
bool m_memoryLimitHit {};
|
||||
|
||||
public:
|
||||
PathPlanner ();
|
||||
~PathPlanner () = default;
|
||||
|
||||
public:
|
||||
// initialize all planners
|
||||
void init ();
|
||||
|
||||
// has real path distance (instead of distance2d) ?
|
||||
bool hasRealPathDistance () const;
|
||||
|
||||
public:
|
||||
// get the dijkstra algo
|
||||
decltype (auto) getDijkstra () {
|
||||
return m_dijkstra.get ();
|
||||
}
|
||||
|
||||
// get the floyd algo
|
||||
decltype (auto) getFloydWarshall () {
|
||||
return m_floyd.get ();
|
||||
}
|
||||
|
||||
// get the floyd algo
|
||||
decltype (auto) getAStar () {
|
||||
return m_astar.get ();
|
||||
}
|
||||
|
||||
public:
|
||||
// do the pathfinding
|
||||
bool find (int srcIndex, int destIndex, NodeAdderFn onAddedNode, int *pathDistance = nullptr);
|
||||
|
||||
// distance between two nodes with pathfinder
|
||||
int dist (int srcIndex, int destIndex);
|
||||
};
|
||||
|
||||
CR_EXPOSE_GLOBAL_SINGLETON (PathPlanner, planner);
|
||||
Loading…
Add table
Add a link
Reference in a new issue