Pong AI

16 posts

Flag Post

I’m working on a side-game when I’m bored, and right now I’m just trying to clone Pong but with some cool AI. I’m stuck with AI, though I have the concept down;
1. Estimate where the ball is going.
2. Move the paddle there.
3. If the ball bounces off a wall, start again.

I seem to not know how to do math though. Keep in mind the paddles and the ball’s registration points are in the middle:
This is currently my AI Class; it extends paddle;

	public class AIPaddle extends Paddle
	{
		private var eY:int;
		public function AIPaddle(gameScreen:GameScreen)
		{
			super(gameScreen)
		}
		
		override public function eFrame():Boolean
		{
			//AI Algorithm
			if (g.xDir > 0)
			{
				//TODO Calculate where it is going and factor in mistakes
				
				if (eY < this.y - this.height / 4)
				{
					this.y -= g.paddleSpeed;
				}
				else if (eY > this.y + this.height / 4)
				{
					this.y += g.paddleSpeed;
				}
			}
			return super.eFrame();
		
		}
		
		public function calculateEnd(currentPos:Point, xSpeed:int, ySpeed:int):int
		{
			var timeToReach:Number = (this.x - currentPos.x) / xSpeed;
			var estimatedY:int = (timeToReach * ySpeed) + currentPos.y;
			trace("Current Position: " + currentPos)
			trace("ySpeed: " + ySpeed)
			trace("Estimated Position: " + estimatedY)
			return estimatedY;
		}
		public function set estY(newY:int):void {
			eY = newY;
		}

Paddle Class:

public class Paddle extends Sprite
	{
		private var art:Shape;
		public var g:GameScreen;
		private var pEvent:PaddleEvent;
		
		public function Paddle(gameScreen:GameScreen)
		{
			g = gameScreen;
			addEventListener(Event.ADDED_TO_STAGE, init)
		}
		
		private function init(e:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			art = new Shape();
			art.graphics.beginFill(0xC0C0C0, 1);
			art.graphics.drawRect(0, 0, 25, 100);
			art.graphics.endFill();
			addChild(art)
			art.x = -(art.width);
			art.y = -(art.height / 2);
		
		}
		
		public function eFrame():Boolean
		{
			var hit:Boolean;
			if (this.hitTestPoint(g.pongBall.x - g.pongBall.width / 2, g.pongBall.y, false) || this.hitTestPoint(g.pongBall.x + g.pongBall.width / 2, g.pongBall.y, false))
			{
				g.xDir *= -1;
				pEvent = new PaddleEvent(PaddleEvent.ON_HIT);
				pEvent.customMessage = "Paddle Hit."
				dispatchEvent(pEvent)
				hit = true;
			}
			else
			{
				hit = false;
			}
			return hit;
		}

And what deals with the game:

private function eFrame(e:Event):void
		{
			if (moveUp)
			{
				if ((playerPaddle.y - playerPaddle.height / 2) - paddleSpeed > paddleBuffer)
				{
					playerPaddle.y -= paddleSpeed;
				}
			}
			else if (moveDown)
			{
				if ((playerPaddle.y + playerPaddle.height / 2) + paddleSpeed < STAGE_HEIGHT - paddleBuffer)
				{
					playerPaddle.y += paddleSpeed;
				}
			}
			if (!ballReleased)
			{ // If the ball has not been served...
				if (!setup)
				{
					xDir = 10;
					yDir = 0;
					playerPaddle.addEventListener(PaddleEvent.ON_HIT, paddleHit)
					aiPaddle.addEventListener(PaddleEvent.ON_HIT, paddleHit);
					setup = true;
				}
				pongBall.y = playerPaddle.y;
			}
			else
			{ // If the ball is in the air...
				if (numberOfHits == 0) {
					aiPaddle.estY = aiPaddle.calculateEnd(new Point(pongBall.x, pongBall.y), xDir, yDir);
					numberOfHits++;
				}
				pongBall.x += xDir;
				pongBall.y += yDir;
				if (pongBall.x > playerPaddle.x && pongBall.x < aiPaddle.x)
				{
					
					if (playerPaddle.eFrame())
					{
						yDir = -(playerPaddle.y - pongBall.y) / 5;
						xDir += speedIncrease;
					}
					else if (aiPaddle.eFrame())
					{
						yDir = -(aiPaddle.y - pongBall.y) / 5;
						xDir += -speedIncrease;
					}
					else if (pongBall.y - pongBall.height / 2 <= 0 || pongBall.y + pongBall.height / 2 >= STAGE_HEIGHT)
					{
						yDir *= -1;
						aiPaddle.estY = aiPaddle.calculateEnd(new Point(pongBall.x, pongBall.y), xDir, yDir);
						numberOfHits++;
					}
					
				}
			}
		}
		
		private function paddleHit(e:PaddleEvent):void
		{
			aiPaddle.estY = aiPaddle.calculateEnd(new Point(pongBall.x, pongBall.y), xDir, yDir);
			numberOfHits++;
		}

I think really all that’s wrong is the math I’m using, not sure why though! Any help?

 
Flag Post

Can’t you just make the paddle go to the ball’s y pos?

 
Flag Post
Originally posted by UnknownGuardian:

Can’t you just make the paddle go to the ball’s y pos?

I’m trying to make it more human-like. That would be unbeatable.

 
Flag Post

What if I am unbeatable.

EDIT Also, a good idea would be to simulate a secondary game a certain amount of frames ahead of the actual game (so perhaps 10-15 frames) and have the paddle move to an approximation (meaning within 25 pixels) of where the ball is, and as the ball approaches the paddle the approximation becomes closer to what the true hit location will be. (Also, not super beatable)

 
Flag Post

Isn’t there just an easy equation to predict where the ball would end up?

 
Flag Post
Originally posted by RTL_Shadow:

Isn’t there just an easy equation to predict where the ball would end up?

Considering that the ball moves in a strait line……

 
Flag Post

You know… you don’t need much math to code this. You just need an if statement… if the ball is so far away, move to that point. But do so at a given speed that is slightly slower than the ball, so hitting corners would slow the AI down. Even better… predict where the ball is going. Using the current vector of the ball, predict where the ball will land. But maybe use a timer with a random delay. If the AI reacts too slowly… one point too you. If the AI reacts quickly enough, increase the ball’s speed. As for the paddles… use an upper bounds and lower bounds for the paddles stored in the Paddle class. If the ball y position is not in these bounds, remove the ball and give the opponet an extra point, and vice versa.

 
Flag Post
Originally posted by Draco18s:
Originally posted by RTL_Shadow:

Isn’t there just an easy equation to predict where the ball would end up?

Considering that the ball moves in a strait line……

It bounces.

 
Flag Post
Originally posted by jasonjie88:

You know… you don’t need much math to code this. You just need an if statement… if the ball is so far away, move to that point. But do so at a given speed that is slightly slower than the ball, so hitting corners would slow the AI down. Even better… predict where the ball is going. Using the current vector of the ball, predict where the ball will land. But maybe use a timer with a random delay. If the AI reacts too slowly… one point too you. If the AI reacts quickly enough, increase the ball’s speed. As for the paddles… use an upper bounds and lower bounds for the paddles stored in the Paddle class. If the ball y position is not in these bounds, remove the ball and give the opponet an extra point, and vice versa.

I initially wanted to predict where the pong ball was going to end up in the first place, similar to how a human does. I had it previously that if the ball was above the paddle, the paddle would move up, and below likewise, but that was easy to beat and just boring.

EDIT: Since that was a ton of code, I’ll simplify it.
All I need to do is find out what the Y position is going to be of the ball when it’s X = the ai paddles x. Could someone explain it to me like I’m five?

 
Flag Post

Okay. Your ball has a vector of movement, with its DX and DY part representing vector projection on axes. Currently your ball has X and Y coords, the walls are at Y1 and Y2 respectively, and the AI paddle’s X coordinate is at X1. Now, you want to find out where the ball will end when his X will reach X1. You calculate what coordinate would the ball have in case there would be no walls, and start mirroring that result against both walls in succession, until the resultant value will be between the walls.

Yend=Y+DY*(X1-X)/DX; 
numberOfBounces=0; // in case you would need this to alternate AI behavior
while ((Yend>Y2)||(Yend<Y1)) {
  if (Yend>Y2) Yend=Y2+Y2-Yend; 
  else Yend=Y1+Y1-Yend;
  numberOfBounces++;
}

Then move the AI paddle towards the found Yend.

 
Flag Post

You could make it that the enemy paddle follows the ball until it reaches a certain distance between the enemy, then the enemy selects a point a few pixels up/down and moves there. Just an idea.

 
Flag Post

You could go super simple and give the paddle a max move speed. Just move towards the ball’s Y and if the ball is moving faster than the paddle can, it may not be able to keep up and possibly miss. I’ve seen quite a few pong AI’s that do that.

 
Flag Post

my first ever flash project… (where I simply copy pasted all the code, and did nothing myself)
http://www.danishdragon.com/flash/pingpong.swf

There you can see an example of an opponent which simply follows the ball.

 
Flag Post
Originally posted by RTL_Shadow:
Originally posted by Draco18s:
Originally posted by RTL_Shadow:

Isn’t there just an easy equation to predict where the ball would end up?

Considering that the ball moves in a strait line……

It bounces.

Reflections are symmetrical. :|

 
Flag Post

Yea, Vesperbot even supplied the proper code to predict the endpoint of the ball. Truth is, Pong isn’t a very interesting game, and it hasn’t been novel for more than 30 years. It’s a good first game for a starting programmer, as it introduces a lot of the core concepts of game programming (reacting to user commands, sprite movement, collision, basic AI, real-time play, basic variable storage and display…)

Don’t get me wrong, coding Pong as an exercise is laudable, and shows that you have the right mindset in your approach to programming. But the concept is too limited to make it interesting. The basic principle was already pushed quite far in the old Shufflepuck Cafe video game (see the Wikipedia article), and if you’ve ever played air hockey in an arcade, that’s about as close as you can get to real-life pong. I recommend you make your little classic Pong game, realize it’s too limited, and move on to more interesting game designs.

 
Flag Post
Originally posted by vesperbot:

Okay. Your ball has a vector of movement, with its DX and DY part representing vector projection on axes. Currently your ball has X and Y coords, the walls are at Y1 and Y2 respectively, and the AI paddle’s X coordinate is at X1. Now, you want to find out where the ball will end when his X will reach X1. You calculate what coordinate would the ball have in case there would be no walls, and start mirroring that result against both walls in succession, until the resultant value will be between the walls.

Yend=Y+DY*(X1-X)/DX; 
numberOfBounces=0; // in case you would need this to alternate AI behavior
while ((Yend&gt;Y2)||(Yend&lt;Y1)) {
  if (Yend&gt;Y2) Yend=Y2+Y2-Yend; 
  else Yend=Y1+Y1-Yend;
  numberOfBounces++;
}

Then move the AI paddle towards the found Yend.

Thanks a ton, I’m going to try to modify it so that it acts similar to how a human does:)

EDIT: Thanks a ton for the help everyone. You can view the finished AI class here.