Skip to content

Commit 0333054

Browse files
committed
Add more defense
1 parent a65d4b0 commit 0333054

File tree

2 files changed

+131
-48
lines changed

2 files changed

+131
-48
lines changed

src/karts/controller/soccer_ai.cpp

Lines changed: 127 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -230,16 +230,16 @@ Vec3 SoccerAI::determineBallAimingPosition()
230230

231231
if (m_overtake_ball)
232232
{
233-
Vec3 overtake_wc;
234-
const bool can_overtake = determineOvertakePosition(ball_lc, ball_pos,
235-
&overtake_wc);
233+
Vec3 overtake_lc;
234+
const bool can_overtake = determineOvertakePosition(ball_lc, aim_lc,
235+
ball_pos, &overtake_lc);
236236
if (!can_overtake)
237237
{
238238
m_overtake_ball = false;
239239
return ball_aim_pos;
240240
}
241241
else
242-
return overtake_wc;
242+
return m_kart->getTrans()(Vec3(overtake_lc));
243243
}
244244
else
245245
{
@@ -248,11 +248,10 @@ Vec3 SoccerAI::determineBallAimingPosition()
248248
// is behind the ball , if so m_overtake_ball is true
249249
if (aim_lc.z() > 0 && aim_lc.z() > ball_lc.z())
250250
{
251-
const bool can_overtake = determineOvertakePosition(ball_lc,
252-
ball_pos, NULL);
253-
if (can_overtake)
251+
if (isOvertakable(ball_lc, ball_pos))
254252
{
255253
m_overtake_ball = true;
254+
return ball_aim_pos;
256255
}
257256
else
258257
{
@@ -284,28 +283,13 @@ Vec3 SoccerAI::determineBallAimingPosition()
284283
} // determineBallAimingPosition
285284

286285
//-----------------------------------------------------------------------------
287-
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
288-
const posData& ball_pos,
289-
Vec3* overtake_wc)
286+
bool SoccerAI::isOvertakable(const Vec3& ball_lc, const posData& ball_pos)
290287
{
291-
// This done by drawing a circle using the center of ball local coordinates
292-
// and the distance / 2 from kart to ball center as radius (which allows more
293-
// offset for overtaking), then find tangent line from kart (0, 0, 0) to the
294-
// circle. The intercept point will be used as overtake position
295-
296288
// No overtake if ball is behind
297289
if (ball_lc.z() < 0.0f) return false;
298290

299291
// Circle equation: (x-a)2 + (y-b)2 = r2
300292
const float r2 = (ball_pos.distance / 2) * (ball_pos.distance / 2);
301-
302-
// No overtake if sqrtf(r2) / 2 < ball radius,
303-
// which will likely push to ball forward
304-
if ((sqrtf(r2) / 2) < (m_world->getBallDiameter() / 2))
305-
{
306-
return false;
307-
}
308-
309293
const float a = ball_lc.x();
310294
const float b = ball_lc.z();
311295

@@ -316,6 +300,23 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
316300
{
317301
return false;
318302
}
303+
return true;
304+
305+
} // isOvertakable
306+
307+
//-----------------------------------------------------------------------------
308+
bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
309+
const Vec3& aim_lc,
310+
const posData& ball_pos,
311+
Vec3* overtake_lc)
312+
{
313+
// This done by drawing a circle using the center of ball local coordinates
314+
// and the distance / 2 from kart to ball center as radius (which allows more
315+
// offset for overtaking), then find tangent line from kart (0, 0, 0) to the
316+
// circle. The intercept point will be used as overtake position
317+
318+
// Check if overtakable at current location
319+
if (!isOvertakable(ball_lc, ball_pos)) return false;
319320

320321
// Otherwise calculate the tangent
321322
// As all are local coordinates, so center is 0,0 which is y = mx for the
@@ -329,56 +330,136 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc,
329330
// (E2 - 4F)m2 + (2DE)m + (D2 - 4F) = 0
330331
// Now solve the above quadratic equation using
331332
// x = -b (+/-) sqrt(b2 - 4ac) / 2a
333+
334+
// Circle equation: (x-a)2 + (y-b)2 = r2
335+
const float r = ball_pos.distance / 2;
336+
const float r2 = r * r;
337+
const float a = ball_lc.x();
338+
const float b = ball_lc.z();
339+
332340
const float d = -2 * a;
333341
const float e = -2 * b;
334342
const float f = (d * d / 4) + (e * e / 4) - r2;
335343
const float discriminant = (2 * 2 * d * d * e * e) -
336344
(4 * ((e * e) - (4 * f)) * ((d * d) - (4 * f)));
337345

338346
assert(discriminant > 0.0f);
339-
const float slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
347+
float t_slope_1 = (-(2 * d * e) + sqrtf(discriminant)) /
340348
(2 * ((e * e) - (4 * f)));
341-
const float slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
349+
float t_slope_2 = (-(2 * d * e) - sqrtf(discriminant)) /
342350
(2 * ((e * e) - (4 * f)));
343351

344-
assert(!std::isnan(slope_1));
345-
assert(!std::isnan(slope_2));
352+
assert(!std::isnan(t_slope_1));
353+
assert(!std::isnan(t_slope_2));
354+
355+
// Make the slopes in correct order, allow easier rotate later
356+
float slope_1 = 0.0f;
357+
float slope_2 = 0.0f;
358+
if ((t_slope_1 > 0 && t_slope_2 > 0) || (t_slope_1 < 0 && t_slope_2 < 0))
359+
{
360+
if (t_slope_1 > t_slope_2)
361+
{
362+
slope_1 = t_slope_1;
363+
slope_2 = t_slope_2;
364+
}
365+
else
366+
{
367+
slope_1 = t_slope_2;
368+
slope_2 = t_slope_1;
369+
}
370+
}
371+
else
372+
{
373+
if (t_slope_1 > t_slope_2)
374+
{
375+
slope_1 = t_slope_2;
376+
slope_2 = t_slope_1;
377+
}
378+
else
379+
{
380+
slope_1 = t_slope_1;
381+
slope_2 = t_slope_2;
382+
}
383+
}
346384

347385
// Calculate two intercept points, as we already put y=mx into circle
348386
// equation and know that only one root for each slope, so x can be
349387
// calculated easily with -b / 2a
350388
// From (1+m2)x2 + (D+Em)x +F = 0:
351389
const float x1 = -(d + (e * slope_1)) / (2 * (1 + (slope_1 * slope_1)));
352390
const float x2 = -(d + (e * slope_2)) / (2 * (1 + (slope_2 * slope_2)));
391+
const float y1 = slope_1 * x1;
392+
const float y2 = slope_2 * x2;
353393

354-
// Use the closest point to aim
355-
float x = std::min(fabsf(x1), fabsf(x2));
356-
float y = 0.0f;
357-
if (-x == x1)
358-
{
359-
// x was negative
360-
x = -x;
361-
y = slope_1 * x;
362-
}
363-
else if (x == x1)
364-
{
365-
y = slope_1 * x;
366-
}
367-
else if (-x == x2)
394+
const Vec3 point1(x1, 0, y1);
395+
const Vec3 point2(x2, 0, y2);
396+
397+
const float d1 = (point1 - aim_lc).length_2d();
398+
const float d2 = (point2 - aim_lc).length_2d();
399+
400+
// Use the tangent closest to the ball aiming position to aim
401+
const bool use_tangent_one = d1 < d2;
402+
403+
// Adjust x and y if r < ball diameter,
404+
// which will likely push to ball forward
405+
// Notice: we cannot increase the radius before, as the kart location
406+
// will likely lie inside the enlarged circle
407+
if (r < m_world->getBallDiameter())
368408
{
369-
x = -x;
370-
y = slope_2 * x;
409+
// Constuctor a equation using y = (rotateSlope(old_m)) x which is
410+
// a less steep or steeper line, and find out the new adjusted position
411+
// using the distance to the original point * 2 at new line
412+
413+
// Determine if the circle is drawn around the side of kart
414+
// ie point1 or point2 z() < 0, if so reverse the below logic
415+
const float m = ((point1.z() < 0 || point2.z() < 0) ?
416+
(use_tangent_one ? rotateSlope(slope_1, false/*rotate_up*/) :
417+
rotateSlope(slope_2, true/*rotate_up*/)) :
418+
(use_tangent_one ? rotateSlope(slope_1, true/*rotate_up*/) :
419+
rotateSlope(slope_2, false/*rotate_up*/)));
420+
421+
// Calculate new distance from kart to new adjusted position
422+
const float dist = (use_tangent_one ? point1 : point2).length_2d() * 2;
423+
const float dist2 = dist * dist;
424+
425+
// x2 + y2 = dist2
426+
// so y = m * sqrtf (dist2 - y2)
427+
// y = sqrtf(m2 * dist2 / (1 + m2))
428+
const float y = sqrtf((m * m * dist2) / (1 + (m * m)));
429+
const float x = y / m;
430+
*overtake_lc = Vec3(x, 0, y);
371431
}
372432
else
373433
{
374-
y = slope_2 * x;
434+
// Use the calculated position depends on distance to aim position
435+
if (use_tangent_one)
436+
*overtake_lc = point1;
437+
else
438+
*overtake_lc = point2;
375439
}
376440

377-
if (overtake_wc)
378-
*overtake_wc = m_kart->getTrans()(Vec3(x, 0, y));
379441
return true;
380442
} // determineOvertakePosition
381443

444+
//-----------------------------------------------------------------------------
445+
float SoccerAI::rotateSlope(float old_slope, bool rotate_up)
446+
{
447+
const float theta = atan(old_slope) + (old_slope < 0 ? M_PI : 0);
448+
float new_theta = theta + (rotate_up ? M_PI / 6 : -M_PI /6);
449+
if (new_theta > ((M_PI / 2) - 0.02f) && new_theta < ((M_PI / 2) + 0.02f))
450+
{
451+
// Avoid almost tan 90
452+
new_theta = (M_PI / 2) - 0.02f;
453+
}
454+
// Check if over-rotated
455+
if (new_theta > M_PI)
456+
new_theta = M_PI - 0.1f;
457+
else if (new_theta < 0)
458+
new_theta = 0.1f;
459+
460+
return tan(new_theta);
461+
} // rotateSlope
462+
382463
//-----------------------------------------------------------------------------
383464
int SoccerAI::getCurrentNode() const
384465
{

src/karts/controller/soccer_ai.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ class SoccerAI : public ArenaAI
5555
bool m_steer_with_ball;
5656

5757
Vec3 determineBallAimingPosition();
58-
bool determineOvertakePosition(const Vec3& ball_lc,
59-
const posData& ball_pos, Vec3* overtake_wc);
58+
bool isOvertakable(const Vec3& ball_lc, const posData& ball_pos);
59+
bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc,
60+
const posData& ball_pos, Vec3* overtake_lc);
61+
float rotateSlope(float old_slope, bool rotate_up);
6062

6163
virtual void findClosestKart(bool use_difficulty);
6264
virtual void findTarget();

0 commit comments

Comments
 (0)