Please note: this blog has been migrated to a new location at https://jakesgordon.com. All new writing will be published over there, existing content has been left here for reference, but will no longer be updated (as of Nov 2023)

Pong - Computer AI

Sat, May 14, 2011

General Strategy

Pong is a simple game, so we can provide a computer AI with a simple 2 part strategy:

We can try to balance the game by making the computers skills better if it starts loosing, or by making it worse if the computer is dominating.

Levels: [
  {aiReaction: 0.2, aiError:  40}, // 0:  ai is losing by 8
  {aiReaction: 0.3, aiError:  50}, // 1:  ai is losing by 7
  {aiReaction: 0.4, aiError:  60}, // 2:  ai is losing by 6
  {aiReaction: 0.5, aiError:  70}, // 3:  ai is losing by 5
  {aiReaction: 0.6, aiError:  80}, // 4:  ai is losing by 4
  {aiReaction: 0.7, aiError:  90}, // 5:  ai is losing by 3
  {aiReaction: 0.8, aiError: 100}, // 6:  ai is losing by 2
  {aiReaction: 0.9, aiError: 110}, // 7:  ai is losing by 1
  {aiReaction: 1.0, aiError: 120}, // 8:  tie
  {aiReaction: 1.1, aiError: 130}, // 9:  ai is winning by 1
  {aiReaction: 1.2, aiError: 140}, // 10: ai is winning by 2
  {aiReaction: 1.3, aiError: 150}, // 11: ai is winning by 3
  {aiReaction: 1.4, aiError: 160}, // 12: ai is winning by 4
  {aiReaction: 1.5, aiError: 170}, // 13: ai is winning by 5
  {aiReaction: 1.6, aiError: 180}, // 14: ai is winning by 6
  {aiReaction: 1.7, aiError: 190}, // 15: ai is winning by 7
  {aiReaction: 1.8, aiError: 200}  // 16: ai is winning by 8
],

Paddle Prediction

The paddle AI follows this strategy:

ai: function(dt, ball) {
  if (((ball.x < this.left) && (ball.dx < 0)) ||
      ((ball.x > this.right) && (ball.dx > 0))) {
    this.stopMovingUp();
    this.stopMovingDown();
    return;
  }

  this.predict(ball, dt);

  if (this.prediction) {
    if (this.prediction.y < (this.top + this.height/2 - 5)) {
      this.stopMovingDown();
      this.moveUp();
    }
    else if (this.prediction.y > (this.bottom - this.height/2 + 5)) {
      this.stopMovingUp();
      this.moveDown();
    }
    else {
      this.stopMovingUp();
      this.stopMovingDown();
    }
  }
},

The prediction code is a little tricky.

predict: function(ball, dt) {
  // only re-predict if the ball changed direction, or its been some amount of time since last prediction
  if (this.prediction &&
      ((this.prediction.dx * ball.dx) > 0) &&
      ((this.prediction.dy * ball.dy) > 0) &&
      (this.prediction.since < this.level.aiReaction)) {
    this.prediction.since += dt;
    return;
  }

  var pt  = Pong.Helper.ballIntercept(ball, {left: this.left, right: this.right, top: -10000, bottom: 10000}, ball.dx * 10, ball.dy * 10);
  if (pt) {
    var t = this.minY + ball.radius;
    var b = this.maxY + this.height - ball.radius;

    while ((pt.y < t) || (pt.y > b)) {
      if (pt.y < t) {
        pt.y = t + (t - pt.y);
      }
      else if (pt.y > b) {
        pt.y = t + (b - t) - (pt.y - b);
      }
    }
    this.prediction = pt;
  }
  else {
    this.prediction = null;
  }

  if (this.prediction) {
    this.prediction.since = 0;
    this.prediction.dx = ball.dx;
    this.prediction.dy = ball.dy;
    this.prediction.radius = ball.radius;
    this.prediction.exactX = this.prediction.x;
    this.prediction.exactY = this.prediction.y;
    var closeness = (ball.dx < 0 ? ball.x - this.right : this.left - ball.x) / this.pong.width;
    var error = this.level.aiError * closeness;
    this.prediction.y = this.prediction.y + Game.random(-error, error);
  }
},

The game is now playable as both a 1 and 2 player game with this demo here and is almost complete.

The FINAL VERSION adds

More…

You can find the final game here and the code is here