Jump to content
  • Advertisement
Sign in to follow this  

Slightly borked Pong AI

This topic is 4732 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, me again, I'm just about finished Pong, but I've got a slight problem. The Two player mode is working perfectly (spare a few bugs in the collision detection, but it's not too bad), but the single player is still a bit dodgy. The problem is the computer controlled paddle twitches violently up and down and it sometimes hits the ball perfectly, stopping dead when the ball is in the bottom right corner of the screen, then twitches again. It moves up and down in accordance to the ball, sort of. It never moves smoothly like the player's paddle does. Here's my AI handler function:

/* HandleAI - handles the AI */

void HandleAI()
{
     
     lbp = g_ball.position->y; /* lbp - Last Ball Position*/
     
     float centreofball=g_ball.position->y+7.5;
     
     
     if(lbp==g_ball.position->y)
     {
                                
                                AIChoice_p2 = rand()%3+1;
                                
                                lbp=g_ball.position->y;
                                
     }
     
     switch(CurrentState)
     {
                         
                         case 1: /* Single player mode - only needs AI on one paddle */
                         
                               
                               
                               switch(AIChoice_p2)
                               {
                                               
                                               case 1: // Hit with top
                                                    
                                                                                            p2_yhit=player2_cpu.position->y;  // p2_yhit is the top left of the paddle
                                                   
                                                                                            break;
                                                    
                                                    
                                               case 2: // Hit with middle
                                                    
                                                                                            p2_yhit=player2_cpu.position->y/2;  //p2_yhit is the paddle's centre now
                                                                                             
                                                                                            break;
                                                                                            
                                               case 3: // Hit with bottom

                                               
                                                                                            p2_yhit=player2_cpu.position->y-75; // p2_yhit is now the bottom left of the paddle
                                                    
                                                                                            break;
                                                    
                              }
                                                    
     }
    
    float wtf=(fabs(p2_yhit-centreofball)); // Not sure about this - hence the variable name 'wtf'
    
    if(wtf<20) //?
    {
                                    return;
    } 
     
     if(lbp<p2_yhit) // If the ball's last y position is above p2_yhit
     {
                             
     if(player2_cpu.position->y>BOUNDARY_TOP) // If paddle hasn't hit the top of the screen
     {
                          
                         player2_cpu.position->y-=player2_cpu.direction->y*player2_cpu.speed; // Move it up
                        
     }
     
     }
     
     
     else if(lbp>p2_yhit)  // If the last known ball's y position is below p2_yhit
     {
                             
     if(player2_cpu.position->y<WINDOW_HEIGHT-90) //If the paddle hasn't hit the bottom
     {
                                                   
                           player2_cpu.position->y+=player2_cpu.direction->y*player2_cpu.speed; // Move it down
    
     }
     
     }
    
}

/* End HandleAI */



I appreciate this may not make a lot of sense outwith my own code, but I hope my description of the problem is of use to you. If you need further clarification, please let me know. I sure hope you can help, if this gets fixed I can finish Pong and move on. Thanks in advance, ukdeveloper.

Share this post


Link to post
Share on other sites
Advertisement
It would help alot if you could put in decent comment and variable names. "wtf" doesn't help - just think about what it does. Describe what each part of the code does. Sorry this doesn't answer your question, but you will draw better, faster responses if it is easier for the rest of us to read - remember, we didn't participate in writing the code :)

Share this post


Link to post
Share on other sites
Your best bet is to look at the position and velocity of the ball right when it crosses the vertical midline of the arena and anticipate somewhat inaccurately where the ball could be when it reaches the computer paddle.

Next set this extrapolation of the ball's position as a destination point for the computer paddle. In other words, have the computer gradually move toward this anticipated point, and upon reaching it wait for the ball to either hit the paddle, or go past the goal line.

This should get rid of the twitchy behavior and incorporate human fallibity into the actions of the computer.

<edit> Your code made me laugh. You have a variable named 'wtf' [lol]. Just so you know, it seems that 'wtf' is storing the distance between the center of the ball and the paddle (which one I don't know) on the y-axis.
</edit>

<edit2>
I wrote some pseudocode to demonstrate what I mean:


// ... lots of code ...

// check if ball has just passed the vertical midline
if (verticalMidline > lastBallPos.x && verticalMidline < currBallPos.x) {

aiPaddle.setDest(extrapolateBallPos());
}

// ... lots of code ...

Vec2 extrapolateBallPos() {

// find time interval
float ti = ( arenaWidth / 2 ) / ballVel.x;

// integrate ball velocity over above time interval
Vec2 newBallPos = ballPos + ballVel * ti;

// yes I realize this code doesn't take into account the fact that
// the ball will quite likely bounce the top and bottom walls.
// this could be a good thing, though :)
// it could make it seem as though the computer messed up badly.
// it's probably best you take this into account in the real code
// and then do something like the following lines:

// add a random number to the y coordinate to simulate human fallibility
newBallPos.y += rand() % aiPaddle.height - rand() % 2 * aiPaddle.height;

// return the anticipate ball position
return newBallPos;
}

// ... lots of code ...

void aiPaddle::setDest(Vec2& DestPnt) { dest = DestPnt; }

void aiPaddle::update() {

if (pos.y < dest.y)
pos.y += vel.y;
else if (pos.y > dest.y)
pos.y -= vel.y;
}






Note: code is very untested.

</edit2>

HTH,
nilkn

[Edited by - nilkn on June 7, 2005 8:46:53 PM]

Share this post


Link to post
Share on other sites
I removed some redundancies and stuff, here is the revised function:



void HandleAI()
{

lbp = g_ball.position->y; /* lbp - Last Ball Position*/

float centreofball=g_ball.position->y+7.5;

AIChoice_p2 = rand()%3+1;


switch(CurrentState)
{

case 1: /* Single player mode - only needs AI on one paddle */
switch(AIChoice_p2)
{

case 1: // Hit with top

p2_yhit = player2_cpu.position->y; // p2_yhit is the top left of the paddle

break;


case 2: // Hit with middle

p2_yhit=player2_cpu.position->y/2; //p2_yhit is the paddle's centre now

break;

case 3: // Hit with bottom


p2_yhit=player2_cpu.position->y-75; // p2_yhit is now the bottom left of the paddle

break;

}

}


if(lbp<p2_yhit && player2_cpu.position->y > BOUNDARY_TOP )
{
player2_cpu.position->y-=player2_cpu.direction->y*player2_cpu.speed; // Move it up
}
else if( player2_cpu.position->y<WINDOW_HEIGHT - 90 ) //If the paddle hasn't hit the bottom
{
player2_cpu.position->y+=player2_cpu.direction->y*player2_cpu.speed; // Move it down
}


// NOTE: WTF has been removed because I saw no point in it, O_O.
}






Your code really confused me at first, especially the variable 'WTF.'



[Edited by - Lenox on June 6, 2005 7:56:11 PM]

Share this post


Link to post
Share on other sites
I'll take a crack at cleaning it up a bit, and then point to where I think the error is


// Don't comment your functions unless you can say something beyond what the function name already says.

void HandleAI() {
// Don't comment variable names to keep them short; write them out at least
// to a reasonable extent. Certain abbreviations are common enough in game
// programming that people will understand them, thus:
lastBallPos = g_ball.position->y;
float ballCentre = lastBallPos + 7.5;
// Avoid redundant logic; in particular, watch out for switches with only
// one case, "if"s that are easily seen to be always - or never - executed,
// or re-setting a variable to the value it's already known to have.
AIChoice_p2 = rand()%3+1;
if (CurrentState == 1) {
// Single player mode - only needs AI on one paddle
switch(AIChoice_p2) {
case 1: // Hit with top
p2_yhit=player2_cpu.position->y;
break;
case 2: // Hit with middle
p2_yhit=player2_cpu.position->y/2; // <-- I THINK THIS IS THE PROBLEM
break;
case 3: // Hit with bottom
p2_yhit=player2_cpu.position->y-75;
break;
}
}
// I will assume the old "wtf" has a purpose for now
float hitInaccuracy = (fabs(p2_yhit-BallCentre));

// Notice how the conditions are combined. Also, I reworked the "return" logic
// because it happens near the end of the function - IMHO early returns should
// be *really* early when possible, as "guard clauses".
if(hitInaccuracy >= 20) {
if(lastBallPos<p2_yhit && player2_cpu.position->y>BOUNDARY_TOP) {
// Cleaned up the math here.
player2_cpu.position->y *= (1-player2_cpu.speed);
} else if (lastBallPos>p2_yhit && player2_cpu.position->y<WINDOW_HEIGHT-90) {
player2_cpu.position->y*=(1+player2_cpu.speed);
}
}
}



Note the marked line. The way that you calculate a p2_yhit is inconsistent; logically one would expect the value for the "middle" to be the average of the other two - I think you meant p2_yhit=player2_cpu.position->y-75/2;. (Note that division takes precedence over multiplication, and depending on your data types, you might want that to be 75.0 instead.)

That said, you really should provide constant values for the other "magic" numbers in this function, the same way as you have for BOUNDARY_TOP for example. Also, since most of the manipulation is of members of the player2_cpu class (or struct as the case may be), this really should be implemented (the function name is another clue) as a member function of that class (assuming this is C++ and that you therefore have the option).

Share this post


Link to post
Share on other sites
I think it's nice of everyone to clean up your cade and stuff. But if you call the Handle AI function every frame your wobble is coming from the fact the every frame your changing where you want the paddle to hit the ball. This statement is the culprit
Quote:

AIChoice_p2 = rand()%3+1;

Better I think is to decide this everytime the ball is hit or everytime the AI player hits the ball. Good luck

Share this post


Link to post
Share on other sites
Thanks for all your help, but it still isn't working properly. I've uploaded what I've got so far, you can get it here, everything you need is in the zip archive.

Please download it, run it and you'll see exactly what my problem is. If you can give greater guidance about how to fix it, that would also be greatly appreciated.

The two player mode works absolutely fine. It's the single player blue paddle that isn't working properly.

If you have any problems understanding the code or the way I've done it, please ask.

Thanks for your help,

ukdeveloper.

Share this post


Link to post
Share on other sites
Here's how I handle the Pong AI in my Enemy Pong game.

double difficulty = 0.03;

// Update CPU and perform AI
private void updateCPU()
{
int yDistanceToBall;
double speed;

if ( ball.dx > 0) // if ball is going right
{
yDistanceToBall = Math.abs( ball.y - cpu.y );

speed = yDistanceToBall * difficulty;

// If ball is going high:
if ( ball.y + ball.dy < cpu.y )
cpu.y -= speed; // Move CPU paddle up.

// Else ball is going low:
else
cpu.y += speed; // Move CPU paddle down.
}
else // Move slowly back to middle.
{
if ( cpu.y < SCREEN_HEIGHT/2 - 1 )
cpu.y += 2;
else if (cpu.y > SCREEN_HEIGHT/2 + 1 )
cpu.y -= 2;
}
}





Hope it helps.

[Edited by - EnemyBoss on June 7, 2005 5:28:59 PM]

Share this post


Link to post
Share on other sites
Here's some pseudocode of how my Pong logic works


if (ball.x > limit)
{
if (ball.y > enemy.y) enemy.y+=5;
if (ball.y < enemy.y) enemy.y-=5;
}


Can't get any simpler than that.

Share this post


Link to post
Share on other sites
ukdeveloper: I would test out your game, but I don't have the required DLLs. Please from now on include these in your download packages. I think I had this problem with one of your earlier pong threads [smile]

Also, why is your indent so huge?! I mean seriously, one of your switch statements took up over twice my monitor's width! The bodies of your if statements were indented 48 spaces! Typically, five is enough.

I'm having some trouble following your code, but I can't test out the game to see if my assumptions are correct. Please add in those DLLs for those of us who don't have them all.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!