DEV Community

🎮 From Prompt to Playable: Building Synaptic Weave with Amazon Q CLI

Can you build an innovative game with no manual coding, no images, and only prompts?

I said yes. Amazon Q CLI delivered.

🚀 The Challenge

As part of the Amazon Q CLI Game Build Challenge, I set out to create something bold:

  1. Fully procedural
  2. Purely prompt-engineered
  3. Code-only visuals (no images or sprites)

The goal? Build a game that feels intelligent, abstract, and alive.

My Prompt:

You are Amazon Q CLI, an AI game-engineer assistant. I want to build an unprecedented, procedural-only, shape-and-text-driven PyGame project called “Synaptic Weave.” In this game, the player traverses a dynamically growing neural-network maze whose corridors and obstacles evolve in real-time based on the player’s own path history.
Enter fullscreen mode Exit fullscreen mode

🧠 Game Concept: What is Synaptic Weave?

Synaptic Weave is a procedurally expanding neural maze game where:

The maze grows based on your movement

  • Your trails become permanent paths
  • Hazards (pulses) emerge from firing nodes
  • Logic puzzles test your path planning skills

Game Terms:

Depth:

Number of unique nodes you've visited

Branching Factor:

Average number of connections per node

Active Pulses:

Number of expanding hazard waves present on the screen

🏃‍♂️ Iteration 1: The Foundation

Amazon Q CLI gave me a complete, working PyGame project:

main.py, graph_maze.py, player.py, hazards.py, ui.py

Procedural maze built using NetworkX

Smooth player movement and fading trails

Initial hazard system and scoring logic

I ran python src/main.py and within seconds, I was navigating a neural web with glowing nodes and path-based evolution. No errors. Fully playable.

🔄 Iteration 2: Making It Understandable & Alive

I asked:

"Make the game more interactive. Explain game mechanics clearly. Add visual polish."

Q CLI responded with:

  • A tutorial system at the start
  • H key for help screen
  • ESC/P to pause
  • Animated milestone popups, fading trails, and pulsing glow effects
  • Interactive HUD with tooltips
  • Background particles and more visual feedback

Now the game felt alive — not just running logic, but immersive play.

🔥 Iteration 3: Cranking Up the Challenge

I told Q:

"Make it more competitive. I want to think, plan, and sweat!"

Q CLI leveled up the experience:

⚔️ Major Enhancements:

Hazards Every 4 Nodes: Fire pulses. Every 8 nodes? Logic gate puzzles.

Pulse Overhaul: Faster, variable-speed hazards that spawn immediately and randomly after 20 nodes.

  • Progressive Difficulty:

At 25, 50, 100 nodes, pulses get 30% faster

Pulse frequency and hazard rates scale aggressively

Visual Warnings: On hazard creation and difficulty jumps

  • Strategic Depth:

Risk vs. reward exploration

Trail hardening becomes key to survival

You must plan movement and solve timed hazards

**
I wanted strategy. I got it. Each run now feels like a tactical deep-dive through chaos.**

Some of the code snippets generated by Amazon Q cli (hazards.py)

import random
import math
import time
import pygame

# Constants
PULSE_COLOR = (255, 100, 100, 180)
PULSE_BORDER_COLOR = (255, 50, 50)
PULSE_SPEED = 80  # Pixels per second
PULSE_MAX_RADIUS = 150
FIRING_NODE_COLOR = (255, 100, 100)
LOGIC_GATE_COLOR = (100, 255, 100)
LOGIC_GATE_ACTIVE_COLOR = (200, 255, 200)


class Pulse:
    """Expanding circular pulse hazard."""

    def __init__(self, center, start_time):
        """Initialize a new pulse.

        Args:
            center (tuple): (x, y) center position of the pulse
            start_time (float): Time when the pulse started
        """
        self.center = center
        self.start_time = start_time
        self.radius = 0
        self.active = True
        self.speed = PULSE_SPEED * (0.8 + random.random() * 0.4)  # Randomize speed slightly

    def update(self, current_time):
        """Update the pulse radius based on elapsed time.

        Args:
            current_time (float): Current game time

        Returns:
            bool: True if the pulse is still active, False if it should be removed
        """
        elapsed = current_time - self.start_time
        self.radius = elapsed * self.speed

        # Deactivate if radius exceeds maximum
        if self.radius > PULSE_MAX_RADIUS:
            self.active = False

        return self.active

    def check_collision(self, player_pos, player_radius):
        """Check if the pulse collides with the player.

        Args:
            player_pos (tuple): (x, y) player position
            player_radius (float): Player collision radius

        Returns:
            bool: True if collision detected, False otherwise
        """
        # Calculate distance between pulse center and player
        dx = player_pos[0] - self.center[0]
        dy = player_pos[1] - self.center[1]
        distance = math.sqrt(dx*dx + dy*dy)

        # Check if player is within the pulse ring (with some thickness)
        pulse_thickness = 10
        min_distance = self.radius - pulse_thickness - player_radius
        max_distance = self.radius + pulse_thickness + player_radius

        return min_distance <= distance <= max_distance and self.active

    def draw(self, surface):
        """Draw the pulse on the given surface.

        Args:
            surface: Pygame surface to draw on
        """
        # Create a surface for the semi-transparent pulse
        pulse_surface = pygame.Surface((surface.get_width(), surface.get_height()), pygame.SRCALPHA)

        # Draw the pulse as a semi-transparent circle
        pygame.draw.circle(
            pulse_surface,
            PULSE_COLOR,
            (int(self.center[0]), int(self.center[1])),
            int(self.radius),
            10  # Width of the pulse ring
        )

        # Draw the border of the pulse
        pygame.draw.circle(
            surface,
            PULSE_BORDER_COLOR,
            (int(self.center[0]), int(self.center[1])),
            int(self.radius),
            2  # Width of the border
        )

        # Blit the pulse surface onto the main surface
        surface.blit(pulse_surface, (0, 0))


class FiringNode:
    """Node that periodically emits expanding pulses."""

    def __init__(self, node_id, position, maze):
        """Initialize a firing node.

        Args:
            node_id (int): ID of the node in the maze
            position (tuple): (x, y) position of the node
            maze: Reference to the GraphMaze instance
        """
        self.node_id = node_id
        self.position = position
        self.maze = maze
        self.pulses = []
        self.last_fire_time = time.time()
        self.fire_interval = random.uniform(2.0, 4.0)  # Faster firing rate (was 3.0-6.0)

        # Immediately fire a pulse when created
        self.pulses.append(Pulse(self.position, self.last_fire_time))

    def update(self, current_time):
        """Update the firing node and its pulses.

        Args:
            current_time (float): Current game time
        """
        # Check if it's time to fire a new pulse
        if current_time - self.last_fire_time > self.fire_interval:
            self.pulses.append(Pulse(self.position, current_time))
            self.last_fire_time = current_time
            self.fire_interval = random.uniform(3.0, 6.0)  # Randomize next interval

        # Update existing pulses and remove inactive ones
        self.pulses = [pulse for pulse in self.pulses if pulse.update(current_time)]

    def check_collision(self, player_pos, player_radius):
        """Check if any pulse from this node collides with the player.

        Args:
            player_pos (tuple): (x, y) player position
            player_radius (float): Player collision radius

        Returns:
            bool: True if collision detected, False otherwise
        """
        for pulse in self.pulses:
            if pulse.check_collision(player_pos, player_radius):
                return True
        return False

    def draw(self, surface):
        """Draw the firing node and its pulses.

        Args:
            surface: Pygame surface to draw on
        """
        # Draw each pulse
        for pulse in self.pulses:
            pulse.draw(surface)


class LogicGate:
    """Logic gate puzzle that requires a sequence to solve."""

    def __init__(self, node_id, position):
        """Initialize a logic gate puzzle.

        Args:
            node_id (int): ID of the node in the maze
            position (tuple): (x, y) position of the node
        """
        self.node_id = node_id
        self.position = position
        self.solved = False
        self.active = False
        self.sequence = self._generate_sequence()
        self.player_sequence = []
        self.interaction_radius = 50  # Distance at which player can interact

    def _generate_sequence(self):
        """Generate a random sequence for the puzzle.

        Returns:
            list: List of keys in the sequence
        """
        keys = [pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4]
        sequence_length = random.randint(3, 5)
        return [random.choice(keys) for _ in range(sequence_length)]

    def check_interaction(self, player_pos):
        """Check if the player is close enough to interact.

        Args:
            player_pos (tuple): (x, y) player position

        Returns:
            bool: True if player can interact, False otherwise
        """
        dx = player_pos[0] - self.position[0]
        dy = player_pos[1] - self.position[1]
        distance = math.sqrt(dx*dx + dy*dy)

        return distance <= self.interaction_radius

    def process_key_input(self, key):
        """Process key input for the puzzle sequence.

        Args:
            key (int): Pygame key constant

        Returns:
            bool: True if the puzzle is solved, False otherwise
        """
        if not self.active or self.solved:
            return False

        # Add key to player sequence
        self.player_sequence.append(key)

        # Check if the sequence matches so far
        sequence_length = len(self.player_sequence)
        if self.player_sequence != self.sequence[:sequence_length]:
            # Reset on mistake
            self.player_sequence = []
            return False

        # Check if complete sequence entered
        if len(self.player_sequence) == len(self.sequence):
            self.solved = True
            return True

        return False

    def draw(self, surface):
        """Draw the logic gate and its state.

        Args:
            surface: Pygame surface to draw on
        """
        # Draw the logic gate node
        color = LOGIC_GATE_ACTIVE_COLOR if self.active else LOGIC_GATE_COLOR
        pygame.draw.circle(
            surface,
            color,
            (int(self.position[0]), int(self.position[1])),
            20  # Slightly larger than regular nodes
        )

        # If active, draw the sequence UI
        if self.active and not self.solved:
            font = pygame.font.SysFont(None, 24)

            # Draw sequence prompt
            prompt = "Enter sequence: " + "".join([str(i+1) for i in range(len(self.sequence))])
            text = font.render(prompt, True, (255, 255, 255))
            surface.blit(text, (self.position[0] - text.get_width() // 2, self.position[1] - 50))

            # Draw player progress
            progress = "".join([str((key - pygame.K_0)) for key in self.player_sequence])
            progress_text = font.render(progress, True, (255, 255, 255))
            surface.blit(progress_text, (self.position[0] - progress_text.get_width() // 2, self.position[1] - 25))

        # If solved, show completion message
        if self.solved:
            font = pygame.font.SysFont(None, 24)
            text = font.render("Gate Unlocked!", True, (255, 255, 255))
            surface.blit(text, (self.position[0] - text.get_width() // 2, self.position[1] - 30))


class HazardManager:
    """Manages all hazards in the game."""

    def __init__(self, maze):
        """Initialize the hazard manager.

        Args:
            maze: Reference to the GraphMaze instance
        """
        self.maze = maze
        self.firing_nodes = {}  # Map of node_id to FiringNode
        self.logic_gates = {}   # Map of node_id to LogicGate
        self.nodes_visited = 0
        self.pulse_frequency = 0.1  # Probability of a new node becoming a firing node
        self.gate_frequency = 0.1   # Probability of a new node becoming a logic gate

    def update(self, dt):
        """Update all hazards.

        Args:
            dt (float): Time elapsed since last update

        Returns:
            bool: True if a new pulse was created
        """
        current_time = time.time()
        pulse_created = False

        # Update firing nodes
        for node in self.firing_nodes.values():
            old_pulse_count = len(node.pulses)
            node.update(current_time)
            if len(node.pulses) > old_pulse_count:
                pulse_created = True

        return pulse_created

    def check_player_collision(self, player):
        """Check if the player collides with any hazard.

        Args:
            player: Player instance

        Returns:
            bool: True if collision detected, False otherwise
        """
        player_pos = player.position
        player_radius = 10  # Player collision radius

        # Check collision with each firing node's pulses
        for node in self.firing_nodes.values():
            if node.check_collision(player_pos, player_radius):
                return True

        return False

    def check_logic_gate_interaction(self, player):
        """Check if the player can interact with a logic gate.

        Args:
            player: Player instance
        """
        # Deactivate all gates first
        for gate in self.logic_gates.values():
            gate.active = False

        # Check if player is near any gate
        for gate in self.logic_gates.values():
            if gate.check_interaction(player.position):
                gate.active = True
                break

    def process_key_input(self, key):
        """Process key input for active logic gates.

        Args:
            key (int): Pygame key constant
        """
        for gate in self.logic_gates.values():
            if gate.active:
                gate.process_key_input(key)

    def add_node(self, node_id, position):
        """Consider adding a hazard at the given node.

        Args:
            node_id (int): ID of the node in the maze
            position (tuple): (x, y) position of the node
        """
        self.nodes_visited += 1

        # Every 8 nodes, add a logic gate
        if self.nodes_visited % 8 == 0:
            self.logic_gates[node_id] = LogicGate(node_id, position)
        # Every 4 nodes, add a firing node (increased frequency)
        elif self.nodes_visited % 4 == 0:
            self.firing_nodes[node_id] = FiringNode(node_id, position, self.maze)

        # After reaching 20 nodes, occasionally add random firing nodes
        elif self.nodes_visited > 20 and random.random() < self.pulse_frequency:
            self.firing_nodes[node_id] = FiringNode(node_id, position, self.maze)

    def increase_difficulty(self, milestone):
        """Increase difficulty parameters based on milestone reached.

        Args:
            milestone (int): Current milestone level
        """
        self.pulse_frequency = 0.15 + milestone * 0.1  # Increased base frequency
        # Increase pulse speed based on milestone
        global PULSE_SPEED
        PULSE_SPEED = 100 + milestone * 30  # Faster pulses

        # Make existing firing nodes fire more frequently
        for node in self.firing_nodes.values():
            node.fire_interval = max(1.0, node.fire_interval * 0.7)  # Reduce interval by 30%

    def get_active_pulses_count(self):
        """Get the total number of active pulses.

        Returns:
            int: Count of active pulses
        """
        count = 0
        for node in self.firing_nodes.values():
            count += len(node.pulses)
        return count

    def draw(self, surface):
        """Draw all hazards.

        Args:
            surface: Pygame surface to draw on
        """
        # Draw firing nodes and their pulses
        for node in self.firing_nodes.values():
            node.draw(surface)

        # Draw logic gates
        for gate in self.logic_gates.values():
            gate.draw(surface)
Enter fullscreen mode Exit fullscreen mode

📊 Tech Stack

Tool

  • Amazon Q CLI

Prompt-powered code generation

  • PyGame

Game rendering and interaction engine

  • NetworkX

Graph generation for maze logic

  • Python

Core game logic

🧵 Reflections: Building with Prompts

This project flipped traditional dev on its head:

🚀 Rapid ideation to playable game in minutes

🎨 Focus on gameplay, not boilerplate

🤖 AI as a coding co-pilot that explains, iterates, and surprises you

Image description

📲 Try It Yourself

Want to build your own game using Amazon Q CLI?

🔗 Links

GitHub Repository - Synaptic Weave

Warp.dev image

Warp is the highest-rated coding agent—proven by benchmarks.

Warp outperforms every other coding agent on the market, and gives you full control over which model you use. Get started now for free, or upgrade and unlock 2.5x AI credits on Warp's paid plans.

Download Warp

Top comments (0)

Best Practices for Running  Container WordPress on AWS (ECS, EFS, RDS, ELB) using CDK cover image

Best Practices for Running Container WordPress on AWS (ECS, EFS, RDS, ELB) using CDK

This post discusses the process of migrating a growing WordPress eShop business to AWS using AWS CDK for an easily scalable, high availability architecture. The detailed structure encompasses several pillars: Compute, Storage, Database, Cache, CDN, DNS, Security, and Backup.

Read full post

👋 Kindness is contagious

Dive into this thoughtful piece, beloved in the supportive DEV Community. Coders of every background are invited to share and elevate our collective know-how.

A sincere "thank you" can brighten someone's day—leave your appreciation below!

On DEV, sharing knowledge smooths our journey and tightens our community bonds. Enjoyed this? A quick thank you to the author is hugely appreciated.

Okay