Merge branch 'dijkstra-test'

This commit is contained in:
hiker 2016-07-25 16:42:24 +10:00
commit c64733e3d0
7 changed files with 223 additions and 34 deletions

View File

@ -194,6 +194,7 @@
#include "states_screens/state_manager.hpp"
#include "states_screens/user_screen.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
#include "tracks/battle_graph.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/command_line.hpp"
@ -1843,12 +1844,12 @@ void runUnitTests()
{
Log::info("UnitTest", "Starting unit testing");
Log::info("UnitTest", "=====================");
Log::info("UnitTest", "- GraphicsRestrictions");
Log::info("UnitTest", "GraphicsRestrictions");
GraphicsRestrictions::unitTesting();
Log::info("UnitTest", " - NetworkString");
Log::info("UnitTest", "NetworkString");
NetworkString::unitTesting();
Log::info("UnitTest", " - Easter detection");
Log::info("UnitTest", "Easter detection");
// Test easter mode: in 2015 Easter is 5th of April - check with 0 days
// before and after
int saved_easter_mode = UserConfigParams::m_easter_ear_mode;
@ -1880,9 +1881,12 @@ void runUnitTests()
UserConfigParams::m_easter_ear_mode = saved_easter_mode;
Log::info("UnitTest", " - Kart characteristics");
Log::info("UnitTest", "Kart characteristics");
CombinedCharacteristic::unitTesting();
Log::info("UnitTest", "Battle Graph");
BattleGraph::unitTesting();
Log::info("UnitTest", "=====================");
Log::info("UnitTest", "Testing successful ");
Log::info("UnitTest", "=====================");

View File

@ -34,6 +34,7 @@
#include "karts/controller/soccer_ai.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/battle_graph.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"

View File

@ -17,9 +17,6 @@
#include "modes/three_strikes_battle.hpp"
#include <string>
#include <IMeshSceneNode.h>
#include "main_loop.hpp"
#include "audio/music_manager.hpp"
#include "config/user_config.hpp"
@ -31,10 +28,14 @@
#include "karts/kart_properties.hpp"
#include "physics/physics.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/battle_graph.hpp"
#include "tracks/track.hpp"
#include "tracks/track_object_manager.hpp"
#include "utils/constants.hpp"
#include <string>
#include <IMeshSceneNode.h>
//-----------------------------------------------------------------------------
/** Constructor. Sets up the clock mode etc.
*/

View File

@ -23,12 +23,17 @@
#include <IMeshSceneNode.h>
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "items/item_manager.hpp"
#include "race/race_manager.hpp"
#include "tracks/navmesh.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/log.hpp"
#include <queue>
const int BattleGraph::UNKNOWN_POLY = -1;
BattleGraph * BattleGraph::m_battle_graph = NULL;
@ -36,15 +41,19 @@ BattleGraph * BattleGraph::m_battle_graph = NULL;
* then runs shortest path algorithm to find and store paths to be used
* by the AI. */
BattleGraph::BattleGraph(const std::string &navmesh_file_name,
const XMLNode& node)
const XMLNode *node)
{
m_items_on_graph.clear();
NavMesh::create(navmesh_file_name);
m_navmesh_file = navmesh_file_name;
buildGraph(NavMesh::get());
computeFloydWarshall();
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
// Compute shortest distance from all nodes
for(unsigned int i=0; i < NavMesh::get()->getNumberOfPolys(); i++)
computeDijkstra(i);
if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
loadGoalNodes(node);
} // BattleGraph
@ -60,21 +69,23 @@ BattleGraph::~BattleGraph(void)
GraphStructure::destroyRTT();
} // ~BattleGraph
// -----------------------------------------------------------------------------
/** Builds a graph from an existing NavMesh. The graph is stored as an adjacency
* matrix. */
// ----------------------------------------------------------------------------
/** Builds a graph from an existing NavMesh. The graph is stored as an
* adjacency matrix. */
void BattleGraph::buildGraph(NavMesh* navmesh)
{
unsigned int n_polys = navmesh->getNumberOfPolys();
m_distance_matrix = std::vector< std::vector<float> > (n_polys, std::vector<float>(n_polys, 9999.9f));
m_distance_matrix = std::vector< std::vector<float> >
(n_polys, std::vector<float>(n_polys, 9999.9f));
for(unsigned int i=0; i<n_polys; i++)
{
NavPoly currentPoly = navmesh->getNavPoly(i);
std::vector<int> adjacents = navmesh->getAdjacentPolys(i);
const std::vector<int> &adjacents = navmesh->getAdjacentPolys(i);
for(unsigned int j=0; j<adjacents.size(); j++)
{
Vec3 diff = navmesh->getCenterOfPoly(adjacents[j]) - currentPoly.getCenter();
Vec3 diff = navmesh->getCenterOfPoly(adjacents[j])
- currentPoly.getCenter();
float distance = diff.length();
m_distance_matrix[i][adjacents[j]] = distance;
//m_distance_matrix[adjacents[j]][i] = distance;
@ -82,14 +93,88 @@ void BattleGraph::buildGraph(NavMesh* navmesh)
m_distance_matrix[i][i] = 0.0f;
}
// Allocate and initialise the previous node data structure:
unsigned int n = getNumNodes();
m_parent_poly = std::vector< std::vector<int> >
(n, std::vector<int>(n, BattleGraph::UNKNOWN_POLY));
for(unsigned int i=0; i<n; i++)
{
for(unsigned int j=0; j<n; j++)
{
if(i == j || m_distance_matrix[i][j]>=9899.9f)
m_parent_poly[i][j]=-1;
else
m_parent_poly[i][j] = i;
} // for j
} // for i
} // buildGraph
// -----------------------------------------------------------------------------
/** computeFloydWarshall() computes the shortest distance between any two nodes.
* At the end of the computation, m_distance_matrix[i][j] stores the shortest path
* distance from i to j and m_parent_poly[i][j] stores the last vertex visited on the
* shortest path from i to j before visiting j. Suppose the shortest path from i to j is
* i->......->k->j then m_parent_poly[i][j] = k
// ----------------------------------------------------------------------------
/** Dijkstra shortest path computation. It computes the shortest distance from
* the specified node 'source' to all other nodes. At the end of the
* computation, m_distance_matrix[i][j] stores the shortest path distance from
* source to j and m_parent_poly[source][j] stores the last vertex visited on
* the shortest path from i to j before visiting j. Suppose the shortest path
* from i to j is i->......->k->j then m_parent_poly[i][j] = k
*/
void BattleGraph::computeDijkstra(int source)
{
// Stores the distance (float) to 'source' from a specified node (int)
typedef std::pair<int, float> IndDistPair;
class Shortest
{
public:
bool operator()(const IndDistPair &p1, const IndDistPair &p2)
{
return p1.second > p2.second;
}
};
std::priority_queue<IndDistPair, std::vector<IndDistPair>, Shortest> queue;
IndDistPair begin(source, 0.0f);
queue.push(begin);
const unsigned int n=getNumNodes();
std::vector<bool> visited;
visited.resize(n, false);
NavMesh *navmesh = NavMesh::get();
while(!queue.empty())
{
// Get element with shortest path
IndDistPair current = queue.top();
queue.pop();
int cur_index = current.first;
if(visited[cur_index]) continue;
visited[cur_index] = true;
const NavPoly &current_poly = navmesh->getNavPoly(cur_index);
const std::vector<int> &adjacents = current_poly.getAdjacents();
for(unsigned int j=0; j<adjacents.size(); j++)
{
int adjacent = adjacents[j];
// Distance already computed, can be ignored
if(visited[adjacent]) continue;
float new_dist = current.second + m_distance_matrix[cur_index][adjacent];
if(new_dist < m_distance_matrix[source][adjacent])
{
m_distance_matrix[source][adjacent] = new_dist;
m_parent_poly[source][adjacent] = cur_index;
}
IndDistPair pair(adjacent, new_dist);
queue.push(pair);
}
}
} // computeDijkstra
// ----------------------------------------------------------------------------
/** THIS FUNCTION IS ONLY USED FOR UNIT-TESTING, to verify that the new
* Dijkstra algorithm gives the same results.
* computeFloydWarshall() computes the shortest distance between any two
* nodes. At the end of the computation, m_distance_matrix[i][j] stores the
* shortest path distance from i to j and m_parent_poly[i][j] stores the last
* vertex visited on the shortest path from i to j before visiting j. Suppose
* the shortest path from i to j is i->......->k->j then
* m_parent_poly[i][j] = k
*/
void BattleGraph::computeFloydWarshall()
{
@ -245,12 +330,12 @@ const bool BattleGraph::differentNodeColor(int n, NodeColor* c) const
} // differentNodeColor
// -----------------------------------------------------------------------------
void BattleGraph::loadGoalNodes(const XMLNode& node)
void BattleGraph::loadGoalNodes(const XMLNode *node)
{
m_red_node.clear();
m_blue_node.clear();
const XMLNode *check_node = node.getNode("checks");
const XMLNode *check_node = node->getNode("checks");
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
{
const XMLNode *goal = check_node->getNode(i);
@ -278,3 +363,97 @@ void BattleGraph::loadGoalNodes(const XMLNode& node)
}
}
} // loadGoalNodes
// ============================================================================
/** Unit testing for battle graph distance and parent node computation.
* Instead of using hand-tuned test cases we use the tested, verified and
* easier to understand Floyd-Warshall algorithm to compute the distances,
* and check if the (significanty faster) Dijkstra algorithm gives the same
* results. For now we use the cave mesh as test case.
*/
void BattleGraph::unitTesting()
{
Track *track = track_manager->getTrack("cave");
std::string navmesh_file_name=track->getTrackFile("navmesh.xml");
double s = StkTime::getRealTime();
BattleGraph *bg = new BattleGraph(navmesh_file_name);
double e = StkTime::getRealTime();
Log::error("Time", "Dijkstra %lf", e-s);
// Save the Dijkstra results
std::vector< std::vector< float > > distance_matrix = bg->m_distance_matrix;
std::vector< std::vector< int > > parent_poly = bg->m_parent_poly;
bg->buildGraph(NavMesh::get());
// Now compute results with Floyd-Warshall
s = StkTime::getRealTime();
bg->computeFloydWarshall();
e = StkTime::getRealTime();
Log::error("Time", "Floyd-Warshall %lf", e-s);
int error_count = 0;
for(unsigned int i=0; i<bg->m_distance_matrix.size(); i++)
{
for(unsigned int j=0; j<bg->m_distance_matrix[i].size(); j++)
{
if(bg->m_distance_matrix[i][j] - distance_matrix[i][j] > 0.001f)
{
Log::error("BattleGraph",
"Incorrect distance %d, %d: Dijkstra: %f F.W.: %f",
i, j, distance_matrix[i][j], bg->m_distance_matrix[i][j]);
error_count++;
} // if distance is too different
// Unortunately it happens frequently that there are different
// shortest path with the same length. And Dijkstra might find
// a different path then Floyd-Warshall. So the test for parent
// polygon often results in false positives, so it is disabled,
// but I leave the code in place in case it is useful for some
// debugging in the feature
#undef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
#ifdef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES
if(bg->m_parent_poly[i][j] != parent_poly[i][j])
{
error_count++;
std::vector<int> dijkstra_path = getPathFromTo(i, j, parent_poly);
std::vector<int> floyd_path = getPathFromTo(i, j, bg->m_parent_poly);
if(dijkstra_path.size()!=floyd_path.size())
{
Log::error("BattleGraph",
"Incorrect path length %d, %d: Dijkstra: %d F.W.: %d",
i, j, parent_poly[i][j], bg->m_parent_poly[i][j]);
continue;
}
Log::error("BattleGraph", "Path problems from %d to %d:",
i, j);
for (unsigned k = 0; k < dijkstra_path.size(); k++)
{
if(dijkstra_path[k]!=floyd_path[k])
Log::error("BattleGraph", "%d/%d dijkstra: %d floyd %d",
k, dijkstra_path.size(), dijkstra_path[k],
floyd_path[k]);
} // for k<dijkstra_path.size()
} // if dijkstra parent_poly != floyd parent poly
#endif
} // for j
} // for i
} // unitTesting
// ----------------------------------------------------------------------------
/** Determines the full path from 'from' to 'to' and returns it in a
* std::vector (in reverse order). Used only for unit testing.
*/
std::vector<int> BattleGraph::getPathFromTo(int from, int to,
const std::vector< std::vector< int > > parent_poly)
{
std::vector<int> path;
path.push_back(to);
while(from!=to)
{
to = parent_poly[from][to];
path.push_back(to);
}
return path;
} // getPathFromTo

View File

@ -63,9 +63,9 @@ private:
void buildGraph(NavMesh*);
void computeFloydWarshall();
void loadGoalNodes(const XMLNode& node);
void loadGoalNodes(const XMLNode *node);
BattleGraph(const std::string &navmesh_file_name, const XMLNode& node);
BattleGraph(const std::string &navmesh_file_name, const XMLNode *node=NULL);
~BattleGraph(void);
// ------------------------------------------------------------------------
@ -86,17 +86,27 @@ private:
{ return false; }
// ------------------------------------------------------------------------
virtual const bool differentNodeColor(int n, NodeColor* c) const;
void computeDijkstra(int n);
static std::vector<int> getPathFromTo(int from, int to,
const std::vector< std::vector< int > > parent_poly);
public:
static const int UNKNOWN_POLY;
void findItemsOnGraphNodes();
int pointToNode(const int cur_node,
const Vec3& cur_point,
bool ignore_vertical) const;
static void unitTesting();
/** Returns the one instance of this object. */
static BattleGraph *get() { return m_battle_graph; }
// ----------------------------------------------------------------------
/** Asserts that no BattleGraph instance exists. Then
* creates a BattleGraph instance. */
static void create(const std::string &navmesh_file_name,
const XMLNode& node)
const XMLNode *node)
{
assert(m_battle_graph==NULL);
m_battle_graph = new BattleGraph(navmesh_file_name, node);
@ -149,11 +159,6 @@ public:
void insertItems(Item* item, int polygon)
{ m_items_on_graph.push_back(std::make_pair(item, polygon)); }
// ------------------------------------------------------------------------
void findItemsOnGraphNodes();
// ------------------------------------------------------------------------
int pointToNode(const int cur_node,
const Vec3& cur_point,
bool ignore_vertical) const;
}; //BattleGraph
#endif

View File

@ -672,7 +672,7 @@ void Track::startMusic() const
*/
void Track::loadBattleGraph(const XMLNode &node)
{
BattleGraph::create(m_root+"navmesh.xml", node);
BattleGraph::create(m_root+"navmesh.xml", &node);
if(BattleGraph::get()->getNumNodes()==0)
{

View File

@ -43,7 +43,6 @@ class ModelDefinitionLoader;
#include "items/item.hpp"
#include "scriptengine/script_engine.hpp"
#include "tracks/quad_graph.hpp"
#include "tracks/battle_graph.hpp"
#include "utils/aligned_array.hpp"
#include "utils/translation.hpp"
#include "utils/vec3.hpp"