DEV Community

Divyanshi Jain
Divyanshi Jain

Posted on

# Building "Echoes of Code": A Time-Loop Puzzle Game with Python and PyGame with #AmazonQCLI

In this technical blog, I'll walk you through creating "Echoes of Code," a 2D top-down puzzle game where you solve puzzles by collaborating with echoes of your past actions. I'll focus on the key components and concepts to keep this a quick 5-minute read.

Setting Up the Development Environment

Installing WSL on Windows

  1. Enable WSL: Open PowerShell as Administrator and run:([DEV Community][1])
   wsl --install
Enter fullscreen mode Exit fullscreen mode
  1. Restart your computer and complete the Linux distribution setup.

Setting Up Python and PyGame

# Install Python and dependencies
sudo apt install python3 python3-pip python3-dev -y
sudo apt install python3-pygame -y

# Install PyGame
pip3 install pygame
Enter fullscreen mode Exit fullscreen mode

Installing Amazon Q CLI

# Install AWS CLI first
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Configure AWS
aws configure

# Install Amazon Q CLI
curl -Lo "/tmp/q-cli.deb" "https://d3l58w4xvhud7h.cloudfront.net/linux/amd64/latest/q-cli.deb"
sudo dpkg -i /tmp/q-cli.deb

# Login
q login
Enter fullscreen mode Exit fullscreen mode

Game Design and Concept

"Echoes of Code" features a software engineer trapped in a glitched digital world. Every 10 seconds, time resets, and your past actions are replayed by ghost clones called "echoes." You must use these echoes strategically to solve puzzles.

Implementation Highlights

Time Loop System

The core of the game is the TimerManager class that handles the time loop mechanics:

class TimerManager:
    def __init__(self, loop_duration: float, max_loops: int):
        self.loop_duration = loop_duration
        self.max_loops = max_loops
        self.start_time = time.time()
        self.current_loop = 1

    def get_loop_time(self) -> float:
        """Get time elapsed in the current loop"""
        return self.get_elapsed_time() % self.loop_duration

    def should_reset_loop(self) -> bool:
        return self.get_loop_time() >= self.loop_duration

    def reset_loop(self):
        self.current_loop += 1
Enter fullscreen mode Exit fullscreen mode

Action Recording and Replay

The Player class records actions, while the Echo class replays them:

# In Player class
def record_action(self, action_type: str, time_stamp: float):
    self.actions.append(Action(action_type, (self.x, self.y), time_stamp, self.current_direction))

# In Echo class
def update(self, current_time: float, game_objects: List):
    if self.current_action_index < len(self.actions):
        next_action = self.actions[self.current_action_index]

        if current_time >= next_action.time_stamp:
            if next_action.action_type == "move":
                self.x, self.y = next_action.position
                self.current_direction = next_action.direction
            elif next_action.action_type == "interact":
                self.interact_with_objects(game_objects)

            self.current_action_index += 1
Enter fullscreen mode Exit fullscreen mode

Interactive Game Objects

The game includes various interactive elements:

# Example of the PressurePlate class
class PressurePlate:
    def update(self, players: List[Player]):
        self.is_active = False
        plate_rect = pygame.Rect(self.x, self.y, self.width, self.height)

        for player in players:
            player_rect = pygame.Rect(player.x, player.y, player.width, player.height)
            if plate_rect.colliderect(player_rect):
                self.is_active = True
                break
Enter fullscreen mode Exit fullscreen mode

Loop Reset Logic

When a loop resets, we create a new echo and reset the player:

def reset_loop(self):
    # Create a new echo from the current player
    echo_colors = [PURPLE, YELLOW, CYAN, RED, GREEN]
    echo_color = echo_colors[min(len(self.echoes), len(echo_colors) - 1)]
    self.echoes.append(Echo(self.player, echo_color, self.timer_manager.current_loop))

    # Reset player position
    current_level = self.level_manager.get_current_level()
    self.player.x, self.player.y = current_level.player_start

    # Clear player actions for the new loop
    self.player.actions = []

    # Increment loop counter
    self.timer_manager.reset_loop()
Enter fullscreen mode Exit fullscreen mode

Level Design

Levels are created using the Level class, which manages all game objects:

def create_levels(self):
    # Level 1: Simple pressure plate and gate
    level1 = Level(1, (100, 300), 3)  # Player starts at (100, 300), 3 loops max

    # Add walls (border)
    level1.add_wall(0, 0, SCREEN_WIDTH, 20)  # Top
    level1.add_wall(0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, 20)  # Bottom

    # Add pressure plate
    level1.add_pressure_plate(300, 200, 1)  # Controls gate with ID 1

    # Add gate
    level1.add_gate(400, 400, 20, 100, 1)  # Gate with ID 1

    # Add exit
    level1.set_exit(700, 300)
Enter fullscreen mode Exit fullscreen mode

Main Game Loop

The Game class ties everything together:

def run(self):
    running = True

    while running:
        running = self.handle_events()
        self.update()
        self.draw()
        self.clock.tick(FPS)
Enter fullscreen mode Exit fullscreen mode

Development Challenges

  1. Echo Replay Timing: Ensuring echoes replayed actions at the exact same time they were recorded.

  2. Collision Detection: Making sure players and echoes interacted correctly with game objects.

  3. Loop Reset Logic: Managing the state transition when a loop resets, especially ensuring all objects reset properly.

Future Enhancements

The game can be expanded with:

  1. Additional Levels: More complex puzzles requiring multiple echoes to solve.

  2. Mini Terminal: Implementing a system where players can write simple logic commands.

  3. Enemies: Adding entities that track and interact with echoes.

  4. Visual Timeline: Creating a replay system to show all actions at the end of each level.

Running the Game

To run the game:

cd ~/echoes_of_code
python3 echoes_of_code.py
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building "Echoes of Code" demonstrates how a unique game mechanic like time loops can create engaging puzzle gameplay. The modular code structure makes it easy to expand with new levels and features. By leveraging WSL for development and Amazon Q CLI for assistance, I was able to streamline the development process and focus on implementing the core game mechanics.

If you're interested in time-based puzzle games or want to explore PyGame development, I encourage you to try out "Echoes of Code" and even extend it with your own ideas!

Top comments (0)