Building a Snake Game using Prompt Engineering

Using Prompt Engineering to build a simple game.

Creating a Snake Game with AI Prompt Engineering

In today's world, artificial intelligence (AI) is revolutionizing the way we approach problem-solving and task automation. One fascinating application of AI is prompt engineering, which involves crafting human-readable instructions (prompts) to guide AI models in completing specific tasks. In this blog post, we'll explore how we leveraged AI prompt engineering to create a classic snake game.

The Journey Begins

Our journey started with a simple idea: to build a snake game using AI prompt engineering techniques. We wanted to demonstrate the capabilities of modern AI systems in creating interactive applications while highlighting the power of well-crafted prompts.

Setting the Stage with AI Prompts

The first step was to provide the AI model with a clear and concise prompt, outlining the desired outcome and requirements for the snake game. We crafted a prompt that encompassed the game's mechanics, such as snake movement, food collision, score tracking, and game over conditions.

Open AI Logo

Sure, here's a complete Python code for a basic snake game with various features such as movement, collision detection, score tracking, and game over condition. This implementation uses the pygame module for graphics and event handling:

import pygame
import random

# Initialize Pygame
pygame.init()

# Set up the game window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Snake Game")

# Define colors
black = (0, 0, 0)
green = (0, 255, 0)
red = (255, 0, 0)

# Define game components
snake_position = [100, 100]
snake_body = [[100, 100], [90, 100], [80, 100]]
food_position = [random.randrange(1, (window_width // 10)) * 10, random.randrange(1, (window_height // 10)) * 10]
food_spawned = True
direction = 'RIGHT'
change_to = direction
score = 0

# Define game clock
clock = pygame.time.Clock()

# Game loop
game_over = False
while not game_over:
    # Handle events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_over = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT and change_to != 'RIGHT':
                change_to = 'LEFT'
            elif event.key == pygame.K_RIGHT and change_to != 'LEFT':
                change_to = 'RIGHT'
            elif event.key == pygame.K_UP and change_to != 'DOWN':
                change_to = 'UP'
            elif event.key == pygame.K_DOWN and change_to != 'UP':
                change_to = 'DOWN'

    # Update game state
    if change_to == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    elif change_to == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'
    elif change_to == 'UP' and direction != 'DOWN':
        direction = 'UP'
    elif change_to == 'DOWN' and direction != 'UP':
        direction = 'DOWN'

    # Move the snake
    if direction == 'LEFT':
        snake_position[0] -= 10
    elif direction == 'RIGHT':
        snake_position[0] += 10
    elif direction == 'UP':
        snake_position[1] -= 10
    elif direction == 'DOWN':
        snake_position[1] += 10

    # Insert the new head position at the beginning of the snake body
    snake_body.insert(0, list(snake_position))

    # Check for food collision
    if snake_position[0] == food_position[0] and snake_position[1] == food_position[1]:
        food_spawned = False
        score += 10
    else:
        snake_body.pop()

    # Spawn new food
    if not food_spawned:
        food_position = [random.randrange(1, (window_width // 10)) * 10, random.randrange(1, (window_height // 10)) * 10]
        food_spawned = True

    # Draw game elements
    window.fill(black)
    for position in snake_body:
        pygame.draw.rect(window, green, (position[0], position[1], 10, 10))
    pygame.draw.rect(window, red, (food_position[0], food_position[1], 10, 10))
    pygame.display.set_caption(f"Snake Game | Score: {score}")
    pygame.display.update()

    # Check for collisions
    for body_part in snake_body[1:]:
        if snake_position[0] == body_part[0] and snake_position[1] == body_part[1]:
            game_over = True
            break

    # Check for wall collisions
    if (
        snake_position[0] < 0
        or snake_position[0] > window_width - 10
        or snake_position[1] < 0
        or snake_position[1] > window_height - 10
    ):
        game_over = True

    # Set the game speed
    clock.tick(15)

# Quit Pygame
pygame.quit()

Here's a breakdown of the code:

  1. The game components are initialized, including the snake's position, body, food position, direction, score, and game clock.
  2. The game loop continuously runs until the game_over condition is met.
  3. User input is handled through the pygame.KEYDOWN event, allowing the player to change the snake's direction.
  4. The snake's position is updated based on the current direction.
  5. The snake's body is updated by inserting the new head position at the beginning and removing the last position if the snake hasn't eaten food.
  6. Food collision is checked, and if the snake eats the food, the score is incremented, and a new food position is generated.
  7. The game elements (snake body and food) are drawn on the window using pygame.draw.rect.
  8. Collisions with the snake's body and walls are checked, and the game_over condition is set accordingly.
  9. The game speed is controlled using clock.tick(15), which limits the frame rate to 15 frames per second.

This code covers the basic functionality of a snake game, including movement, collision detection, score tracking, and game over condition. You can further enhance the game by adding additional features such as sound effects, levels, high scores, and a pause/resume functionality.


Snake Game with Prompt Engineering

The AI model, trained on vast amounts of data, interpreted our prompt and generated a Python implementation of the snake game. This initial version included the core functionality, like snake movement, food collision detection, and score tracking.

Iterative Refinement through Prompt Engineering

While the initial output was impressive, we recognized the need for further refinements and enhancements. This is where the true power of prompt engineering came into play. We iteratively refined our prompts, providing additional instructions and clarifications, guiding the AI model to improve and expand upon the initial code.

Convert this into an HTML5 and CSS3 website code.

The AI model seamlessly adapted and generated the corresponding HTML, CSS, and JavaScript code, enabling us to create a web-based snake game.

Adding Personal Touches

As we continued refining the prompts, we sought to incorporate personalized elements into the project. We asked the AI model to modify the screen size, adjust the snake's speed, and include an introduction blurb mentioning the game's creation using AI prompt engineering.

Make the screen size 800x600 for the game.
Reproduce the code with a reduced speed.
Include an introduction blurb indicating this game was created with AI Prompt Engineering.

The AI model dutifully implemented these changes, resulting in a customized and engaging experience for the users.

Enhancing the User Experience

To further enrich the game's user experience, we prompted the AI model to maintain a list of high scores:

Maintain a list of high scores.

The AI model seamlessly integrated this feature, allowing players to track their progress and compete with others for the top spot on the leaderboard.

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game (AI-Powered)</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div id="game-container">
        <h1>Snake Game (Created with AI Prompt Engineering)</h1>
        <p id="intro">This snake game was created using AI prompt engineering techniques to demonstrate the capabilities of modern AI systems in building interactive applications.</p>
        <canvas id="game-canvas"></canvas>
        <div id="score-container">Score: <span id="score">0</span></div>
        <div id="high-scores-container">
            <h2>High Scores</h2>
            <ol id="high-scores-list"></ol>
        </div>
    </div>
    <script src="game.js"></script>
</body>
</html>

CSS (styles.css):

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #333;
    font-family: Arial, sans-serif;
    color: #fff;
}

#game-container {
    text-align: center;
    max-width: 800px;
    padding: 20px;
}

#intro {
    margin-bottom: 20px;
}

#game-canvas {
    border: 1px solid #fff;
    background-color: #000;
}

#score-container {
    margin-top: 10px;
    font-size: 18px;
}

#high-scores-container {
    margin-top: 20px;
    text-align: left;
}

#high-scores-list {
    list-style-type: decimal;
    padding-left: 20px;
}

JavaScript (game.js):

// Get the canvas element and its 2D rendering context
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');

// Set the canvas dimensions
canvas.width = 800;
canvas.height = 600;

// Define game variables
let snake = [
    { x: 400, y: 300 },
    { x: 390, y: 300 },
    { x: 380, y: 300 }
];
let food = { x: 200, y: 200 };
let direction = 'right';
let score = 0;
let highScores = JSON.parse(localStorage.getItem('highScores')) || [];

// Draw the game elements
function drawGame() {
    // Clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Draw the snake
    ctx.fillStyle = 'green';
    snake.forEach(segment => {
        ctx.fillRect(segment.x, segment.y, 10, 10);
    });

    // Draw the food
    ctx.fillStyle = 'red';
    ctx.fillRect(food.x, food.y, 10, 10);
}

// Update the game state
function updateGame() {
    // Move the snake
    const head = { x: snake[0].x, y: snake[0].y };
    switch (direction) {
        case 'up':
            head.y -= 10;
            break;
        case 'down':
            head.y += 10;
            break;
        case 'left':
            head.x -= 10;
            break;
        case 'right':
            head.x += 10;
            break;
    }
    snake.unshift(head);

    // Check for food collision
    if (head.x === food.x && head.y === food.y) {
        score += 10;
        document.getElementById('score').textContent = score;
        food = { x: Math.floor(Math.random() * 79) * 10, y: Math.floor(Math.random() * 59) * 10 };
    } else {
        snake.pop();
    }

    // Check for collisions
    if (
        head.x < 0 || head.x >= canvas.width ||
        head.y < 0 || head.y >= canvas.height ||
        snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y)
    ) {
        gameOver();
    }
}

// Handle keyboard input
document.addEventListener('keydown', event => {
    const key = event.key.toLowerCase();
    if (key === 'arrowup' && direction !== 'down') {
        direction = 'up';
    } else if (key === 'arrowdown' && direction !== 'up') {
        direction = 'down';
    } else if (key === 'arrowleft' && direction !== 'right') {
        direction = 'left';
    } else if (key === 'arrowright' && direction !== 'left') {
        direction = 'right';
    }
});

// Game over function
function gameOver() {
    const gameOverMessage = `Game Over! Your score: ${score}`;
    alert(gameOverMessage);

    // Save high score
    const playerName = prompt('Enter your name for the high score list:');
    if (playerName) {
        highScores.push({ name: playerName, score: score });
        highScores.sort((a, b) => b.score - a.score);
        highScores = highScores.slice(0, 10); // Keep only the top 10 scores
        localStorage.setItem('highScores', JSON.stringify(highScores));
        updateHighScoresList();
    }

    location.reload();
}

// Update the high scores list
function updateHighScoresList() {
    const highScoresList = document.getElementById('high-scores-list');
    highScoresList.innerHTML = '';

    highScores.forEach((entry, index) => {
        const listItem = document.createElement('li');
        listItem.textContent = `${index + 1}. ${entry.name} - ${entry.score}`;
        highScoresList.appendChild(listItem);
    });
}

// Game loop
let lastFrameTime = 0;
const frameDelay = 150; // Adjust this value to control the snake's speed (higher value = slower speed)

function gameLoop(currentTime) {
    const timeSinceLastFrame = currentTime - lastFrameTime;

    if (timeSinceLastFrame >= frameDelay) {
        lastFrameTime = currentTime;
        drawGame();
        updateGame();
    }

    requestAnimationFrame(gameLoop);
}

// Initialize the high scores list
updateHighScoresList();

// Start the game loop
gameLoop();

Here's what has been added or modified:

HTML:

  • Added an h1 heading with the game title and a mention of "AI Prompt Engineering".
  • Added a p element with an id="intro" to display an introduction blurb about the game being created using AI prompt engineering.
  • Added a new div with id="high-scores-container" to display the list of high scores.
  • Inside the high scores container, added an h2 heading and an ordered list (ol) with id="high-scores-list" to display the high scores.

CSS:

  • Added styles for the introduction blurb, high scores container, and high scores list.

JavaScript:

  • Initialized an empty highScores array or retrieved the existing high scores from localStorage if available.
  • Added a gameOver function that handles the game over condition and prompts the player to enter their name for the high score list.
  • Inside the gameOver function, the player's score is added to the highScores array, the array is sorted in descending order based on the scores, and only the top 10 scores are kept.
  • The highScores array is then saved to localStorage for persistence across

Conclusion

Throughout this project, we witnessed the remarkable capabilities of AI prompt engineering in creating interactive applications. By crafting well-structured prompts and iteratively refining them, we were able to guide the AI model in generating a fully-functional snake game with various features and enhancements.

See the game in action here

This experience showcases the potential of AI prompt engineering as a powerful tool for developers, enabling them to leverage AI models for task automation, code generation, and rapid prototyping. As AI technologies continue to evolve, the possibilities for creating innovative and engaging applications through prompt engineering are truly exciting.