Create a Simple Maze Game with Python

Hello aspiring coders and game enthusiasts! Have you ever wanted to create your own game but thought it was too complicated? Well, think again! Today, we’re going to embark on a fun journey to build a simple text-based maze game using Python. It’s a fantastic project for beginners to learn about basic programming concepts like data structures, loops, and user input in an interactive way.

No fancy graphics, no complex engines – just pure Python power to navigate your player through a challenging maze right in your console! Ready to get started? Let’s dive in!

What You’ll Need

All you need for this project is:
* Python: Make sure you have Python installed on your computer (version 3.x is recommended). If not, you can download it from python.org.
* A Text Editor: Any text editor will do, like VS Code, Sublime Text, Notepad++, or even a simple Notepad.

That’s it! No special libraries or installations are required beyond Python itself.

Designing Our Maze

Before we start coding, let’s think about how we’ll represent our maze.
Imagine a maze drawn on a piece of grid paper. We can mimic this in Python using a list of lists.

  • List: In Python, a list is like a container that can hold multiple items (numbers, text, or even other lists!).
  • List of Lists (or Grid): This is a list where each item is another list. This creates a two-dimensional structure, perfect for representing a grid like our maze.

We’ll use simple characters to represent different elements of our maze:
* #: Represents a wall. You can’t move through walls.
* : Represents an open path. You can move here.
* P: Represents the player. This is where our adventurer starts!
* E: Represents the exit. The goal is to reach this spot.

Let’s look at an example of what our maze might look like in code:

maze = [
    ["#", "#", "#", "#", "#", "#", "#"],
    ["#", "P", " ", " ", " ", " ", "#"],
    ["#", " ", "#", "#", "#", " ", "#"],
    ["#", " ", "#", " ", " ", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", " ", " ", "#", "E", "#"],
    ["#", "#", "#", "#", "#", "#", "#"]
]

In this example:
* The first inner list ["#", "#", "#", "#", "#", "#", "#"] represents the top row of the maze.
* The second inner list ["#", "P", " ", " ", " ", " ", "#"] represents the second row, where ‘P’ is our starting player position.

Step-by-Step Implementation

Let’s break down the code into manageable pieces.

Step 1: Setting Up the Maze and Player

First, we’ll define our maze structure and keep track of the player’s current position. We need to know both the row and column where the player is located.

maze = [
    ["#", "#", "#", "#", "#", "#", "#", "#", "#"],
    ["#", "P", " ", " ", " ", " ", " ", " ", "#"],
    ["#", " ", "#", "#", "#", " ", "#", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", "#", " ", "#", " ", " ", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", " ", " ", " ", " ", "#", "E", "#"],
    ["#", "#", "#", "#", "#", "#", "#", "#", "#"]
]

player_position = [1, 1] # Row 1, Column 1 (0-indexed)

def display_maze(current_maze, player_pos):
    # Create a temporary maze to display the player without changing the original map
    display_map = [row[:] for row in current_maze] # Creates a copy of the maze

    # Place the player 'P' at their current position on the display map
    display_map[player_pos[0]][player_pos[1]] = "P"

    # Print each row of the maze
    for row in display_map:
        # The .join() method concatenates all strings in an iterable (like a list)
        # into a single string, using the string it's called on as a separator.
        # Here, it joins characters with no separator, effectively printing them side-by-side.
        print("".join(row))

Explanation:
* player_position = [1, 1] means the player starts at row 1, column 1 (remember, in programming, we often start counting from 0!).
* The display_maze function takes our maze and player position. It creates a temporary copy (display_map) to place the ‘P’ character for the player without permanently altering our original maze layout. Then, it iterates through each row and prints it, joining the characters to form a clean maze visualization.

Step 2: Handling Player Movement

Now, let’s allow the player to move! We’ll need to:
1. Get input from the player (e.g., ‘w’, ‘a’, ‘s’, ‘d’ for up, left, down, right).
2. Calculate the new desired position.
3. Check if the move is valid (not a wall, not out of bounds).
4. Update the player’s position if the move is valid.

def get_player_move():
    while True: # A loop that continues indefinitely until a valid input is received
        move = input("Enter your move (w: up, a: left, s: down, d: right): ").lower()
        if move in ['w', 'a', 's', 'd']:
            return move
        else:
            print("Invalid input. Please use 'w', 'a', 's', or 'd'.")

def is_valid_move(current_maze, next_row, next_col):
    # Check if the next position is within the maze boundaries
    # len(current_maze) gives the number of rows.
    # len(current_maze[0]) gives the number of columns in the first row.
    if not (0 <= next_row < len(current_maze) and 0 <= next_col < len(current_maze[0])):
        return False # Out of bounds

    # Check if the next position is a wall
    if current_maze[next_row][next_col] == "#":
        return False # It's a wall

    return True # The move is valid!

Explanation:
* get_player_move() uses a while True loop to keep asking for input until the player enters ‘w’, ‘a’, ‘s’, or ‘d’. input() is a built-in Python function to get text input from the user. .lower() converts the input to lowercase, so ‘W’ also works.
* is_valid_move() checks two things:
* Bounds check: Ensures the next_row and next_col are within the valid range of rows and columns for our maze.
* Wall check: Ensures the target cell (current_maze[next_row][next_col]) is not a wall (#).

Step 3: The Game Loop and Win Condition

This is where all the pieces come together! The game will run in a game loop (a while loop that continues as long as the game is active). Inside this loop, we’ll display the maze, get player input, check the move, update the position, and finally, check if the player has reached the exit.

def start_game():
    current_player_pos = list(player_position) # Make a copy to avoid modifying original
    game_over = False

    while not game_over: # The game continues as long as game_over is False
        # 1. Display the current maze
        display_maze(maze, current_player_pos)

        # 2. Get player's move
        move = get_player_move()

        # Calculate potential new position
        next_row, next_col = current_player_pos[0], current_player_pos[1]

        # Conditional statements (if/elif/else) help us make decisions in code.
        # They check conditions and execute different blocks of code based on whether conditions are true or false.
        if move == 'w': # Move up (decrease row number)
            next_row -= 1
        elif move == 's': # Move down (increase row number)
            next_row += 1
        elif move == 'a': # Move left (decrease column number)
            next_col -= 1
        elif move == 'd': # Move right (increase column number)
            next_col += 1

        # 3. Check if the move is valid
        if is_valid_move(maze, next_row, next_col):
            current_player_pos[0] = next_row
            current_player_pos[1] = next_col
            print("You moved!")
        else:
            print("Oops! You hit a wall or went out of bounds. Try again.")

        # 4. Check for win condition
        # If the player's current position is the 'E'xit
        if maze[current_player_pos[0]][current_player_pos[1]] == "E":
            display_maze(maze, current_player_pos) # Show the final position
            print("\nCongratulations! You've found the exit and won the game!")
            game_over = True # Set game_over to True to end the loop

        # Optional: Clear screen for cleaner display (works in some terminals)
        # import os
        # os.system('cls' if os.name == 'nt' else 'clear')

Explanation:
* The start_game() function initiates the game.
* while not game_over: means the loop will continue as long as game_over is False.
* Inside the loop, we call display_maze(), get_player_move(), and then use if/elif statements to determine the next_row and next_col based on the input.
* is_valid_move() is called. If True, the current_player_pos is updated. If False, an error message is printed.
* The win condition checks if the player landed on the ‘E’xit character in the original maze. If so, a congratulatory message is printed, and game_over is set to True, breaking the loop and ending the game.

Putting It All Together (Full Code)

Here’s the complete code for our simple maze game. Copy and paste this into a file named maze_game.py (or any other .py file).

import os

maze = [
    ["#", "#", "#", "#", "#", "#", "#", "#", "#"],
    ["#", "P", " ", " ", " ", " ", " ", " ", "#"],
    ["#", " ", "#", "#", "#", " ", "#", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", "#", " ", "#", " ", " ", " ", "#"],
    ["#", " ", "#", " ", "#", " ", "#", " ", "#"],
    ["#", " ", " ", " ", " ", " ", "#", "E", "#"],
    ["#", "#", "#", "#", "#", "#", "#", "#", "#"]
]

player_position = [1, 1] # Row 1, Column 1 (0-indexed)

def clear_screen():
    # 'cls' for Windows, 'clear' for macOS/Linux
    os.system('cls' if os.name == 'nt' else 'clear')

def display_maze(current_maze, player_pos):
    clear_screen() # Clear the screen before displaying the new maze
    print("--- MAZE GAME ---")
    print("Use w, a, s, d to move.")
    print("Find 'E' to win!\n")

    # Create a temporary maze to display the player without changing the original map
    display_map = [row[:] for row in current_maze] 

    # Place the player 'P' at their current position on the display map
    display_map[player_pos[0]][player_pos[1]] = "P"

    # Print each row of the maze
    for row in display_map:
        print("".join(row))
    print("\n-----------------")

def get_player_move():
    while True:
        move = input("Enter your move (w: up, a: left, s: down, d: right): ").lower()
        if move in ['w', 'a', 's', 'd']:
            return move
        else:
            print("Invalid input. Please use 'w', 'a', 's', or 'd'.")

def is_valid_move(current_maze, next_row, next_col):
    # Check if the next position is within the maze boundaries
    if not (0 <= next_row < len(current_maze) and 0 <= next_col < len(current_maze[0])):
        return False # Out of bounds

    # Check if the next position is a wall
    if current_maze[next_row][next_col] == "#":
        return False # It's a wall

    return True # The move is valid!

def start_game():
    # Make a copy of player_position to ensure the original global list isn't modified
    current_player_pos = list(player_position) 
    game_over = False

    while not game_over:
        # 1. Display the current maze
        display_maze(maze, current_player_pos)

        # 2. Get player's move
        move = get_player_move()

        # Calculate potential new position
        next_row, next_col = current_player_pos[0], current_player_pos[1]

        if move == 'w': # Move up (decrease row number)
            next_row -= 1
        elif move == 's': # Move down (increase row number)
            next_row += 1
        elif move == 'a': # Move left (decrease column number)
            next_col -= 1
        elif move == 'd': # Move right (increase column number)
            next_col += 1

        # 3. Check if the move is valid
        if is_valid_move(maze, next_row, next_col):
            current_player_pos[0] = next_row
            current_player_pos[1] = next_col
            # print("You moved!") # Removed to avoid extra line before clear_screen
        else:
            print("Oops! You hit a wall or went out of bounds. Try again.")
            # Pause briefly to let the user read the message before clearing
            input("Press Enter to continue...") 

        # 4. Check for win condition
        if maze[current_player_pos[0]][current_player_pos[1]] == "E":
            display_maze(maze, current_player_pos) # Show the final position
            print("\n**************************************************")
            print("CONGRATULATIONS! You've found the exit and won!")
            print("**************************************************")
            game_over = True

if __name__ == "__main__":
    start_game()

How to Run Your Game

  1. Save the code: Save the code above into a file named maze_game.py.
  2. Open your terminal/command prompt: Navigate to the directory where you saved the file.
  3. Run the script: Type python maze_game.py and press Enter.

Your maze game will appear in the terminal, and you can start playing!

Ideas for Improvement

This is a very basic maze game, but it’s a great foundation! Here are some ideas to make it even better:

  • More Mazes: Create a list of different maze layouts and let the player choose which one to play, or randomly pick one.
  • Larger Mazes: Experiment with bigger grids.
  • Scoring System: Keep track of the number of moves the player makes and display it at the end.
  • Timer: Add a timer to see how fast the player can solve the maze.
  • Different Characters: Use different symbols for the player, walls, or even add obstacles or power-ups.
  • Maze Generator: This is more advanced, but you could write a program that generates a random maze for you!
  • GUI: If you’re feeling adventurous, explore Python libraries like Pygame or Tkinter to add a graphical user interface instead of a text-based one.

Conclusion

Congratulations! You’ve successfully built your first simple maze game in Python. You’ve used fundamental programming concepts like lists, functions, loops, and conditional statements. This project is a fantastic stepping stone for further exploration into game development and general programming.

Remember, the best way to learn is by doing and experimenting. Don’t be afraid to change things, break them, and fix them. Happy coding, and have fun navigating your mazes!

Comments

Leave a Reply