Categories
Artificial Intelligence Game Development PC Nerding

Blazing fast game development using LLMs

I keep saying it: AI is advancing too fast, not keeping up will make us humans obsolete. While generative AI will not replace a skilled photographer, writer, or programmer, it’s already good enough to replace the hobbyist to low skilled ones. I used photographer, writer and programmer as example as I feel I’m in the hobby-low skilled range in all three categories and I’m sure AI can already replace people at my level. /Rant end

Today I’m sharing my latest conversation with a LLM, specifically Codestral in this case, about game development. I don’t label myself a game developer, I made a few over the years, I participated in Ludum Dare competitions, but it’s not one of my main hobbies.

My conversation started with a simple prompt like this:

🐮: Don’t waste time talking, only code. Make a pygame pong game.

💻: Sure, here is a simple implementation of the classic Pong game using Pygame:

import pygame
import sys
import random

# Initialize Pygame
pygame.init()

# Set up some constants
WIDTH, HEIGHT = 800, 600
BALL_SPEED = [4, 4]
PADDLE_SPEED = 5
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Set up the window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Pygame Pong')

# Set up the game objects
ball = pygame.Rect(WIDTH // 2 - 15, HEIGHT // 2 - 15, 30, 30)
player = pygame.Rect(WIDTH - 20, HEIGHT // 2 - 70, 10, 140)
opponent = pygame.Rect(10, HEIGHT // 2 - 70, 10, 140)

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # Move the ball
    ball.left += BALL_SPEED[0]
    ball.top += BALL_SPEED[1]

    # Collision with walls
    if ball.top <= 0 or ball.bottom >= HEIGHT:
        BALL_SPEED[1] = -BALL_SPEED[1]
    if ball.left <= 0 or ball.right >= WIDTH:
        BALL_SPEED = [4, 4]
        ball.center = (WIDTH // 2, HEIGHT // 2)

    # Collision with paddles
    if ball.colliderect(player) or ball.colliderect(opponent):
        BALL_SPEED[0] = -BALL_SPEED[0]

    # Move the player paddle
    keys = pygame.key.get_pressed()
    if keys[pygame.K_UP]:
        player.top -= PADDLE_SPEED
    if keys[pygame.K_DOWN]:
        player.top += PADDLE_SPEED

    # Move the opponent paddle (AI)
    if opponent.top < ball.y:
        opponent.top += PADDLE_SPEED
    elif opponent.bottom > ball.y:
        opponent.top -= PADDLE_SPEED

    # Draw everything
    screen.fill(BLACK)
    pygame.draw.ellipse(screen, WHITE, ball)
    pygame.draw.rect(screen, WHITE, player)
    pygame.draw.rect(screen, WHITE, opponent)

    # Flip the display
    pygame.display.flip()

This game uses two paddles (one controlled by the user and one by a simple AI). The ball bounces off the walls and paddles. If the ball hits the left or right edge of the screen, it resets to the center.

Now, this isn’t rocket science, Pong is considered the “Hello World!” of coding games, but I never ever programmed something from scratch in Python and I have absolutely zero knowledge of Pygame libraries. Making even a simple Pong game would have required at least a couple hours of reading documentation or a good tutorial. Codestral delivered a neatly commented code in a few seconds. If you run this code through python, the game work, but it has a noticeable problem: it runs at like a million frame per seconds. Let me try telling the LLM to add a frame limiter.

🐮: add a frame limiter

💻: Sure, here is the same Pong game code with a frame limiter added:

import pygame
import sys
import random

# Initialize Pygame
pygame.init()

# Set up some constants
WIDTH, HEIGHT = 800, 600
BALL_SPEED = [4, 4]
PADDLE_SPEED = 5
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
FPS = 60 # Frames per second

# Set up the window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Pygame Pong')

# Set up the game objects
ball = pygame.Rect(WIDTH // 2 - 15, HEIGHT // 2 - 15, 30, 30)
player = pygame.Rect(WIDTH - 20, HEIGHT // 2 - 70, 10, 140)
opponent = pygame.Rect(10, HEIGHT // 2 - 70, 10, 140)

# Create a clock object to control the frame rate
clock = pygame.time.Clock()

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # Move the ball
    ball.left += BALL_SPEED[0]
    ball.top += BALL_SPEED[1]

    # Collision with walls
    if ball.top <= 0 or ball.bottom >= HEIGHT:
        BALL_SPEED[1] = -BALL_SPEED[1]
    if ball.left <= 0 or ball.right >= WIDTH:
        BALL_SPEED = [4, 4]
        ball.center = (WIDTH // 2, HEIGHT // 2)

    # Collision with paddles
    if ball.colliderect(player) or ball.colliderect(opponent):
        BALL_SPEED[0] = -BALL_SPEED[0]

    # Move the player paddle
    keys = pygame.key.get_pressed()
    if keys[pygame.K_UP]:
        player.top -= PADDLE_SPEED
    if keys[pygame.K_DOWN]:
        player.top += PADDLE_SPEED

    # Move the opponent paddle (AI)
    if opponent.top < ball.y:
        opponent.top += PADDLE_SPEED
    elif opponent.bottom > ball.y:
        opponent.top -= PADDLE_SPEED

    # Draw everything
    screen.fill(BLACK)
    pygame.draw.ellipse(screen, WHITE, ball)
    pygame.draw.rect(screen, WHITE, player)
    pygame.draw.rect(screen, WHITE, opponent)

    # Flip the display and limit the frame rate
    pygame.display.flip()
    clock.tick(FPS)

The game will now run at a maximum of 60 frames per second (fps), which can help improve performance and make the game smoother.

Again, I know nothing about python/pygame, the LLM knows better and decides to add a clock.tick(FPS) at the end of the main loop, and sets the FPS constant grouped with the other ones at the top. All I had to do was to ask. Now the game is actually playable, but I don’t want the game to start automatically and I want a score system with a win condition.

🐮: Add a start button. Add a score system. Add a win condition for when the player or cpu reaches 5 goals.

💻: Sure, I’ve added these changes to the code:

import pygame
import sys
import random

# Initialize Pygame
pygame.init()

# Set up some constants
WIDTH, HEIGHT = 800, 600
BALL_SPEED = [4, 4]
PADDLE_SPEED = 5
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
FPS = 60 # Frames per second

# Set up the window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Pygame Pong')

# Set up game variables
player_score = 0
opponent_score = 0
game_started = False

# Font for the score text
font = pygame.font.Font(None, 36)

def draw_text(text, font, color, x, y):
    img = font.render(text, True, color)
    screen.blit(img, (x, y))

# Set up the game objects
ball = pygame.Rect(WIDTH // 2 - 15, HEIGHT // 2 - 15, 30, 30)
player = pygame.Rect(WIDTH - 20, HEIGHT // 2 - 70, 10, 140)
opponent = pygame.Rect(10, HEIGHT // 2 - 70, 10, 140)
start_button = pygame.Rect(WIDTH // 2 - 50, HEIGHT // 2, 100, 50)

# Create a clock object to control the frame rate
clock = pygame.time.Clock()

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN and not game_started:
            # Start the game when clicking the start button
            if start_button.collidepoint(event.pos):
                game_started = True

    if game_started:
        # Move the ball
        ball.left += BALL_SPEED[0]
        ball.top += BALL_SPEED[1]

        # Collision with walls
        if ball.top <= 0 or ball.bottom >= HEIGHT:
            BALL_SPEED[1] = -BALL_SPEED[1]
        if ball.left <= 0:
            player_score += 1
            ball = pygame.Rect(WIDTH // 2 - 15, HEIGHT // 2 - 15, 30, 30)
            BALL_SPEED = [4, 4]
        elif ball.right >= WIDTH:
            opponent_score += 1
            ball = pygame.Rect(WIDTH // 2 - 15, HEIGHT // 2 - 15, 30, 30)
            BALL_SPEED = [-4, 4]

        # Collision with paddles
        if ball.colliderect(player):
            BALL_SPEED[0] = -BALL_SPEED[0]
        elif ball.collidererect(opponent):
            BALL_SPEED[0] = -BALL_SPEED[0]

        # Move the player paddle with keyboard
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP]:
            player.top -= PADDLE_SPEED
        if keys[pygame.K_DOWN]:
            player.top += PADDLE_SPEED
    else:
        # Move the opponent paddle towards the ball (AI)
        if opponent.top < ball.y:
            opponent.top += PADDLE_SPEED
        elif opponent.bottom > ball.y:
            opponent.top -= PADDLE_SPEED

    # Draw everything
    screen.fill(BLACK)
    pygame.draw.ellipse(screen, WHITE, ball)
    pygame.draw.rect(screen, WHITE, player)
    pygame.draw.rect(screen, WHITE, opponent)
    draw_text("Player: " + str(player_score), font, WHITE, 10, 10)
    draw_text("CPU: " + str(opponent_score), font, WHITE, WIDTH - 90, 10)

    # Draw start button if the game hasn't started yet
    if not game_started:
        pygame.draw.rect(screen, WHITE, start_button)
        draw_text("START", font, BLACK, WIDTH // 2 - 30, HEIGHT // 2 + 15)

    # Check win condition
    if player_score >= 5:
        print("Player wins!")
        game_started = False
        player_score = 0
        opponent_score = 0
    elif opponent_score >= 5:
        print("CPU wins!")
        game_started = False
        player_score = 0
        opponent_score = 0

    # Flip the display and limit the frame rate
    pygame.display.flip()
    clock.tick(FPS)

Now, there is a start button that appears when the game hasn’t started yet. After clicking this button, the game will begin. A score system has been added, which increases the player’s or CPU’s score by one every time they score a goal. The win condition has also been implemented: If either the player or the CPU reaches a score of 5 goals, the respective win message will be printed and the game will reset for a new round.

This bunch of multiplicated 0s an 1s absolutely delivered what I asked for. This time the code doesn’t run, tho, and required manual intervention:
– There was and odd typo to fix, the LLM calls colliders colliderect, and in the typo collidererect at line #72 there’s two extra characters. Luckily python outputted to my cmd the line, the error and the possible fix.
– The AI paddle didn’t move after I clicked the Start, which was caused by a stray else statement, at line #81. This one required me too actually look where the paddle moving code was in the code.

Now I have a complete game:

A couple things remain to be fixed: the ball and AI paddle move at the same speed making impossible for the player to score a goal and the win condition is outputted into the console instead of ingame. Why do I have to do the heavy lifting?

🐮: There are three things to fix: The AI paddle is to fast compared to the ball; The AI paddle doesn’t move after I press start; Player/CPU wins should be outputted in an ingame box, not into the console.

💻: Sure, let’s make these modifications.
1. Let’s slow down the opponent’s paddle by using a smaller speed factor for it.
2. To make sure the AI paddle moves after we press start, I will move its motion inside of our main game loop and include a condition to check if the game has started.
3. For displaying the winner within the game screen, I’ll add some text that says “Player wins!” or “CPU wins!”. This will be drawn on the screen using pygame’s font rendering functionality. When either player reaches 5 goals, we’ll set a flag to indicate that the game is over and display the win message in each frame until a new game starts

Let me make these changes for you.

import pygame
import sys
import random

# Initialize Pygame
pygame.init()

# Set up some constants
WIDTH, HEIGHT = 800, 600
BALL_SPEED = [4, 4]
PADDLE_SPEED = 5
AI_PADDLE_SPEED = 3 # Slowing down the AI paddle
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
FPS = 60 # Frames per second

# Set up the window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Pygame Pong')

# Set up game variables
player_score = 0
opponent_score = 0
game_started = False
game_over = False # New flag for when the game is over
winner = None # Variable to store the winner

# Font for the score text
font = pygame.font.Font(None, 36)

def draw_text(text, font, color, x, y):
    img = font.render(text, True, color)
    screen.blit(img, (x, y))

# Set up the game objects
ball = pygame.Rect(WIDTH // 2 - 15, HEIGHT // 2 - 15, 30, 30)
player = pygame.Rect(WIDTH - 20, HEIGHT // 2 - 70, 10, 140)
opponent = pygame.Rect(10, HEIGHT // 2 - 70, 10, 140)
start_button = pygame.Rect(WIDTH // 2 - 50, HEIGHT // 2, 100, 50) # Start button rectangle object

# Create a clock object to control the frame rate
clock = pygame.time.Clock()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN and not game_started: # Check if the mouse was clicked and the game hasn't started yet
            if start_button.collidepoint(event.pos): # If the click was on the start button, then start the game
                game_started = True

    keys = pygame.key.get_pressed()
    if keys[pygame.K_UP] and player.top > 0:
        player.move_ip(0, -PADDLE_SPEED)
    if keys[pygame.K_DOWN] and player.bottom < HEIGHT:
        player.move_ip(0, PADDLE_SPEED)

    # Move the AI paddle if the game has started
    if game_started:
        if ball.centery > opponent.centery + AI_PADDLE_SPEED // 2 and opponent.bottom < HEIGHT:
            opponent.move_ip(0, AI_PADDLE_SPEED)
        elif ball.centery < opponent.centery - AI_PADDLE_SPEED // 2 and opponent.top > 0:
            opponent.move_ip(0, -AI_PADDLE_SPEED)

    # Move the ball if the game has started and isn't over
    if game_started and not game_over:
        ball.move_ip(*BALL_SPEED)
        if ball.left <= 0 or ball.right >= WIDTH:
            BALL_SPEED[0] = -BALL_SPEED[0]
            if ball.left <= 0:
                player_score += 1
            else:
                opponent_score += 1
        if ball.top <= 0 or ball.bottom >= HEIGHT:
            BALL_SPEED[1] = -BALL_SPEED[1]
        if ball.colliderect(player) or ball.colliderect(opponent):
            BALL_SPEED[0] = -BALL_SPEED[0]

    # Draw everything on the screen
    screen.fill(BLACK)
    pygame.draw.rect(screen, WHITE, player)
    pygame.draw.rect(screen, WHITE, opponent)
    pygame.draw.ellipse(screen, WHITE, ball)
    draw_text("Player: " + str(player_score), font, WHITE, 10, 10)
    draw_text("CPU: " + str(opponent_score), font, WHITE, WIDTH - 90, 10)

    # Draw the start button if the game hasn't started yet
    if not game_started and not game_over:
        pygame.draw.rect(screen, WHITE, start_button)
        draw_text("START", font, BLACK, WIDTH // 2 - 30, HEIGHT // 2 + 15)
    # If the game is over, display the winner message in the center of the screen
    elif game_over:
        draw_text(winner + " wins!", font, WHITE, WIDTH // 2 - 80, HEIGHT // 2)

    # Check if either player has reached 5 points and end the game if so
    if player_score >= 5:
        winner = "Player"
        game_over = True
        game_started = False
        player_score = 0
        opponent_score = 0
    elif opponent_score >= 5:
        winner = "CPU"
        game_over = True
        game_started = False
        player_score = 0
        opponent_score = 0

    # If the game is over and we press enter, reset the game state
    if keys[pygame.K_RETURN] and game_over:
        winner = None
        game_over = False
        ball.center = (WIDTH // 2, HEIGHT // 2)
        player.center = (50, HEIGHT // 2)
        opponent.center = (WIDTH - 50, HEIGHT // 2)
        BALL_SPEED = [random.choice([-4, 4]), random.choice([-4, 4])]

    pygame.display.update()
    clock.tick(60)

This is a simple implementation of a pong game in Python using the Pygame library. The player can move their paddle up and down to hit the ball back towards their opponent (the computer). When the ball hits one of the sides of the screen, a point is scored for the other player. If either player reaches 5 points, they win and the game ends. Pressing enter will reset the game state so that you can play again.
The AI paddle is controlled by moving it towards the center of the ball’s y-coordinate if it is above or below the center of the paddle. This gives the computer a simple but effective strategy for hitting the ball back towards the player. The speed at which the ball and paddles move can be adjusted to change the difficulty of the game.

The newly updated code is playable without any modification.
There are a few new problems:
– The ball now doesn’t recenter after a goal, the code was actually removed.
– You click Start for the first game, but use Enter to reset after a match.

I got bored of toying around and I will stop here, making this blog post took me over an hour, while making the game probably took the AI five minutes at most. I’m incredibly amused with the AI’s skill.

By Andrea Giorgio "Muu?" Cerioli

Italian dad, developer, designer, maker who loves everything in technology: AI, mechanics, electronics, IT, 3D printing.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.