Major update to Battle AI. AI can localize itself on the navigation mesh. Battle graph is now stored as adjacency matrix instead of adjacency list.

Implemented pathfinding (Floyd-Warshall), AI can now find a path from one sector to another. As a proof of concept: AI can almost follow a player kart. Some code refactoring is in order before further development.

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14340 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
nixt 2013-10-29 05:47:11 +00:00
parent 1c75270912
commit a31c314cb4
9 changed files with 178 additions and 23 deletions

View File

@ -53,11 +53,7 @@ protected:
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
virtual void setSteering (float angle, float dt);
void setControllerName(const std::string &name);

View File

@ -45,6 +45,12 @@ private:
protected:
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
/** Keep a pointer to world. */
LinearWorld *m_world;

View File

@ -30,13 +30,18 @@
#include "karts/skidding.hpp"
#include "karts/skidding_properties.hpp"
#include "modes/three_strikes_battle.hpp"
#include "tracks/nav_poly.hpp"
#include "tracks/navmesh.hpp"
#include <iostream>
BattleAI::BattleAI(AbstractKart *kart,
StateManager::ActivePlayer *player)
: AIBaseController(kart, player)
{
reset();
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES)
{
m_world = dynamic_cast<ThreeStrikesBattle*>(World::getWorld());
@ -50,6 +55,9 @@ BattleAI::BattleAI(AbstractKart *kart,
m_world = NULL;
m_track = NULL;
}
updateCurrentNode();
// Don't call our own setControllerName, since this will add a
// billboard showing 'AIBaseController' to the kar.
Controller::setControllerName("BattleAI");
@ -58,7 +66,82 @@ BattleAI::BattleAI(AbstractKart *kart,
void BattleAI::update(float dt)
{
m_controls->m_accel = 1;
m_controls->m_steer = 0;
return;
m_controls->m_accel = 0.65f;
// m_controls->m_steer = 0;
updateCurrentNode();
handleSteering(dt);
}
void BattleAI::reset()
{
m_current_node = BattleGraph::UNKNOWN_POLY;
}
void BattleAI::updateCurrentNode()
{
//std::cout<<"Current Node \t"<< m_current_node << std::endl;
// if unknown location, search everywhere
if(m_current_node == BattleGraph::UNKNOWN_POLY)
{
int max_count = BattleGraph::get()->getNumNodes();
for(unsigned int i =0; i<max_count; i++)
{
const NavPoly& p = BattleGraph::get()->getPolyOfNode(i);
if(p.pointInPoly(m_kart->getXYZ()))
m_current_node = i;
}
return;
}
if(m_current_node != BattleGraph::UNKNOWN_POLY)
{
//check if the kart is still on the same node
const NavPoly& p = BattleGraph::get()->getPolyOfNode(m_current_node);
if(p.pointInPoly(m_kart->getXYZ())) return;
//if not then check all adjacent polys
const std::vector<int>& adjacents =
NavMesh::get()->getAdjacentPolys(m_current_node);
// Set m_current_node to unknown so that if no adjacent poly checks true
// we look everywhere the next time updateCurrentNode is called. This is
// useful in cases when you are "teleported" to some other poly, ex. rescue
m_current_node = BattleGraph::UNKNOWN_POLY;
for(unsigned int i=0; i<adjacents.size(); i++)
{
const NavPoly& p_temp =
BattleGraph::get()->getPolyOfNode(adjacents[i]);
if(p_temp.pointInPoly(m_kart->getXYZ())) m_current_node = adjacents[i];
}
return;
}
}
void BattleAI::handleSteering(const float dt)
{
Vec3 target_point;
const AbstractKart* kart = m_world->getPlayerKart(0);
int player_node = -1;
for(unsigned int i =0; i<BattleGraph::get()->getNumNodes(); i++)
{
const NavPoly& p = BattleGraph::get()->getPolyOfNode(i);
if(p.pointInPoly(kart->getXYZ()))
player_node = i;
}
if(player_node == BattleGraph::UNKNOWN_POLY || m_current_node == BattleGraph::UNKNOWN_POLY) return;
int next_node = BattleGraph::get()->getNextShortestPathPoly(m_current_node, player_node);
if(next_node == -1) return;
target_point = NavMesh::get()->getCenterOfPoly(next_node);
if(player_node != m_current_node)
setSteering(steerToPoint(target_point),dt);
else
setSteering(steerToPoint(kart->getXYZ()),dt);
}

View File

@ -44,19 +44,31 @@ namespace irr
class BattleAI : public AIBaseController
{
private:
int m_current_node;
void updateCurrentNode();
void handleAcceleration(const float dt) {};
void handleSteering(const float dt);
void handleBraking() {};
protected:
/** Keep a pointer to world. */
ThreeStrikesBattle *m_world;
public:
BattleAI(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
//~BattleAI();
unsigned int getCurrentNode() const { return m_current_node; }
virtual void update (float delta);
virtual void reset () {};
//static void enableDebug() {m_ai_debug = true; }
virtual void reset ();
virtual void crashed(const AbstractKart *k) {};
virtual void crashed(const Material *m) {};
virtual void handleZipper(bool play_sound) {};

View File

@ -35,6 +35,7 @@
#include "io/file_manager.hpp"
#include "input/device_manager.hpp"
#include "items/projectile_manager.hpp"
#include "karts/controller/battle_ai.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/controller/skidding_ai.hpp"
@ -327,6 +328,9 @@ Controller* World::loadAIController(AbstractKart *kart)
{
Controller *controller;
int turn=0;
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES)
turn=1;
// If different AIs 8should be used, adjust turn (or switch randomly
// or dependent on difficulty)
switch(turn)
@ -334,6 +338,9 @@ Controller* World::loadAIController(AbstractKart *kart)
case 0:
controller = new SkiddingAI(kart);
break;
case 1:
controller = new BattleAI(kart);
break;
default:
Log::warn("World", "Unknown AI, using default.");
controller = new SkiddingAI(kart);

View File

@ -28,7 +28,7 @@
#include "tracks/navmesh.hpp"
#include "utils/vec3.hpp"
const int BattleGraph::UNKNOWN_POLY = -1;
BattleGraph * BattleGraph::m_battle_graph = NULL;
BattleGraph::BattleGraph(const std::string &navmesh_file_name)
@ -36,12 +36,15 @@ BattleGraph::BattleGraph(const std::string &navmesh_file_name)
NavMesh::create(navmesh_file_name);
m_navmesh_file = navmesh_file_name;
buildGraph(NavMesh::get());
computeFloydWarshall();
}
void BattleGraph::buildGraph(NavMesh* navmesh)
{
unsigned int n_polys = navmesh->getNumberOfPolys();
m_graph.resize(n_polys);
//m_graph.resize(n_polys);
m_distance_matrix = std::vector< std::vector<float>> (n_polys, std::vector<float>(n_polys, 999.9f));
for(unsigned int i=0; i<n_polys; i++)
{
NavPoly currentPoly = navmesh->getNavPoly(i);
@ -50,12 +53,49 @@ void BattleGraph::buildGraph(NavMesh* navmesh)
{
Vec3 adjacentPolyCenter = navmesh->getCenterOfPoly(adjacents[j]);
float distance = Vec3(adjacentPolyCenter - currentPoly.getCenter()).length_2d();
m_graph[i].push_back(std::make_pair(adjacents[i], distance));
//m_graph[i].push_back(std::make_pair(adjacents[j], distance));
m_distance_matrix[i][adjacents[j]] = distance;
}
m_distance_matrix[i][i] = 0.0f;
}
}
// computes all pair shortest path
// iteratively updates the m_next_poly
void BattleGraph::computeFloydWarshall()
{
int n = getNumNodes();
// initialize m_next_poly with unknown_poly so that if no path is found b/w i and j
// then m_next_poly[i][j] = -1 (UNKNOWN_POLY)
// AI must check this
m_next_poly = std::vector< std::vector<int>> (n, std::vector<int>(n,BattleGraph::UNKNOWN_POLY));
for(unsigned int k=0; k<n; k++)
{
for(unsigned int i=0; i<n; i++)
{
for(unsigned int j=0; j<n; j++)
{
if( (m_distance_matrix[i][k] + m_distance_matrix[k][j]) < m_distance_matrix[i][j])
{
m_distance_matrix[i][j] = m_distance_matrix[i][k] + m_distance_matrix[k][j];
m_next_poly[i][j] = k;
}
else if((m_distance_matrix[i][k] + m_distance_matrix[k][j]) == m_distance_matrix[i][j])
{
m_next_poly[i][j]= j;
}
}
}
}
}
BattleGraph::~BattleGraph(void)
{
NavMesh::destroy();
@ -93,7 +133,7 @@ void BattleGraph::createMesh(bool enable_transparency,
// Now add all polygons
int i=0;
for(unsigned int count=0; count<m_graph.size(); count++)
for(unsigned int count=0; count<getNumNodes(); count++)
{
///compute colors
if(!track_color)
@ -163,7 +203,7 @@ void BattleGraph::createMesh(bool enable_transparency,
void BattleGraph::createDebugMesh()
{
if(m_graph.size()<=0) return; // no debug output if not graph
if(getNumNodes()<=0) return; // no debug output if not graph
createMesh(/*enable_transparency*/true);
m_node = irr_driver->addMesh(m_mesh);
@ -175,7 +215,9 @@ void BattleGraph::createDebugMesh()
void BattleGraph::cleanupDebugMesh()
{
irr_driver->removeNode(m_node);
if(m_node != NULL)
irr_driver->removeNode(m_node);
m_node = NULL;
// No need to call irr_driber->removeMeshFromCache, since the mesh
// was manually made and so never added to the mesh cache.

View File

@ -43,7 +43,8 @@ private:
static BattleGraph *m_battle_graph;
std::vector< std::vector< std::pair<int,float> > > m_graph;
std::vector< std::vector< float > > m_distance_matrix;
std::vector< std::vector< int > > m_next_poly;
/** For debug mode only: the node of the debug mesh. */
scene::ISceneNode *m_node;
/** For debug only: the mesh of the debug mesh. */
@ -56,6 +57,7 @@ private:
void buildGraph(NavMesh*);
void computeFloydWarshall();
void createMesh(bool enable_transparency=false,
const video::SColor *track_color=NULL);
@ -64,6 +66,8 @@ private:
public:
static const int UNKNOWN_POLY;
static BattleGraph *get() { return m_battle_graph; }
static void create(const std::string &navmesh_file_name)
@ -82,7 +86,12 @@ public:
}
}
unsigned int getNumNodes() const { return m_graph.size(); }
unsigned int getNumNodes() const { return m_distance_matrix.size(); }
const NavPoly& getPolyOfNode(int i) const
{ return NavMesh::get()->getNavPoly(i); }
const int & getNextShortestPathPoly(int i, int j) const
{ return m_next_poly[i][j]; }
void createDebugMesh();
void cleanupDebugMesh();

View File

@ -22,7 +22,7 @@
#include "tracks/navmesh.hpp"
#include <algorithm>
#include <iostream>
NavPoly::NavPoly(const std::vector<int> &polygonVertIndices,
const std::vector<int> &adjacentPolygonIndices)
@ -33,7 +33,7 @@ NavPoly::NavPoly(const std::vector<int> &polygonVertIndices,
std::vector<Vec3> xyz_points = getVertices();
Vec3 temp;
Vec3 temp(0.0f,0.0f,0.0f);
for(unsigned int i=0; i<xyz_points.size(); i++)
temp = temp + xyz_points[i];

View File

@ -101,7 +101,7 @@ NavMesh::NavMesh(const std::string &filename)
std::vector<int> polygonVertIndices;
std::vector<int> adjacentPolygonIndices;
xml_node_node->get("indices", &polygonVertIndices);
xml_node_node->get("adjacent", &adjacentPolygonIndices);
xml_node_node->get("adjacents", &adjacentPolygonIndices);
NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices);
m_polys.push_back(*np);
m_n_polys++;