Commit 718479c1 authored by Dmitry Suzdalev's avatar Dmitry Suzdalev
Browse files

I had lot of joy with klines development today :).

That's because I have just implemented an "A*" pathfinding algorithm.
I found excellent beginners tutorial at www.gamedev.net.

I wonder if it is needed in other kdegames?
If yes, I think, it is easy to make it more general for other's use.

I failed to understand how the pathfinding is
implemented in the old klines code, there were
no comments, just a pure code with some misterious vars.

So I went to gamedev and found how to do that myself :).

Path is found but not used yet. That's what I'll do next ;).

svn path=/trunk/KDE/kdegames/klines/; revision=616115
parent e9d7dc0b
......@@ -24,6 +24,22 @@
#include "scene.h"
#include "ballitem.h"
#include <kdebug.h>
#include <math.h> // for pow, sqrt
// Needed by A* pathfinding algorithm
struct PathNode
{
FieldPos pos;
PathNode *parent;
int G;
float H;
float F;
PathNode( const FieldPos& fpos, PathNode* p = 0, int g=0, int h=0 )
: pos(fpos), parent(p), G(g), H(h), F(g+h) { }
};
KLinesAnimator::KLinesAnimator( KLinesScene* scene )
: m_scene(scene), m_movingBall(0)
{
......@@ -33,6 +49,7 @@ KLinesAnimator::KLinesAnimator( KLinesScene* scene )
void KLinesAnimator::animateMove( const FieldPos& from, const FieldPos& to )
{
findPath(from, to);
m_from = from;
m_to = to;
......@@ -51,6 +68,148 @@ void KLinesAnimator::animateMove( const FieldPos& from, const FieldPos& to )
m_timeLine.start();
}
void KLinesAnimator::findPath( const FieldPos& from, const FieldPos& to )
{
// Implementation of A* pathfinding algorithm
// Thanks to Patrick Lester for excellent tutorial on gamedev.net.
// See http://www.gamedev.net/reference/articles/article2003.asp
QList<PathNode*> openList;
QList<PathNode*> closedList;
openList.append( new PathNode(from) );
PathNode *curNode=0;
bool pathFound = false;
while(true)
{
// find the square with lowes F(=G+H) on the open list
PathNode *minF = openList.at(0);
for(int i=1; i<openList.count(); ++i)
{
if( openList.at(i)->F < minF->F )
minF = openList.at(i);
}
kDebug() << "minF:" << minF->F << endl;
// move it to closed list
closedList.append(minF);
openList.removeAll(minF);
curNode = minF;
kDebug() << "Current node (" << curNode->pos.x << "," << curNode->pos.y << ")" << endl;
// for each of adjasent 4 squares (upper,lower, on the left and on the right)...
QList<FieldPos> adjasentSquares;
int x = curNode->pos.x;
int y = curNode->pos.y;
if( x != 0 ) adjasentSquares.append( FieldPos(x-1,y) );
if( y != 0 ) adjasentSquares.append( FieldPos(x,y-1) );
if( x != FIELD_SIZE-1 ) adjasentSquares.append( FieldPos(x+1,y) );
if( y != FIELD_SIZE-1 ) adjasentSquares.append( FieldPos(x,y+1) );
foreach( FieldPos pos, adjasentSquares )
{
if( m_scene->ballAt(pos) != 0 ) // skip non-walkable cells
{
kDebug() << "node (" << pos.x << "," << pos.y << ") contains ball - skipping" << endl;
continue;
}
kDebug() << "looking at adjasent node (" << pos.x << "," << pos.y << ")" << endl;
// skip if closed list contains this square
bool found=false;
for(int i=0;i<closedList.count(); ++i)
if( closedList.at(i)->pos == pos )
{
found = true;
break;
}
if(found)
{
kDebug() << "node (" << pos.x << "," << pos.y << ") is in closed list - skipping" << endl;
continue;
}
// search for node with position 'pos' in openList
PathNode *node = 0;
for(int i=0;i<openList.count(); ++i)
if( openList.at(i)->pos == pos )
{
node = openList.at(i);
break;
}
if(!node) // not found
{
kDebug() << "it is not in open list. adding" << endl;
node = new PathNode( pos );
node->parent = curNode;
node->G = curNode->G + 10;
// h is manhattanLength from node to target square
node->H = sqrt( pow( (to.x - pos.x)*10, 2 ) + pow( (to.y - pos.y)*10, 2 ) );
node->F = node->G+node->H;
openList.append( node );
kDebug() << "G:" << node->G << " H:" << node->H << endl;
}
else
{
// check if this path to square is better
kDebug() << "it is in open list. cheking if this path is better..." << endl;
if( curNode->G + 10 < node->G )
{
// yup, it's better, reparent and recalculate G,F
node->parent = curNode;
node->G = curNode->G + 10;
node->F = node->G + node->H;
}
}
kDebug() << "====" << endl;
} // foreach
// exit conditions:
// a) if closeList contains "to"
// b) we can't find "to" in closedList and openlist is empty => no path exists
bool found=false;
for(int i=0;i<closedList.count(); ++i)
if( closedList.at(i)->pos == to )
{
found = true;
// let's save last node in curNode variable
curNode = closedList.at(i);
break;
}
if(found)
{
pathFound = true;
break; // while
}
else if(openList.isEmpty())
{
pathFound = false;
break;
}
}
if(pathFound)
{
kDebug() << "====" << endl;
kDebug() << "and the path is:" <<endl;
// restoring path starting from last node:
PathNode* node = curNode;
while(node)
{
kDebug() << "(" << node->pos.x << "," << node->pos.y << ")" << endl;
node = node->parent;
}
}
else
kDebug() << "no path found!" << endl;
}
void KLinesAnimator::animFrameChanged(int frame)
{
QPointF p1 = m_scene->fieldToPix(m_from);
......
......@@ -41,6 +41,10 @@ signals:
private slots:
void animFrameChanged(int);
private:
/**
* Implements A* pathfinding algorithm.
*/
void findPath(const FieldPos& from, const FieldPos& to);
QTimeLine m_timeLine;
KLinesScene* m_scene;
FieldPos m_from;
......
......@@ -34,6 +34,10 @@ struct FieldPos
int y;
FieldPos( int _x=-1, int _y=-1) : x(_x), y(_y) { }
bool isValid() const { return (x != -1 && y != -1); }
bool operator==(const FieldPos& other)
{
return (x == other.x && y == other.y);
}
};
#endif
......@@ -52,6 +52,10 @@ public:
* is no item there
*/
BallItem* ballAt( const FieldPos& pos ) { return m_field[pos.x][pos.y]; }
/**
* Overloaded above function
*/
BallItem* ballAt( int x, int y ) { return m_field[x][y]; }
/**
* Field coords to pixel coords
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment