Pong in Python
Pygame Online · 155 lines · Up and Down
Keep the ball in play against the computer. First to five wins.
How it works
Serving the ball
The ball starts in the middle with a random slope and waits 45 frames (three quarters of a second at 60 frames a second) before it moves, so you can see where it is going.
def serve(direction): """Ball back to the middle; it sets off (1 = right, -1 = left) after a pause.""" global ball_x, ball_y, ball_dx, ball_dy, serve_timer ball_x, ball_y = WIDTH / 2 - 5, HEIGHT / 2 - 5 ball_dx = 4 * direction ball_dy = random.uniform(-2.5, 2.5) serve_timer = 45 ball.topleft = (round(ball_x), round(ball_y))
Where the ball hits the paddle sets its angle
offset is -1 at the top of the paddle, 0 in the middle and 1 at the bottom. The ball leaves steeper the further from the middle it hits, and a little faster each time, up to a limit.
def bounce(paddle, direction): """Send the ball back: the further from the paddle's middle, the steeper.""" global ball_dx, ball_dy offset = (ball.centery - paddle.centery) / (paddle.height / 2) # -1 to 1 speed = min(abs(ball_dx) * 1.07, 9) ball_dx = speed * direction ball_dy = offset * 5
The computer player
The computer only chases the ball while it comes its way, aiming at a random point on its own paddle, and it is slower than the ball can get. That is what makes it beatable.
# The computer chases the ball while it comes its way, otherwise it # drifts back to the middle. It is slower than the ball can be. if ball_dx > 0: target = ball.centery + computer_aim else: target = HEIGHT // 2 if computer.centery < target - 4: computer.y += COMPUTER_SPEED elif computer.centery > target + 4: computer.y -= COMPUTER_SPEED
Make it yours
Change the code above and press Run again. Some ideas:
- Make it a two-player game: move the right paddle with W and S instead of the computer code.
- Change
WIN_SCOREto play longer matches. - Make the computer harder by raising
COMPUTER_SPEED, or easier by lowering it. - Add a second ball after each point is scored.
The whole program
Plain pygame: it runs the same on a computer with Python and pygame installed.
import random import pygame pygame.init() WIDTH, HEIGHT = 480, 360 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pong") clock = pygame.time.Clock() font = pygame.font.Font(None, 30) score_font = pygame.font.Font(None, 72) big_font = pygame.font.Font(None, 60) BG = (16, 18, 22) NET = (40, 44, 52) YELLOW = (255, 212, 59) BLUE = (48, 105, 152) LIGHT_BLUE = (90, 162, 224) TEXT = (230, 232, 235) WIN_SCORE = 5 PLAYER_SPEED = 6 COMPUTER_SPEED = 3 START_KEYS = (pygame.K_UP, pygame.K_SPACE, pygame.K_RETURN) player = pygame.Rect(16, HEIGHT // 2 - 30, 10, 60) computer = pygame.Rect(WIDTH - 26, HEIGHT // 2 - 30, 10, 60) ball = pygame.Rect(0, 0, 10, 10) def serve(direction): """Ball back to the middle; it sets off (1 = right, -1 = left) after a pause.""" global ball_x, ball_y, ball_dx, ball_dy, serve_timer ball_x, ball_y = WIDTH / 2 - 5, HEIGHT / 2 - 5 ball_dx = 4 * direction ball_dy = random.uniform(-2.5, 2.5) serve_timer = 45 ball.topleft = (round(ball_x), round(ball_y)) def bounce(paddle, direction): """Send the ball back: the further from the paddle's middle, the steeper.""" global ball_dx, ball_dy offset = (ball.centery - paddle.centery) / (paddle.height / 2) # -1 to 1 speed = min(abs(ball_dx) * 1.07, 9) ball_dx = speed * direction ball_dy = offset * 5 def draw_text(text, text_font, color, center): image = text_font.render(text, True, color) screen.blit(image, image.get_rect(center=center)) player_score = 0 computer_score = 0 computer_aim = 0 # where on its paddle the computer tries to hit the ball state = "ready" # "ready", "playing" or "over" wait = 0 # frames until a new game may start serve(1) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN and event.key in START_KEYS: if state == "ready": state = "playing" elif state == "over" and wait == 0: player_score = 0 computer_score = 0 serve(1) state = "playing" # Your paddle follows the arrow keys. keys = pygame.key.get_pressed() if keys[pygame.K_UP]: player.y -= PLAYER_SPEED if keys[pygame.K_DOWN]: player.y += PLAYER_SPEED player.clamp_ip(screen.get_rect()) if wait > 0: wait -= 1 if state == "playing": # The computer chases the ball while it comes its way, otherwise it # drifts back to the middle. It is slower than the ball can be. if ball_dx > 0: target = ball.centery + computer_aim else: target = HEIGHT // 2 if computer.centery < target - 4: computer.y += COMPUTER_SPEED elif computer.centery > target + 4: computer.y -= COMPUTER_SPEED computer.clamp_ip(screen.get_rect()) if serve_timer > 0: serve_timer -= 1 else: ball_x += ball_dx ball_y += ball_dy if ball_y < 0 or ball_y > HEIGHT - ball.height: ball_dy = -ball_dy ball_y = max(0, min(ball_y, HEIGHT - ball.height)) ball.topleft = (round(ball_x), round(ball_y)) # Bounce only if the ball has not already gone past the paddle. if ball_dx < 0 and ball.colliderect(player) and ball.left > player.left: bounce(player, 1) ball_x = player.right computer_aim = random.randint(-25, 25) elif ball_dx > 0 and ball.colliderect(computer) and ball.right < computer.right: bounce(computer, -1) ball_x = computer.left - ball.width if ball.right < 0: computer_score += 1 serve(-1) elif ball.left > WIDTH: player_score += 1 serve(1) if player_score == WIN_SCORE or computer_score == WIN_SCORE: state = "over" wait = 45 screen.fill(BG) for y in range(0, HEIGHT, 24): pygame.draw.rect(screen, NET, (WIDTH // 2 - 2, y + 6, 4, 12)) draw_text(str(player_score), score_font, YELLOW, (WIDTH // 2 - 60, 44)) draw_text(str(computer_score), score_font, LIGHT_BLUE, (WIDTH // 2 + 60, 44)) pygame.draw.rect(screen, YELLOW, player, border_radius=3) pygame.draw.rect(screen, LIGHT_BLUE, computer, border_radius=3) if state == "playing": pygame.draw.rect(screen, TEXT, ball, border_radius=3) if state != "playing": panel = pygame.Rect(0, 0, 360, 140) panel.center = (WIDTH // 2, HEIGHT // 2 + 20) pygame.draw.rect(screen, BG, panel, border_radius=12) pygame.draw.rect(screen, BLUE, panel, 2, border_radius=12) if state == "ready": draw_text("Pong", big_font, YELLOW, (panel.centerx, panel.y + 38)) draw_text("Up and Down move your paddle", font, TEXT, (panel.centerx, panel.y + 82)) draw_text(f"First to {WIN_SCORE}. Press Up to start", font, TEXT, (panel.centerx, panel.y + 112)) else: title = "You win!" if player_score == WIN_SCORE else "Computer wins" draw_text(title, big_font, YELLOW, (panel.centerx, panel.y + 45)) draw_text("Press Up or Space to play again", font, TEXT, (panel.centerx, panel.y + 100)) pygame.display.flip() clock.tick(60) pygame.quit()