Pong was the first sports-based arcade videogame. Basically, it is a virtual version of table tennis. At the time of this project's conception, I had gone through a long period of time without touching neither Java, nor the subject of artificial intelligence. I felt like I neglected both of these long enough and that it was time to come up with a small project that will serve as practice. Making a game-engine for Pong - plus a challenging artificial player to play against - is just the kind of exercise I was looking for!

Table of contents

Game rules

Pong is governed by very simple rules. The virtual table is rectangular, with each player controlling a paddle on opposing sides. Let's call the sides the players occupy goals and the sides they do not walls. The ball is released in the center and bounces around upon contact with either paddles or walls. Each player's objective is to not let the ball touch its goal, since that constitutes a loss.

Figure 1: Schematic of the table.

In all further discussion we will assume the players take the bottom/top sides of the table as their goals. Players can move their paddles left and right at constant top-speed. The ball is also moving at constant speed, and bounces off surfaces it collides with according to the following rules:

  1. Normally, the ball moves in constant speed in its current direction until it hits either a wall or a paddle (if it hits a goal, the game ends).
  2. Upon hitting a wall, the ball's movement direction is reflected across the surface normal, as if it were a ray of light bouncing off a mirror.
  3. Upon hitting a paddle, the ball's new direction depends on its distance from the paddle center: A hit dead on the middle results in a bounce direction that is straight-up. A hit on the edge will send the ball in a low-angled direction away.
Figure 2: Ball bounce angle relative to distance from paddle center.

Designing an artificial opponent

The making of the game-engine itself was not very eventful or exciting. The making of the AI, on the other hand, was a lot of fun. We started with a very simple game-plan, and iteratively identified its deficiencies and worked to amend them up to the point where the AI felt challenging enough.

The naive approach

The most elementary attempt at a virtual Pong player is a passive one. That is, a player that only cares for not losing - never mind winning. As such a player, our first thought in this direction might be that in order not to lose, we must always be there when the ball approaches our goal so we may block it. A simplistic approach to this problem would be simply tracking the ball's horizontal position at all times and moving our paddle towards it:

Figure 3: Human player (bottom) vs. a passive ball-tracking AI (top). The AI simply moves its paddle towards the ball's position at all times.

Unfortunately, as you can see above, this approach assumes that we can move our paddle at instant speed to any point. This of course is not true, and our strategy falls flat against a long-distance serve to the other side of the table.

Predicting the ball's position

Our naive strategy to the passive player problem acted as though the ball was about to hit our goal at any moment, even when this is obviously not the case: For example, when the ball is still a long ways away and moving in a direction that is more horizontal than vertical.

To remedy this, we take advantage of the fact that the rules that govern the ball's movement are deterministic and well-defined. Knowing the current ball position and velocity, we could look ahead into the future and compute its expected movement trajectory up until the next time it hits our goal. Then, we simply look at the position we predict it lands and move our paddle there straight away:

Figure 4: Human player (bottom) vs. a future-predicting AI (top). The AI predicts the position of the ball the next time it hits the goal and aims to be there first.

Already, this approach is a big improvement on our first attempt. However, as can be seen above, it is still easily exploited. The problem lies in the fact that the AI always serves the ball back with its paddle's center, which sends the ball in a purely vertical direction. A smart opponent will use this predictability to manipulate the AI into a corner, and then serve wide - where even though the AI correctly predicts the ball's position at its goal, it is too far away to block it in time.

Engaging the opponent

Clearly the passive approach is not working. A purely passive AI is too predictable and therefore too easily manipulated. A big part of this predictability stems from the fact it currently serves with its paddle's center, which sends the ball in a trajectory that is very easy for a human adversary to follow.

So if we are not always to serve with our paddle's center, how should we serve instead? What would a smart player do? An attractive answer is this: always serve so that the ball lands as further away as possible from the opponent's paddle position. To do this, we again make use of the deterministic nature of the simulation and compute the offset from our own paddle's center that will send the ball in the opposite direction of our opponent:

Figure 5: Human player (bottom) vs. an aggressive AI (top). The AI predicts the position of the ball the next time it approaches the player and aims to position itself such that the ball will bounce off its paddle as further away from the opposing player as possible.

Much better! Playing against this AI is already quite challenging, and although the AI can still technically be exploited, such exploits requires quite the gameplay skill. All in all, we have achieved our objective of making a reasonably challenging artificial opponent for Pong.

Source code

You can view the source code and documentation on GitHub.

See also