Coding a Pong clock in JavaScript

I’m a little bit strapped for things to code on at the moment, as a result I’ve been consuming a tonne of blogs. One of my go-to blogs is Jeff Attwood’s Coding Horror, he’s an excellent writer and has a number of old classics that I binge through regularly. One in particular caught my attention this week:

Directly above my monitors is my pride and joy: my very own Pong clock. I’m fascinated with clocks, so the Pong clock was my holy grail: it mixes art with classic video games, and it’s a functional clock, too. I waited six months to get this delightful piece of art, and every day was worth it. Every minute the right side wins. Every hour the left side wins. Forever.

It’s a bloody brilliant idea.

One of my most successful blogs How to code Pong will be a good place to start. I’ll grab the code from my GitHub repository and get cracking.

The first step is to make both paddles computers.

Here’s the code that the AI uses to move, super sophisticated:

function processAI() {
  let middleOfPaddle = aiPaddle.y + aiPaddle.height / 2;
     
  if (middleOfPaddle > ball.y) {
    aiPaddle.isUp = true;
    aiPaddle.isDown = false;
  } else {
    aiPaddle.isDown = true;
    aiPaddle.isUp = false;
  }
}

We’re going to need to use this for both paddles, so let’s pass in the paddle we want to move:

function processAI(paddle) {
  const middleOfPaddle = paddle.y + paddle.height / 2;
     
  if (middleOfPaddle > ball.y) {
    paddle.isUp = true;
    paddle.isDown = false;
  } else {
    paddle.isDown = true;
    paddle.isUp = false;
  }
}

I imagine we’re going to need to revisit this as I’m sure it’ll accidentally let the odd goal in. Which would mean the wrong time would tell, we can’t be having that.

Next, I want to use the current time as the score. The left score will be the hours and the right score will be the minutes. We only want to set the time once and then have the game play out so that the right paddle scores each minute and the left paddle scores each hour.

In the setup function:

  const date = new Date();

  playerScore = new Score(width / 2 - 40, date.getHours());
  aiScore = new Score(width / 2 + 40, date.getMinutes());

I’ve still kept the playerScore and aiScore variables because I’m lazy and it’s easy to remember the player is on the left 😁

Next let’s make the paddles stop moving when they’re not displaying the correct time:

if (currentTime.getMinutes() == aiScore.score) {
    processAI(playerPaddle);
}
  
if (currentTime.getHours() == playerScore.score) {
    processAI(aiPaddle);
}

That was simple, let’s see it in action:

Lovely, now all that’s needed is to make sure it resets the time correctly when we get to 60 minutes or 24 hours:

// in the ball update method:
        if (this.x < this.r ) {
          aiScore.increment();
          this.reset();
        } else if (this.x > width + this.r) {
          playerScore.increment();
          aiScore.reset();
          if (playerScore == 24) {
            playerScore.reset();
          }
          this.reset();
        }

So when the player on the right scores — every minute — they get a point. When the player on the left scores — every hour — it increments the hour value and then resets the minutes. So when the clock strikes the hour, the player on the right will stop playing. When it’s been 24 hours we reset both sides, so the time will show 00:00.

It all works brilliantly, apart from one slightly major issue. Sometimes the paddles let one slip through the net when they shouldn’t. So that code I mentioned earlier to do with moving the paddles is going to need an upgrade. It’s not exactly smooth anyway!

First step, I’ve upped the refresh rate to 144, I’ve got a 240hz monitor but I don’t want to flex too much. A simple frameRate(144); in the setup method does the trick.

I only bother moving the paddles if they’re a sufficient distance away from the y position of the ball, otherwise I set the speed to 0. Also, I only alter their direction if the ball is coming in their direction:

function processAI(paddle, direction) {
  const difference =  Math.abs(ball.xSpeed - direction);
  const opposite = Math.abs(-ball.xSpeed - direction);
  if (difference < opposite) {
    const middleOfPaddle = paddle.y + paddle.height / 2;
    if (Math.abs(middleOfPaddle - ball.y) < 20) {
      paddle.speed = 0;
    } else if (middleOfPaddle > ball.y) {
      paddle.speed = -paddle.startSpeed;
    } else if (paddle.y < (height - paddle.height)) {
      paddle.speed = paddle.startSpeed;
    }
  }
}

I could go through the trouble of calculating the projected position of the ball when it hits the side of the wall the paddle is on, but my simple solution does a good enough job to make me not bother and go watch The Last of Us instead.

This was a rather silly idea, but I thoroughly enjoyed doing it. It’s important to add some fun to your programming!

All of the source code for this blog can be found here on Github, I’m sure you’ll be able to find a bug or two in it 🐛

And that’s pretty much all there is to it, if you liked this blog then please sign up for my newsletter and join an awesome community!

Leave a Reply