Teach an AI to Play Pong Introduction

Level: Intermediate (11+) Duration: 2 × 1 Hour

Recreate the classic game of Pong using JavaScript, then train an AI to play the game on its own! Learn core JavaScript concepts including Variables, Functions and Events, and get hands-on experience with Neural Networks and Machine Learning.

We’ll be using the Kid.js framework. This quickest way to get started is to use the kidjs.app website. Registration is optional, and if you don't wish to create an account you can login as guest. You can also import the framework into your own project.

Some prior coding experience is recommended, but not required.

What is a Neural Network?

Neural networks are a type of artificial intelligence that work the same way as our brain. Our brains have billions of cells, called neurons, that send and receive signals to each other. These signals help us recognize patterns. For example, when you see a small animal with four legs, pointy ears and whiskers, your brain recognizes this as a cat. It's learned over time from seeing many different cats that follow this pattern.

In an artificial neural network, the neurons are organized into layers. The first layer is given input (such as the pixels in an image) and the final layer returns an output (such as the probability those pixels are a cat.)

The first time the network sees a cat, it’s unlikely to correctly identify it—so we show it thousands of cats, over and over again. When it gets one wrong, we tweak the individual neurons until it gets it right. This is a process called Machine Learning.

Let’s start building our game!

Part 1: Preparing The Stage 10 minutes

First we’re going to need a ball.

Functions

We program a computer by giving it a series of commands, also known as functions. To run a command or function in JavaScript, type the name of the function followed by parentheses.

Many functions need parameters. For example, if you were to command someone to walk, you may also need to tell them how many steps or which direction. The parameters go inside of the parentheses.

We'll use the circle() function to create the ball. The parameters we need are (in order) the x coordinate, y coordinate and diameter.

circle(400, 70, 40)

Variables

Variables are used to keep track of things. Think of a variable as a box with a label you can put things in. There are many things we may need to keep track of in an app, such as high score or health points.

Variables are also useful for assigning names to things. We’ll give the ball a name, allowing us to reference it later. We use the let keyword to create a variable and the equals operator to assign it a value.

let ball = circle(400, 70, 40)

Part 2: The Paddle 15 minutes

Next we need a paddle. We’ll keep it one player for now.

We can use the rect() function to create a paddle. The parameters for the rect() function are (in order) the x coordinate, y coordinate, width and height.

let paddle = rect(width / 2, height - 50, 200, 20)

The variables width and height contain the width and height of the screen. We can use these to position the paddle relative to the bottom center of the screen.

Events

Events are "things" that happen while our app is running, like the mouse moving, or a key being pressed. We can write code that runs when an event occurs.

First we write a function that is intended to run when the event occurs. These types of functions are referred to as Event Handlers. Our function will move the paddle to the current mouse position. The mouse’s horizontal position can be found in the variable mouseX.

function onMouseMove() { paddle.x = mouseX }

Then connect it to the “mousemove” event.

let ball = circle(200, 70, 40) let paddle = rect(width / 2, height - 50, 200, 20) function onMouseMove() { paddle.x = mouseX } on('mousemove', onMouseMove)

Part 3: Setting Things in Motion 20 minutes

Now let’s set the ball in motion.

Properties

Properties describe something. You have properties, such as your age and height. Objects in JavaScript can have properties too. To access a property, type the name of the object, followed by a period, followed by the property name. We can assign a new value to a property with the equals operator, just like we would a variable.

We’re going to change the ball’s speed and direction properties, but we’re going to do this inside of our own custom function. Functions are useful for organizing code into reusable blocks. This will allow us to relaunch the ball in the future.

function launch() { ball.speed = 5 ball.direction = 45 }

Nothing happening? That’s because in addition to creating our function, we need to run it. Just like we did the circle() command.

let ball = circle(400, 70, 40) let paddle = rect(width / 2, height - 50, 200, 20) function onMouseMove() { paddle.x = mouseX } on('mousemove', onMouseMove) function launch() { ball.speed = 5 ball.direction = 45 } launch()

Randomness

It’s not fun for anyone if the ball always launches in the same direction. We can use the random() function to choose a random number between two values.

function launch() { ball.speed = random(5, 10) ball.direction = random(45, 135) } launch()

We can also give it a random starting position.

function launch() { ball.x = random(0, width) ball.y = 70 ball.speed = random(5, 10) ball.direction = random(45, 135) } launch()

Zero Gravity

We need to tweak a few things to make our world more Pong-like. We'll get rid of gravity, friction and remove the floor and ceiling.

gravity = 0 friction = 0 floor = false ceiling = false gravity = 0 friction = 0 floor = false ceiling = false let ball = circle(200, 70, 40) let paddle = rect(width / 2, height - 50, 200, 20) function onMouseMove() { paddle.x = mouseX } on('mousemove', onMouseMove) function launch() { ball.x = random(0, width) ball.y = 70 ball.speed = random(5, 10) ball.direction = random(45, 135) } launch()

Part 4: Keeping Score 15 minutes

Let’s keep track of the score. We’ll create two new variables, hits and misses, and start them both off at zero.

let hits = 0 let misses = 0

We'll show the current score using the display() function. The parameters for the display() function are the x and y coordinates of where to write the text, and what text to write.

let hits = 0 let misses = 0 display(80, 80, hits) display(width - 80, 80, misses)

We need to keep an eye on the ball as it moves. If it goes off the bottom of the screen, we’ll count that as a miss. If it goes off the top, a hit.

If Statements

We’re going to write a function that runs when the ball moves. Inside that function we'll use an if statement to check if the ball’s y coordinate is greater than the height of the screen (off the bottom) or less than zero (off the top).

If statements check that a condition is true, and if so, run some code. If the y coordinate is greater than the height of the screen, we increase the value of the misses variables by adding one to it. Then relaunch the ball.

function onBallMoved() { if (ball.y > height) { misses = misses + 1 launch() } }

Similarly, if the y coordinate is less than zero, we count that as a hit and relaunch the ball.

function onBallMoved() { if (ball.y > height) { misses = misses + 1 launch() } if (ball.y < 0) { hits = hits + 1 launch() } }

Connect this function to the ball’s “move” event.

function onBallMoved() { if (ball.y > height) { misses = misses + 1 launch() } if (ball.y < 0) { hits = hits + 1 launch() } } ball.on('move', onBallMoved)

The complete game so far:

gravity = 0 friction = 0 floor = false ceiling = false let ball = circle(200, 70, 40) let paddle = rect(width / 2, height - 50, 200, 20) function onMouseMove() { paddle.x = mouseX } on('mousemove', onMouseMove) function launch() { ball.x = random(0, width) ball.y = 70 ball.speed = random(5, 10) ball.direction = random(45, 135) } launch() let hits = 0 let misses = 0 display(80, 80, hits) display(width - 80, 80, misses) function onBallMoved() { if (ball.y > height) { misses = misses + 1 launch() } if (ball.y < 0) { hits = hits + 1 launch() } } ball.on('move', onBallMoved)