DEV Community

Cover image for From AI to Arcade: Building a Pygame Adventure with Amazon Q

From AI to Arcade: Building a Pygame Adventure with Amazon Q

Falling Stars Game: Powered by Amazon Q CLI

The Falling Stars Game is an addictive arcade game I built entirely using Amazon Q CLI, a generative AI coding assistant that made turning my ideas into reality a breeze. Using Python and Pygame, this game puts you in control of an airship, catching falling objects, dodging deadly ones, and snagging power-ups to rack up points. In this post, I’ll dive into how Amazon Q CLI brought this game to life, highlight the game’s core features, and share a glimpse of the development process with a screenshot of Amazon Q in action. Whether you’re a coder or a gamer, this project shows what AI can do!

Installing Amazon Q CLI on Windows via WSL

Amazon Q CLI doesn't have a native Windows version, but it can be easily installed and used through the Windows Subsystem for Linux (WSL). This method gives you access to a Linux environment directly from your Windows machine, making it straightforward to work with the CLI tools designed for Linux.

Here's a step-by-step guide to get Amazon Q CLI running on Windows using WSL with Ubuntu:

Installing Amazon Q CLI on Ubuntu (WSL)

  1. Check glibc Version Before downloading, determine which installer version you need by checking your glibc version:

ldd --version

If glibc is 2.34 or newer, use the standard version.

If glibc is older, use the musl version.

  1. Download the Appropriate Installer Choose the correct command based on your architecture and glibc version.

Standard version (glibc 2.34+)

Linux x86-64:

curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip" -o "q.zip"
Enter fullscreen mode Exit fullscreen mode

Linux ARM (aarch64):

curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-aarch64-linux.zip" -o "q.zip"
Musl version (glibc < 2.34)
Enter fullscreen mode Exit fullscreen mode

Linux x86-64 (musl):

curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux-musl.zip" -o "q.zip"
Enter fullscreen mode Exit fullscreen mode

Linux ARM (aarch64, musl):

curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-aarch64-linux-musl.zip" -o "q.zip"
Enter fullscreen mode Exit fullscreen mode
  1. Install Amazon Q CLI Unzip the package and run the installer:

unzip q.zip
./q/install.sh

By default, Amazon Q CLI will be installed to: ~/.local/bin.

  1. Configure AWS Credentials (Optional) If you haven't already, configure your AWS credentials within the WSL environment: aws configure

This step is necessary for Amazon Q CLI to interact with your AWS account.

  1. Configure your AWS credentials within the WSL environment using the following command:

q login

q login

For additional options and detailed instructions for other platforms, refer to the official Amazon Q CLI Installation Documentation.

Amazon Q CLI: My Coding Sidekick

Amazon Q CLI is an AI tool that generates code, suggests features, and debugs issues through simple command-line prompts. I used it to build Falling Stars Game from the ground up, starting with basic concepts and iterating to a polished product. With prompts like “create a Pygame game where a player catches falling objects,” Amazon Q churned out working code, suggested additions like power-ups and levels, and helped fix bugs when things went off track. Its ability to understand my intent and provide Pygame-specific solutions saved hours of work.

The CLI’s conversational style let me refine the game step-by-step, from spawning objects to adding sound effects. For example, when collision detection was glitchy, a prompt like “fix object collision in Pygame” got me a corrected algorithm. The screenshot below (to be added) captures Amazon Q in action, showing how it shaped the game’s core mechanics.

Amazon Q CLI in Action

Initially, I gave a simple prompt to Amazon Q, like:

'Can you guide me to create a game using Pygame?'

It gave me the Python code for the game in a single file, so I asked to structure it like a proper project. As a result, Amazon Q provided me with the following file structure for my game project:

Amazon Q suggested this clean setup for the game:

falling_stars_game/
├── README.md
├── assets
│   ├── images
│   │   ├── background.jpg
│   │   ├── dangerous_object.png
│   │   ├── normal_object.png
│   │   ├── player.png
│   │   ├── powerup_life.png
│   │   ├── powerup_slower.png
│   │   ├── powerup_wider.png
│   │   └── special_object.png
│   └── sounds
│       ├── background.mp3
│       ├── catch.wav
│       ├── gameover.wav
│       └── powerup.wav
├── run_game.py
└── src
    └── game.py
Enter fullscreen mode Exit fullscreen mode

assets/: Holds images and sounds.
src/game.py: Core game logic.
run_game.py: Launches the game.
README.md: Guides setup and gameplay.

Game Overview

Falling Stars Game is a fast-paced arcade challenge set on an 800x600 screen. You control a movable brick wall to catch falling stars for points while avoiding red ones that end the game. Power-ups occasionally drop to give you an edge. With custom graphics, sound effects, and toggleable music, the game feels polished and immersive. A five-level difficulty system keeps the gameplay engaging. Every component — from the game loop to the user interface — was built using the Amazon Q CLI.

Key Features

Objects in the game:

  • ⭐ Purple-background star: +1 point, falls at a steady speed.
  • ⭐ White-background star (Special): +5 points, faster and harder to catch.
  • 🔥 Red fire object: Game over if caught — avoid at all costs.
  • ❤️ Heart: Grants an extra life.
  • ⏱️ Green timer clock: Temporarily slows down all falling objects, making them easier to catch.
  • 🔷 Blue wider object: Temporarily increases the width of your brick wall, making catching objects easier.

Objects in the game

Levels:

  • Five levels, advancing every 50 points. Higher levels mean faster objects, quicker spawns, and more green objects.

Objects in the game

Visuals & Audio:

  • Custom .png images for the brick wal, objects, and power-ups.

  • Sounds for catches (catch.wav), power-ups (powerup.wav), and game over (gameover.wav).

  • Background music (background.mp3), toggleable with the M key.

  • UI shows score, level, lives (as icons), and power-up timers.

Controls:

  • Left/Right Arrows: Move the airship.
  • P: Pause/unpause.
  • M: Toggle music.
  • R: Restart after game over.

Objects in the game

Code Highlights

Here's the complete game.py implementation:

import pygame
import random
import sys
import os
from pathlib import Path

# Get the base directory
BASE_DIR = Path(__file__).resolve().parent.parent
ASSETS_DIR = BASE_DIR / "assets"
IMAGES_DIR = ASSETS_DIR / "images"
SOUNDS_DIR = ASSETS_DIR / "sounds"

# Initialize Pygame
pygame.init()
pygame.mixer.init()  # Initialize sound mixer

# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
PURPLE = (128, 0, 128)

# Create the screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Falling Stars Game")

# Load images
try:
    background_img = pygame.image.load(IMAGES_DIR / "background.jpg").convert()
    background_img = pygame.transform.scale(background_img, (SCREEN_WIDTH, SCREEN_HEIGHT))

    # Load normal player image
    player_img = pygame.image.load(IMAGES_DIR / "player.png").convert_alpha()
    # Scale the player image to the correct size for the game
    player_img = pygame.transform.scale(player_img, (100, 80))  # Increased height to show more of the image

    normal_obj_img = pygame.image.load(IMAGES_DIR / "normal_object.png").convert_alpha()
    normal_obj_img = pygame.transform.scale(normal_obj_img, (50, 50))

    special_obj_img = pygame.image.load(IMAGES_DIR / "special_object.png").convert_alpha()
    special_obj_img = pygame.transform.scale(special_obj_img, (50, 50))

    dangerous_obj_img = pygame.image.load(IMAGES_DIR / "dangerous_object.png").convert_alpha()
    dangerous_obj_img = pygame.transform.scale(dangerous_obj_img, (50, 50))

    powerup_wider_img = pygame.image.load(IMAGES_DIR / "powerup_wider.png").convert_alpha()
    powerup_wider_img = pygame.transform.scale(powerup_wider_img, (50, 50))

    powerup_slower_img = pygame.image.load(IMAGES_DIR / "powerup_slower.png").convert_alpha()
    # Make the slower powerup larger (60x60 pixels)
    powerup_slower_img = pygame.transform.scale(powerup_slower_img, (60, 60))

    powerup_life_img = pygame.image.load(IMAGES_DIR / "powerup_life.png").convert_alpha()
    powerup_life_img = pygame.transform.scale(powerup_life_img, (50, 50))

    # Dictionary to store images for easy access
    images = {
        'player': player_img,
        'normal': normal_obj_img,
        'special': special_obj_img,
        'dangerous': dangerous_obj_img,
        'wider': powerup_wider_img,
        'slower': powerup_slower_img,
        'extra_life': powerup_life_img
    }

except Exception as e:
    print(f"Error loading images: {e}")
    # We'll use colored rectangles if images fail to load
    images = {}

# Load sounds
try:
    catch_sound = pygame.mixer.Sound(SOUNDS_DIR / "catch.wav")
    powerup_sound = pygame.mixer.Sound(SOUNDS_DIR / "powerup.wav")
    game_over_sound = pygame.mixer.Sound(SOUNDS_DIR / "gameover.wav")

    # Set volume
    catch_sound.set_volume(0.5)
    powerup_sound.set_volume(0.7)
    game_over_sound.set_volume(0.8)

    # Try to load background music
    try:
        pygame.mixer.music.load(SOUNDS_DIR / "background.mp3")
        pygame.mixer.music.set_volume(0.3)
        pygame.mixer.music.play(-1)  # -1 means loop indefinitely
        music_playing = True
    except:
        print("Background music not found")
        music_playing = False

except Exception as e:
    print(f"Error loading sounds: {e}")
    # Create dummy sound objects
    catch_sound = None
    powerup_sound = None
    game_over_sound = None
    music_playing = False

# Player settings
player_width = 100
player_height = 80  # Increased height to show more of the image
player_x = SCREEN_WIDTH // 2 - player_width // 2
player_y = SCREEN_HEIGHT - 90  # Moved player up a bit to accommodate taller image
player_speed = 10
player_lives = 3

# Object settings
object_size = 50
objects = []
spawn_timer = 0

# Power-up settings
powerups = []
powerup_timer = 0
powerup_delay = 300  # Frames between powerup spawns
active_powerups = {}
wider_level = 1  # Track the current width level (1=normal, 2=double, 3=triple)

# Game settings
score = 0
level = 1
font = pygame.font.Font(None, 36)
small_font = pygame.font.Font(None, 24)
clock = pygame.time.Clock()
game_over = False
paused = False
music_playing = music_playing if 'music_playing' in locals() else False

# Level settings
level_settings = {
    1: {"spawn_delay": 60, "object_speed": 3, "special_chance": 0.1},
    2: {"spawn_delay": 50, "object_speed": 4, "special_chance": 0.15},
    3: {"spawn_delay": 40, "object_speed": 5, "special_chance": 0.2},
    4: {"spawn_delay": 30, "object_speed": 6, "special_chance": 0.25},
    5: {"spawn_delay": 20, "object_speed": 7, "special_chance": 0.3}
}

# Current level settings
current_settings = level_settings[1]
spawn_delay = current_settings["spawn_delay"]
object_speed = current_settings["object_speed"]
special_chance = current_settings["special_chance"]

def draw_player(x, y):
    if 'player' in images:
        # If we have a player image, use it
        if 'wider' in active_powerups and active_powerups['wider'] > 0:
            # Draw multiple player images side by side based on wider_level
            img_width = player_img.get_width()

            # Draw the images side by side with slight overlap
            for i in range(wider_level):
                pos_x = x + (i * (img_width - 20))  # Overlap by 20 pixels
                screen.blit(images['player'], (pos_x, y))
        else:
            # Use normal airship image
            screen.blit(images['player'], (x, y))
    else:
        # Fallback to a rectangle
        if 'wider' in active_powerups and active_powerups['wider'] > 0:
            pygame.draw.rect(screen, BLUE, [x, y, player_width, player_height])
        else:
            pygame.draw.rect(screen, BLUE, [x, y, 100, player_height])

    # Draw player lives
    for i in range(player_lives):
        if 'extra_life' in images:
            screen.blit(pygame.transform.scale(images['extra_life'], (20, 20)), (10 + i * 30, 40))
        else:
            pygame.draw.rect(screen, RED, [10 + i * 30, 40, 20, 20])

def create_object():
    x = random.randint(0, SCREEN_WIDTH - object_size)
    obj_type = random.choices(
        ['normal', 'special', 'dangerous'], 
        weights=[0.7, special_chance, 0.3-special_chance], 
        k=1
    )[0]

    speed_variation = random.uniform(0.8, 1.2)

    if obj_type == 'normal':
        color = RED
        points = 1
        speed = object_speed * speed_variation
    elif obj_type == 'special':
        color = GREEN
        points = 5
        speed = object_speed * 1.2 * speed_variation
    else:  # dangerous
        color = PURPLE
        points = 0  # Points don't matter as game will end if caught
        speed = object_speed * 0.9 * speed_variation

    return {'x': x, 'y': 0, 'type': obj_type, 'color': color, 'points': points, 'speed': speed}

def create_powerup():
    x = random.randint(0, SCREEN_WIDTH - object_size)
    powerup_type = random.choice(['wider', 'slower', 'extra_life'])

    if powerup_type == 'wider':
        color = YELLOW
        size = object_size
    elif powerup_type == 'slower':
        color = (0, 255, 255)  # Cyan
        size = 60  # Larger size for slower powerup
    else:  # extra_life
        color = (255, 105, 180)  # Pink
        size = object_size

    return {'x': x, 'y': 0, 'type': powerup_type, 'color': color, 'speed': object_speed * 0.7, 'size': size}

def draw_objects():
    for obj in objects:
        if obj['type'] in images:
            screen.blit(images[obj['type']], (obj['x'], obj['y']))
        else:
            pygame.draw.rect(screen, obj['color'], [obj['x'], obj['y'], object_size, object_size])

def draw_powerups():
    for pu in powerups:
        if pu['type'] in images:
            # Special handling for the slower powerup to make it larger
            if pu['type'] == 'slower':
                screen.blit(images[pu['type']], (pu['x'] - 5, pu['y'] - 5))  # Adjust position to center it
            else:
                screen.blit(images[pu['type']], (pu['x'], pu['y']))
        else:
            # Fallback to colored rectangles
            if pu['type'] == 'slower':
                # Draw a larger rectangle for slower powerup
                pygame.draw.rect(screen, pu['color'], [pu['x'] - 5, pu['y'] - 5, 60, 60])
                # Draw a "S" on the powerup
                text = small_font.render("S", True, BLACK)
                screen.blit(text, (pu['x'] + 25, pu['y'] + 20))
            else:
                pygame.draw.rect(screen, pu['color'], [pu['x'], pu['y'], object_size, object_size])
                # Draw a "P" on the powerup
                text = small_font.render("P", True, BLACK)
                screen.blit(text, (pu['x'] + 20, pu['y'] + 15))

def show_score():
    score_text = font.render(f"Score: {score}", True, WHITE)
    level_text = font.render(f"Level: {level}", True, WHITE)
    lives_text = font.render(f"Lives: {player_lives}", True, WHITE)
    screen.blit(score_text, (10, 10))
    screen.blit(level_text, (SCREEN_WIDTH - 120, 10))
    screen.blit(lives_text, (10, 50))

    # Show music status
    music_text = small_font.render("Music: " + ("ON" if music_playing else "OFF") + " (M to toggle)", True, WHITE)
    screen.blit(music_text, (SCREEN_WIDTH - 220, 50))

    # Show active powerups with icons
    y_offset = 90
    for powerup_type, time_left in active_powerups.items():
        if time_left > 0:
            if powerup_type in images:
                # Draw a small version of the powerup icon
                small_icon = pygame.transform.scale(images[powerup_type], (20, 20))
                screen.blit(small_icon, (10, y_offset))

                # For wider powerup, show the current level
                if powerup_type == 'wider':
                    powerup_text = small_font.render(f"{powerup_type.capitalize()} (x{wider_level}): {time_left//60}s", True, YELLOW)
                else:
                    powerup_text = small_font.render(f"{powerup_type.capitalize()}: {time_left//60}s", True, YELLOW)

                screen.blit(powerup_text, (35, y_offset + 2))  # Align text with icon
            else:
                # Fallback if image not available
                if powerup_type == 'wider':
                    powerup_text = small_font.render(f"{powerup_type.capitalize()} (x{wider_level}): {time_left//60}s", True, YELLOW)
                else:
                    powerup_text = small_font.render(f"{powerup_type.capitalize()}: {time_left//60}s", True, YELLOW)
                screen.blit(powerup_text, (10, y_offset))
            y_offset += 25

def check_level_up():
    global level, current_settings, spawn_delay, object_speed, special_chance

    # Level up every 50 points
    new_level = min(5, 1 + score // 50)

    if new_level > level:
        level = new_level
        current_settings = level_settings[level]
        spawn_delay = current_settings["spawn_delay"]
        object_speed = current_settings["object_speed"]
        special_chance = current_settings["special_chance"]

        # Show level up message
        return True
    return False

def apply_powerup(powerup_type):
    global player_width, object_speed, player_lives, wider_level

    if powerup_type == 'wider':
        # Increase the wider level (max 3)
        wider_level = min(3, wider_level + 1)

        # Set width based on wider_level
        if wider_level == 2:
            player_width = 160  # Enough for 2 images with overlap
        elif wider_level == 3:
            player_width = 220  # Enough for 3 images with overlap

        active_powerups['wider'] = 600  # 10 seconds (60 fps * 10)
    elif powerup_type == 'slower':
        object_speed *= 0.7
        active_powerups['slower'] = 300  # 5 seconds
    elif powerup_type == 'extra_life':
        player_lives = min(5, player_lives + 1)
    # Play powerup sound
    if powerup_sound:
        powerup_sound.play()

def update_powerups():
    global player_width, object_speed, wider_level

    for powerup_type in list(active_powerups.keys()):
        active_powerups[powerup_type] -= 1

        if active_powerups[powerup_type] <= 0:
            # Powerup expired
            if powerup_type == 'wider':
                player_width = 100  # Reset to default airship width
                wider_level = 1     # Reset wider level
            elif powerup_type == 'slower':
                object_speed = current_settings["object_speed"]  # Reset to level default

            del active_powerups[powerup_type]

def show_message(message, y_offset=0):
    text = font.render(message, True, WHITE)
    text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + y_offset))
    # Draw a semi-transparent background for the text
    s = pygame.Surface((text_rect.width + 20, text_rect.height + 20))
    s.set_alpha(200)
    s.fill(BLACK)
    screen.blit(s, (text_rect.x - 10, text_rect.y - 10))
    screen.blit(text, text_rect)

# Main game loop
running = True
level_up_message_timer = 0
show_level_up = False

while running:
    # Handle events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_p:
                paused = not paused
            elif event.key == pygame.K_m:
                # Toggle music on/off
                if music_playing:
                    pygame.mixer.music.pause()
                    music_playing = False
                else:
                    try:
                        pygame.mixer.music.unpause()
                        music_playing = True
                    except:
                        # If music wasn't loaded or was stopped (not paused)
                        try:
                            pygame.mixer.music.play(-1)
                            music_playing = True
                        except:
                            pass
            elif event.key == pygame.K_r and game_over:
                # Reset game
                game_over = False
                score = 0
                level = 1
                player_lives = 3
                player_width = 100
                wider_level = 1  # Reset wider level
                objects = []
                powerups = []
                active_powerups = {}
                player_x = SCREEN_WIDTH // 2 - player_width // 2
                current_settings = level_settings[1]
                spawn_delay = current_settings["spawn_delay"]
                object_speed = current_settings["object_speed"]
                special_chance = current_settings["special_chance"]

                # Restart background music
                try:
                    pygame.mixer.music.play(-1)
                except:
                    pass

    # Draw background
    if 'background' in locals() and background_img:
        screen.blit(background_img, (0, 0))
    else:
        # Fallback to a gradient background
        for y in range(0, SCREEN_HEIGHT, 2):
            color_value = 50 + (y / SCREEN_HEIGHT * 50)
            pygame.draw.rect(screen, (50, 50, color_value), [0, y, SCREEN_WIDTH, 2])

    if not game_over and not paused:
        # Move player
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and player_x > 0:
            player_x -= player_speed
        if keys[pygame.K_RIGHT] and player_x < SCREEN_WIDTH - player_width:
            player_x += player_speed

        # Update powerups
        update_powerups()

        # Spawn new objects
        spawn_timer += 1
        if spawn_timer >= spawn_delay:
            objects.append(create_object())
            spawn_timer = 0

        # Spawn powerups
        powerup_timer += 1
        if powerup_timer >= powerup_delay:
            powerups.append(create_powerup())
            powerup_timer = 0

        # Move objects
        for obj in objects[:]:
            obj['y'] += obj['speed']

            # Check if object is caught
            if (obj['y'] + object_size > player_y and 
                obj['y'] < player_y + player_height and
                obj['x'] + object_size > player_x and
                obj['x'] < player_x + player_width):
                objects.remove(obj)

                # Check if dangerous object was caught
                if obj['type'] == 'dangerous':
                    # Game over immediately if dangerous object is caught
                    game_over = True
                    if game_over_sound:
                        game_over_sound.play()
                    # Stop background music
                    pygame.mixer.music.stop()
                else:
                    # Add points for non-dangerous objects
                    score += obj['points']

                    # Play catch sound
                    if catch_sound:
                        catch_sound.play()

                    # Check for level up
                    if check_level_up():
                        show_level_up = True
                        level_up_message_timer = 180  # Show for 3 seconds

            # Check if object is missed
            elif obj['y'] > SCREEN_HEIGHT:
                objects.remove(obj)
                if obj['type'] != 'dangerous':  # Only lose lives for non-dangerous objects
                    player_lives -= 1
                    if player_lives <= 0:
                        game_over = True
                        if game_over_sound:
                            game_over_sound.play()
                        # Stop background music
                        pygame.mixer.music.stop()

        # Move powerups
        for pu in powerups[:]:
            pu['y'] += pu['speed']

            # Check if powerup is caught
            if (pu['y'] + (pu['size'] if 'size' in pu else object_size) > player_y and 
                pu['y'] < player_y + player_height and
                pu['x'] + (pu['size'] if 'size' in pu else object_size) > player_x and
                pu['x'] < player_x + player_width):
                powerups.remove(pu)
                apply_powerup(pu['type'])

            # Check if powerup is missed
            elif pu['y'] > SCREEN_HEIGHT:
                powerups.remove(pu)

    # Draw everything
    draw_player(player_x, player_y)
    draw_objects()
    draw_powerups()
    show_score()

    # Show level up message
    if show_level_up:
        level_up_message_timer -= 1
        show_message(f"Level Up! Level {level}", -50)
        if level_up_message_timer <= 0:
            show_level_up = False

    if game_over:
        show_message("Game Over! Press R to restart")
        show_message("Final Score: " + str(score), 50)

    if paused:
        show_message("PAUSED - Press P to continue")

    # Update display
    pygame.display.flip()
    clock.tick(60)

# Quit the game
pygame.quit()
sys.exit()

Enter fullscreen mode Exit fullscreen mode

Progressive Widening Power-up

The game implements a unique widening system where collecting multiple power-ups increases your paddle width in stages:

def apply_powerup(powerup_type):
    global player_width, object_speed, player_lives, wider_level
    if powerup_type == 'wider':
        wider_level = min(3, wider_level + 1)
        if wider_level == 2:
            player_width = 160
        elif wider_level == 3:
            player_width = 220
        active_powerups['wider'] = 600

Enter fullscreen mode Exit fullscreen mode

Multiple Object Types

The game features three types of falling objects with different behaviors:

def create_object():
    obj_type = random.choices(
        ['normal', 'special', 'dangerous'],
        weights=[0.7, special_chance, 0.3-special_chance],
        k=1
    )[0]

    if obj_type == 'normal':
        color = RED
        points = 1
    elif obj_type == 'special':
        color = GREEN
        points = 5
    else:  # dangerous
        color = PURPLE
        points = 0  # Game over if caught
Enter fullscreen mode Exit fullscreen mode

Level System
The game automatically progresses through 5 difficulty levels:

level_settings = {
    1: {"spawn_delay": 60, "object_speed": 3, "special_chance": 0.1},
    2: {"spawn_delay": 50, "object_speed": 4, "special_chance": 0.15},
    3: {"spawn_delay": 40, "object_speed": 5, "special_chance": 0.2},
    4: {"spawn_delay": 30, "object_speed": 6, "special_chance": 0.25},
    5: {"spawn_delay": 20, "object_speed": 7, "special_chance": 0.3}
}
Enter fullscreen mode Exit fullscreen mode

What Stands Out:

  • Assets: Loads images and sounds with fallbacks (gradient background, colored rectangles).
  • Spawning: create_object balances object types with random.choices, adjusting for level difficulty.
  • Power-Ups: apply_powerup dynamically adjusts paddle width and object speed.
  • UI: Shows lives as icons, power-up timers, and a semi-transparent message box for pause/game over.
  • Loop: Runs at 60 FPS, handling input, updates, and rendering.
  • Amazon Q structured the code cleanly, using pathlib for paths and suggesting a level_settings dictionary for easy tweaks.

Building with Amazon Q CLI

Developing the game was a smooth ride with Amazon Q CLI:

  • Starting Out: A prompt like “set up a Pygame game with a moving paddle” gave me the initial loop and player controls.
  • Adding Objects: “Add falling objects with different types” led to the create_object function, with Amazon Q suggesting weighted probabilities for balance.
  • Power-Ups: “Implement power-ups for wider paddle and extra lives” introduced the wider_level system and timers.
  • Polish: Prompts like “add background music and UI” brought in audio and a clean interface with lives displayed as icons.
  • Debugging: When power-ups didn’t reset properly, “fix power-up expiration” got me the update_powerups function. Amazon Q also caught asset loading errors early.
  • The CLI’s speed and smarts made iterating fun, though I had to be specific with prompts to get exactly what I wanted, like centering the “S” on the slower power-up.

How to Play game:

  1. clone the project repository
git clone https://github.com/rifkhan107/falling_stars_game-amazaon-q
Enter fullscreen mode Exit fullscreen mode
  1. Install pygame
pip install pygame
Enter fullscreen mode Exit fullscreen mode
  1. cd falling_stars_game
cd falling_stars_game
python3 run_game.py
Enter fullscreen mode Exit fullscreen mode

Play:

  • Use Left/Right arrows to move.
  • Catch red/green objects, dodge purple.
  • Grab power-ups for advantages.
  • P (pause), M (music toggle), R (restart).
  • Takeaways

Amazon Q CLI was a powerhouse:

  • Wins: Fast code generation, creative ideas (like the wider paddle progression), and quick fixes.
  • Hurdles: Needed clear prompts for complex features; minor manual tweaks for polish.

What’s Next?

With Amazon Q, I could:

  • Save high scores to a file.
  • Add a “shield” power-up to block purple objects.
  • Build a start menu with options.

Final Thoughts

Falling Stars Game is proof Amazon Q CLI can turn a game idea into reality fast. It’s fun to play, looks sharp, and was built with AI smarts. Add the screenshot to see Amazon Q in action, then try the game or use Q to code your own. Happy gaming and coding!

Top comments (2)

Collapse
 
fazly_fathhy profile image
Fazly Fathhy

Superb bruh♥️🙌🏻

Collapse
 
rifkhan107 profile image
JM Rifkhan AWS Community Builders

Thanks Bruhh 😁