Skip to content
BetaDE
Lesson 4 of 4Programming games

Programming games · Lesson 4 · 60 minutes

Project: paddle duel

What you'll learn

  • Move a ball with a speed in x and y direction
  • Let the ball bounce off walls and paddles
  • Program a simple computer opponent

Two paddles, one ball

In the paddle duel there is a paddle on the left and one on the right. A ball flies back and forth between them. Whoever lets it through gives the other one a point. You play on the right, the computer plays on the left.

A ball with speed

So far everything moved in one direction only. Now the ball gets two speeds: dx for left-right and dy for up-down. Both are added in every frame. A negative dy means: upwards.

from codelix import game

game.window(400, 300)
ball = {"x": 50, "y": 250, "dx": 3, "dy": -2}


def update():
    ball["x"] = ball["x"] + ball["dx"]
    ball["y"] = ball["y"] + ball["dy"]
    game.circle(ball["x"], ball["y"], 8, "orange")


game.run(update)

The ball flies diagonally up and to the right – and then out of the picture.

Bouncing

When a ball bounces off a wall, one direction flips. At the ceiling, dy = -2 becomes dy = 2. You write it like this: ball["dy"] = -ball["dy"].

from codelix import game

game.window(400, 300)
ball = {"x": 50, "y": 150, "dx": 4, "dy": 3, "r": 10}


def update():
    ball["x"] = ball["x"] + ball["dx"]
    ball["y"] = ball["y"] + ball["dy"]
    if ball["x"] - ball["r"] < 0 or ball["x"] + ball["r"] > 400:
        ball["dx"] = -ball["dx"]
    if ball["y"] - ball["r"] < 0 or ball["y"] + ball["r"] > 300:
        ball["dy"] = -ball["dy"]
    game.circle(ball["x"], ball["y"], ball["r"], "orange")


game.run(update)

Watch out, sticky trap: If the ball is still a little inside the wall after flipping, it flips again in the next frame. Then it wobbles along the wall. That’s why the task also checks the direction: at the top, it only flips while it is flying upwards.

The computer opponent

How does a computer play? Very simply: if the ball is lower than the middle of its paddle, the paddle moves down. Otherwise up. Because it only manages 3 pixels per frame, you can beat it:

paddle = {"y": 120, "h": 60}
ball_y = 200

for frame in range(5):
    middle = paddle["y"] + paddle["h"] / 2
    if middle < ball_y:
        paddle["y"] = paddle["y"] + 3
    elif middle > ball_y:
        paddle["y"] = paddle["y"] - 3
    print(f"Frame {frame}: paddle at y = {paddle['y']}")

In the task, everything around it is already done: the controls, the opponent and the points. Only the functions for the ball are missing. After that you can play – with ↑ and ↓ or with the buttons below the game area.

Keep building: Play with two people: steer the left paddle with game.key("w") and game.key("s") instead of the computer. Make the ball a little faster after every hit. Or end the game with game.stop() as soon as someone has 5 points.

Your task

Your turn

Write the two functions that make the paddle duel work. 1. move_ball(ball, height): Move the ball by dx and dy. If it then hits the top (y - r < 0) or the bottom (y + r > height), flip dy – but only if it is moving towards that wall right now. 2. bounce(ball, paddle): If the ball touches the paddle, flip dx and return True, otherwise False. The ball counts as a square from x - r to x + r and from y - r to y + r.

from codelix import game

game.window(400, 300)
ball = {"x": 200, "y": 150, "dx": 4, "dy": 3, "r": 8}
left = {"x": 10, "y": 120, "w": 10, "h": 60}
right = {"x": 380, "y": 120, "w": 10, "h": 60}
score = {"left": 0, "right": 0}


def move_ball(ball, height):
    pass


def bounce(ball, paddle):
    return False


def update():
    # You steer the right paddle with ↑ and ↓, the computer steers the left one
    if game.key("up") and right["y"] > 0:
        right["y"] = right["y"] - 6
    if game.key("down") and right["y"] < 240:
        right["y"] = right["y"] + 6
    if left["y"] + 30 < ball["y"]:
        left["y"] = left["y"] + 3
    elif left["y"] + 30 > ball["y"]:
        left["y"] = left["y"] - 3

    move_ball(ball, 300)
    if ball["dx"] < 0:
        bounce(ball, left)
    else:
        bounce(ball, right)

    if ball["x"] < 0 or ball["x"] > 400:
        if ball["x"] < 0:
            score["right"] = score["right"] + 1
        else:
            score["left"] = score["left"] + 1
        ball["x"], ball["y"] = 200, 150

    game.line(200, 0, 200, 300, "#334", 2)
    game.rect(left["x"], left["y"], left["w"], left["h"], "white")
    game.rect(right["x"], right["y"], right["w"], right["h"], "cyan")
    game.circle(ball["x"], ball["y"], ball["r"], "orange")
    game.text(160, 10, f"{score['left']} : {score['right']}", "white", 24)


game.run(update)

Solve it without hints to earn a ⭐ on your progress page.

Show solution

Try it yourself first – mistakes teach you the most. Here is one possible solution:

from codelix import game

game.window(400, 300)
ball = {"x": 200, "y": 150, "dx": 4, "dy": 3, "r": 8}
left = {"x": 10, "y": 120, "w": 10, "h": 60}
right = {"x": 380, "y": 120, "w": 10, "h": 60}
score = {"left": 0, "right": 0}


def move_ball(ball, height):
    ball["x"] = ball["x"] + ball["dx"]
    ball["y"] = ball["y"] + ball["dy"]
    if ball["y"] - ball["r"] < 0 and ball["dy"] < 0:
        ball["dy"] = -ball["dy"]
    if ball["y"] + ball["r"] > height and ball["dy"] > 0:
        ball["dy"] = -ball["dy"]


def bounce(ball, paddle):
    r = ball["r"]
    if (ball["x"] - r < paddle["x"] + paddle["w"] and ball["x"] + r > paddle["x"]
            and ball["y"] - r < paddle["y"] + paddle["h"] and ball["y"] + r > paddle["y"]):
        ball["dx"] = -ball["dx"]
        return True
    return False


def update():
    # You steer the right paddle with ↑ and ↓, the computer steers the left one
    if game.key("up") and right["y"] > 0:
        right["y"] = right["y"] - 6
    if game.key("down") and right["y"] < 240:
        right["y"] = right["y"] + 6
    if left["y"] + 30 < ball["y"]:
        left["y"] = left["y"] + 3
    elif left["y"] + 30 > ball["y"]:
        left["y"] = left["y"] - 3

    move_ball(ball, 300)
    if ball["dx"] < 0:
        bounce(ball, left)
    else:
        bounce(ball, right)

    if ball["x"] < 0 or ball["x"] > 400:
        if ball["x"] < 0:
            score["right"] = score["right"] + 1
        else:
            score["left"] = score["left"] + 1
        ball["x"], ball["y"] = 200, 150

    game.line(200, 0, 200, 300, "#334", 2)
    game.rect(left["x"], left["y"], left["w"], left["h"], "white")
    game.rect(right["x"], right["y"], right["w"], right["h"], "cyan")
    game.circle(ball["x"], ball["y"], ball["r"], "orange")
    game.text(160, 10, f"{score['left']} : {score['right']}", "white", 24)


game.run(update)