1) Added 2 different findNonCrashingPoint algorthm - one which fixes a known

bug in the default algorithm, and one replacement. Unforatunately, the old
   (and somewhat buggy) algorithm is stil the best.
2) Improved handling of 90 degree corner for the default AI. Now the karts shouldn't
   have any more endless rescue loops.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11669 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk
2012-10-08 06:04:30 +00:00
parent c7ab762799
commit 376252d953
2 changed files with 163 additions and 85 deletions

View File

@@ -128,34 +128,20 @@ SkiddingAI::SkiddingAI(AbstractKart *kart)
setSkiddingFraction(2.0f);
break;
}
m_use_new_aim_point_selection = false;
m_point_selection_algorithm = PSA_DEFAULT;
setControllerName("Skidding");
// Use this define in order to compare the impact of collecting items
#undef COMPARE_AIS
#ifdef COMPARE_AIS
std::string name("");
if(m_kart->getWorldKartId() % 2 ==0)
m_point_selection_algorithm = m_kart->getWorldKartId() % 2
? PSA_DEFAULT : PSA_FIXED;
switch(m_point_selection_algorithm)
{
m_item_behaviour = ITEM_COLLECT_NONE;
name += "Skidding";
}
else
{
m_item_behaviour = ITEM_COLLECT_PRIORITY;
name += "Collect";
}
if(m_kart->getWorldKartId() % 1 ==1)
{
m_use_new_aim_point_selection = true;
name += " new";
}
else
{
m_use_new_aim_point_selection = false;
case PSA_FIXED : name = "Fixed"; break;
case PSA_NEW : name = "New"; break;
case PSA_DEFAULT : name = "Default"; break;
}
setControllerName(name);
#endif
@@ -166,9 +152,15 @@ SkiddingAI::SkiddingAI(AbstractKart *kart)
m_false_start_probability = 0.0f;
}
#ifdef AI_DEBUG
int k = m_kart->getWorldKartId() % 2;
video::SColor col_debug(128, k ? 128 : 0, k ? 0 : 128, 0);
m_debug_sphere = irr_driver->addSphere(1.0f, col_debug);
for(unsigned int i=0; i<4; i++)
{
video::SColor col_debug(128, i==0 ? 128 : 0,
i==1 ? 128 : 0,
i==2 ? 128 : 0);
m_debug_sphere[i] = irr_driver->addSphere(1.0f, col_debug);
m_debug_sphere[i]->setVisible(false);
}
m_debug_sphere[m_point_selection_algorithm]->setVisible(true);
m_item_sphere = irr_driver->addSphere(1.0f);
#define CURVE_PREDICT1 0
@@ -222,7 +214,8 @@ SkiddingAI::SkiddingAI(AbstractKart *kart)
SkiddingAI::~SkiddingAI()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
for(unsigned int i=0; i<3; i++)
irr_driver->removeNode(m_debug_sphere[i]);
irr_driver->removeNode(m_item_sphere);
for(unsigned int i=0; i<NUM_CURVES; i++)
{
@@ -518,6 +511,7 @@ void SkiddingAI::handleSteering(float dt)
//Reaction to being outside of the road
float side_dist =
m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() );
if( fabsf(side_dist) >
0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f )
{
@@ -525,7 +519,7 @@ void SkiddingAI::handleSteering(float dt)
.getCenter());
#ifdef AI_DEBUG
m_debug_sphere->setPosition(QuadGraph::get()->getQuadOfNode(next)
m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next)
.getCenter().toIrrVector());
std::cout << "- Outside of road: steer to center point." <<
std::endl;
@@ -574,12 +568,17 @@ void SkiddingAI::handleSteering(float dt)
Vec3 aim_point;
int last_node = QuadGraph::UNKNOWN_SECTOR;
if(m_use_new_aim_point_selection)
findNonCrashingPoint2(&aim_point, &last_node);
else
findNonCrashingPoint(&aim_point, &last_node);
switch(m_point_selection_algorithm)
{
case PSA_FIXED : findNonCrashingPointFixed(&aim_point, &last_node);
break;
case PSA_NEW: findNonCrashingPointNew(&aim_point, &last_node);
break;
case PSA_DEFAULT:findNonCrashingPointDefault(&aim_point, &last_node);
break;
}
#ifdef AI_DEBUG
m_debug_sphere->setPosition(aim_point.toIrrVector());
m_debug_sphere[m_point_selection_algorithm]->setPosition(aim_point.toIrrVector());
#endif
#ifdef AI_DEBUG_KART_AIM
const Vec3 eps(0,0.5f,0);
@@ -1507,7 +1506,8 @@ void SkiddingAI::handleNitroAndZipper()
// (i.e. more than 2) nitro, use it.
// -------------------------------------------------
const unsigned int num_karts = m_world->getCurrentNumKarts();
if(m_kart->getPosition()== (int)num_karts && m_kart->getEnergy()>2.0f)
if(m_kart->getPosition()== (int)num_karts &&
num_karts>1 && m_kart->getEnergy()>2.0f)
{
m_controls->m_nitro = true;
return;
@@ -1697,7 +1697,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos )
* driven to in a straight line.
* \param last_node The graph node index in which the aim_position is.
*/
void SkiddingAI::findNonCrashingPoint2(Vec3 *result, int *last_node)
void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
{
*last_node = m_next_node_index[m_track_node];
const core::vector2df xz = m_kart->getXYZ().toIrrVector2d();
@@ -1748,6 +1748,8 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result, int *last_node)
m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps);
#endif
}
else
break;
// Test if new right point is to the left of the right line. If
// so, a new right line is defined.
@@ -1766,56 +1768,18 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result, int *last_node)
#endif
right.end = p;
}
else
break;
*last_node = next_sector;
} // while
// Now look for the next curve to find out to which side of the
// track the AI should aim at
*last_node = m_track_node;
int count = 0;
while(1)
{
GraphNode::DirectionType dir;
unsigned int last;
unsigned int succ = m_successor_index[*last_node];
const GraphNode &gn = QuadGraph::get()->getNode(*last_node);
gn.getDirectionData(succ, &dir, &last);
if(dir==GraphNode::DIR_LEFT)
{
core::vector2df diff = left.end - right.end;
diff.normalize();
diff *= m_kart->getKartWidth()*0.5f;
*result = Vec3(left.end.X - diff.X,
m_kart->getXYZ().getY(),
left.end.Y - diff.Y);
return;
}
else if(dir==GraphNode::DIR_RIGHT)
{
core::vector2df diff = right.end - left.end;
diff.normalize();
diff *= m_kart->getKartWidth()*0.5f;
*result = Vec3(right.end.X-diff.X,
m_kart->getXYZ().getY(),
right.end.Y-diff.Y);
return;
}
//Vec3 ppp(0.5f*(left.end.X+right.end.X),
// m_kart->getXYZ().getY(),
// 0.5f*(left.end.Y+right.end.Y));
//*result = ppp;
// We are going straight. Determine point to aim for based on the
// direction of the track after the straight section
*last_node = m_next_node_index[last];
count++;
if(count>1)
printf("That shouldn't happen %d!!!\n", count);
}
Vec3 ppp(0.5f*(left.end.X+right.end.X),
m_kart->getXYZ().getY(),
0.5f*(left.end.Y+right.end.Y));
*result = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
*result = ppp;
} // findNonCrashingPoint2
} // findNonCrashingPointNew
//-----------------------------------------------------------------------------
/** Find the sector that at the longest distance from the kart, that can be
@@ -1826,7 +1790,7 @@ void SkiddingAI::findNonCrashingPoint2(Vec3 *result, int *last_node)
* driven to in a straight line.
* \param last_node The graph node index in which the aim_position is.
*/
void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node)
void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
{
#ifdef AI_DEBUG_KART_HEADING
const Vec3 eps(0,0.5f,0);
@@ -1891,7 +1855,113 @@ void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node)
*last_node = target_sector;
} // for i<100
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
} // findNonCrashingPoint
} // findNonCrashingPointFixed
//-----------------------------------------------------------------------------
/** This is basically the original AI algorithm. It is clearly buggy:
* 1) the test:
* distance + m_kart_width * 0.5f
* > QuadGraph::get()->getNode(*last_node).getPathWidth() )\
* is incorrect, it should compare with getPathWith*0.5f (since distance
* is the distance from the center, i.e. it is half the path width if
* the point is at the edge).
* 2) the test:
* QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
* *last_node );
* in the for loop tests always against distance from the same
* graph node (*last_node), while de-fact the loop will test points
* on various graph nodes.
* This results in this algorithm often picking points to aim at that
* would actually force the kart off track. But in reality the kart has
* to turn (and does not immediate in one frame change its direction)
* which takes some time - so it is actually mostly on track.
* Since this algoritm (so far) ends up with by far the best AI behaviour,
* it is for now the default).
* \param aim_position On exit contains the point the AI should aim at.
* \param last_node On exit contais the graph node the AI is aiming at.
*/
void SkiddingAI::findNonCrashingPointDefault(Vec3 *aim_position, int *last_node)
{
#ifdef AI_DEBUG_KART_HEADING
const Vec3 eps(0,0.5f,0);
m_curve[CURVE_KART]->clear();
m_curve[CURVE_KART]->addPoint(m_kart->getXYZ()+eps);
Vec3 forw(0, 0, 50);
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
#endif
*last_node = m_next_node_index[m_track_node];
float angle = QuadGraph::get()->getAngleToNext(m_track_node,
m_successor_index[m_track_node]);
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
float angle1;
// The original while(1) loop is replaced with a for loop to avoid
// infinite loops (which we had once or twice). Usually the number
// of iterations in the while loop is less than 7.
for(unsigned int j=0; j<100; j++)
{
// target_sector is the sector at the longest distance that we can
// drive to without crashing with the track.
target_sector = m_next_node_index[*last_node];
angle1 = QuadGraph::get()->getAngleToNext(target_sector,
m_successor_index[target_sector]);
// In very sharp turns this algorithm tends to aim at off track points,
// resulting in hitting a corner. So test for this special case and
// prevent a too-far look-ahead in this case
float diff = normalizeAngle(angle1-angle);
if(fabsf(diff)>1.5f)
{
*aim_position = QuadGraph::get()->getQuadOfNode(target_sector)
.getCenter();
return;
}
//direction is a vector from our kart to the sectors we are testing
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
- m_kart->getXYZ();
float len=direction.length_2d();
unsigned int steps = (unsigned int)( len / m_kart_length );
if( steps < 3 ) steps = 3;
// That shouldn't happen, but since we had one instance of
// STK hanging, add an upper limit here (usually it's at most
// 20 steps)
if( steps>1000) steps = 1000;
// Protection against having vel_normal with nan values
if(len>0.0f) {
direction*= 1.0f/len;
}
Vec3 step_coord;
//Test if we crash if we drive towards the target sector
for(unsigned int i = 2; i < steps; ++i )
{
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
*last_node );
float distance = fabsf(step_track_coord[0]);
//If we are outside, the previous node is what we are looking for
if ( distance + m_kart_width * 0.5f
> QuadGraph::get()->getNode(*last_node).getPathWidth() )
{
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node)
.getCenter();
return;
}
}
angle = angle1;
*last_node = target_sector;
} // for i<100
*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter();
} // findNonCrashingPointDefault
//-----------------------------------------------------------------------------
/** Determines the direction of the track ahead of the kart: 0 indicates

View File

@@ -150,8 +150,15 @@ private:
* (which would make it more difficult to avoid items). */
bool m_avoid_item_close;
/** True if the new findNonCrashingPoint2 function should be used. */
bool m_use_new_aim_point_selection;
/** Which of the three Point Selection Algorithms (i.e.
* findNoNCrashingPoint* functions) to use:
* the default (which is actually slightly buggy, but so far best one
* (after handling of 90 degree turns was added)
* the fixed one (which fixes one problem of the buggy algorithm),
* the new algorithm (see findNonCrashingPoint* for details).
* So far the default one has by far the best performance. */
enum {PSA_DEFAULT, PSA_FIXED, PSA_NEW}
m_point_selection_algorithm;
#ifdef DEBUG
/** For skidding debugging: shows the estimated turn shape. */
@@ -159,7 +166,7 @@ private:
/** For debugging purpose: a sphere indicating where the AI
* is targeting at. */
irr::scene::ISceneNode *m_debug_sphere;
irr::scene::ISceneNode *m_debug_sphere[4];
/** For item debugging: set to the item that is selected to
* be collected. */
@@ -193,8 +200,9 @@ private:
std::vector<const Item *> *items_to_collect);
void checkCrashes(const Vec3& pos);
void findNonCrashingPoint(Vec3 *result, int *last_node);
void findNonCrashingPoint2(Vec3 *result, int *last_node);
void findNonCrashingPointFixed(Vec3 *result, int *last_node);
void findNonCrashingPointNew(Vec3 *result, int *last_node);
void findNonCrashingPointDefault(Vec3 *result, int *last_node);
void determineTrackDirection();
void determineTurnRadius(const Vec3 &start,