1

I'm writing a game of pong for an embedded ARM microcontroller (the board is a Keil MCBSTM32C, for anyone interested) and I'm presently trying to implement an AI. The basic functionality was fairly easy because I simply mapped the location of the player 2 controller to the location of the ball. However, that is obviously not ideal because I'd like the AI to make mistakes so the player can win.

So, I tried to implement a randomised amount of error in the AI movements. However, the AI paddle now "jitters" around the screen a lot with the amount of jitter being greater than the amount of error. I think understand why this is happening (it is due to the fact that the randomised difficulty offset changes each time the paddle is redrawn), but I'm not entirely sure how to fix it. I tried making it move in the direction of the ball rather than directly mapped to the ball but that doesn't seem to have helped much.

The code for drawing the player paddle is:

void draw_player2(enum pMode mode) {
    int adcValue; 
    static int lastValue = 0;
    int direction;
    switch (mode)
  {
  default:  
                break;
    case DUAL:      
                adcValue = (ADC1->DR & 0x0FFF) >> 4;    /* AD value (8 bit) */
                if (lastValue != adcValue) {
                    thisGame.p2.y = (unsigned int) adcValue * (HEIGHT-BAR_H)/256;
                    LCDupdatePaddle(thisGame.p2);
                    lastValue = adcValue;
                }
                break;
    case AI:        
                direction = thisGame.ball.y-lastValue;
                adcValue = thisGame.ball.y;     /* AD value (8 bit) */
                if (lastValue != adcValue) {
                    thisGame.p2.y = (lastValue + direction + selectDiffMod()) * (HEIGHT-BAR_H)/256;
                    LCDupdatePaddle(thisGame.p2);
                    lastValue = adcValue;
                }
                break;
    }        
}

(HEIGHT=240 and BAR_H=48, btw)

The code for selectDiffMod() is:

int selectDiffMod() {
    int r;
    if(thisGame.delay == T_SLOW) {
        r = rand() % 100;
    }
    if(thisGame.delay == T_MEDIUM) {
        r = rand() % 50;
    }
    if(thisGame.delay == T_FAST) {
        r = rand() % 20;
    }
    return r;
}

My current line of thinking would be to generate the difficulty modifier/offset less often, but I'm not sure this would actually solve it and I'm wondering if anyone has a better solution?

The General
  • 1,239
  • 2
  • 17
  • 28
  • 1
    One idea would be to have a concept of paddle "speed". If the paddle sees the ball is below it, the paddle accelerates downwards, meaning it could "miss the shot" and need to accelerate upwards afterwards. More intelligent AI could then have the ability to decelerate sooner, for example. And this has the advantage of looking a little more realistic to a player. – Taylor Brandstetter Oct 29 '14 at 22:28
  • Can you please fix the tags? It looks like a general question. – auselen Oct 29 '14 at 22:36
  • @TaylorBrandstetter yes, that would appear to be a good solution. I'm implementing something along the lines of what uesp has described. – The General Oct 29 '14 at 23:14
  • 2
    You can read how the original is implemented. http://books.google.se/books?id=DqePfdz_x6gC&pg=PA40&lpg=PA40&dq=pong+game+original+implementation+error+ai&source=bl&ots=FRQooYPyLw&sig=-S94lY-VjhKWjhJU1ausFVdpj3w&hl=en&sa=X&ei=_wpSVO6mCePlywPh44G4Ag&ved=0CDMQ6AEwCA – auselen Oct 30 '14 at 09:57
  • possible duplicate of [Pong: How does the paddle know where the ball will hit?](http://stackoverflow.com/questions/4577814/pong-how-does-the-paddle-know-where-the-ball-will-hit) – James Greenhalgh Nov 02 '14 at 08:51

1 Answers1

1

I would do it by assigning the player 2 paddle a speed which it can move at. At low difficulties it would move slower. A smaller random fluctuation can be added on top of this as needed. It would look something like (not tested, modify as needed to work for your specific case):

int get_new_value_ai (const int currentValue, const int ballY)
{
    int direction;
    int speed = 2;
    int newValue;

        //basic sign calc: -1, 0, 1
    direction = (currentValue < ballY) - (ballY < currentValue); 

        //Adjust speeds as needed
    if (thisGame.delay == T_SLOW) 
        speed = 1;
    else if (thisGame.delay == T_MEDIUM) 
        speed = 2;
    else if (thisGame.delay == T_FAST) 
        speed = 3;

    if (abs(currentValue - ballY) < speed) //Prevent overshooting
        newValue =  ballY;
    else
        newValue =  currentValue + direction * speed;

    newValue += (rand() % 3) - 2; //Random mod of -1, 0, +1

    return newValue;
}

and call it as:

thisGame.p2.y = get_new_value_ai(thisGame.p2.y, thisGame.ball.y);

Beyond this you could make it even more complex by introducing acceleration/momentum when the paddle changes direction.

uesp
  • 6,194
  • 20
  • 15
  • Thanks, a solution along those lines seems to kill the jitter. Unfortunately, as presently set up, the AI doesn't seem to lose. I'm reckoning that an adjustment to the speed values should fix this? – The General Oct 29 '14 at 23:27
  • Yah, you can try tweaking the speeds to get a good balance between the different game settings. Note that if you need slower speeds you may need to store the player's position (and speed) as a floating point and round to an integer when displaying. – uesp Oct 30 '14 at 00:04
  • @ueasp: is "if (abs(currentValue - ballY) > speed)" seems to me like that would be resetting the bat position every time the distance to the ball is *greater* than the speed and that would result in it running almost every update? – The General Nov 02 '14 at 15:56