Python Tkinter Puzzle Game Source Code

How To Create Image Puzzle Game in Python Tkinter

How To Create Image Puzzle Game in Python Tkinter


In this Python Tutorial we will see How to Create a picture puzzle game using Python And Tkinter.
This project demonstrates how to build a complete image puzzle game where players can upload their own pictures and solve them as puzzles with customizable difficulty levels. 
The game features a modern dark theme, smooth piece-swapping animations, move counting, and a built-in timer.

The Game Features:
- Upload your own images: Players can upload any image (PNG, JPG, JPEG, GIF, or BMP) and instantly transform it into a playable puzzle. The game automatically resizes and crops images to fit perfectly within the puzzle grid.

Multiple difficulty levels: With grid sizes ranging from 4x4 (16 pieces) to 8x8 (64 pieces), players can choose their preferred challenge level.

Move counter and timer: The application tracks moves and elapsed time, adding a competitive element to the experience.

Smooth piece-swapping animations: The piece-swapping feature includes a modern animations with easing functions, making the gameplay feel cool.

- Winning detection and celebration: Displays final statistics in a congratulations window.

How the Game Works:
When you start the game, you'll see a window split into two parts:
- A sidebar with controls and game stats.
- The main puzzle grid area.

You can upload any image, and the game will automatically:
- Resize it to fit the game window.
- Split it into equal pieces based on your chosen difficulty.
- Shuffle the pieces for you to solve.



if you want the source code click on the download button below




Project Source Code:

     - Opens file chooser and loads selected image to start puzzle.
     

def load_and_start_puzzle(self):
# Open file dialog to select an image
file_path = filedialog.askopenfilename(
filetypes=[("Image files", "*.png *.jpg *.jpeg *.gif *.bmp")])

# Check if user selected a file
if file_path:
# Load the selected image and start the game
self.original_image = Image.open(file_path)
self.reset_game() # Reset and start the game with the new image

    

     - Reset game state variables.

def reset_game(self):
self.moves = 0 # Reset move counter
self.elapsed_time = 0 # Reset timer
self.moves_label.config(text="Moves: 0") # Update moves display
self.timer_label.config(text="Time: 00:00") # Update timer display
self.stop_timer() # Stop any existing timer

# Clear previous game elements
self.canvas.delete("all") # Clear the canvas
self.label_list.clear() # Clear the list of puzzle pieces
self.correct_order.clear() # Clear the correct order list

# Resize the uploaded image to fit the puzzle area
resized_image = self.original_image.resize((self.image_size, self.image_size),
                        Image.LANCZOS)

# Slice the image into puzzle pieces
self.sliced_images = self.slice_image(resized_image)
self.correct_order = self.sliced_images.copy() # Store the correct order
random.shuffle(self.sliced_images) # Shuffle pieces to start the game

# Calculate the size of each puzzle piece
slice_width = self.image_size // self.grid_size
slice_height = self.image_size // self.grid_size

# Place sliced images on the canvas
for i, img in enumerate(self.sliced_images):
row = i // self.grid_size # Calculate row position
col = i % self.grid_size # Calculate column position
x = col * slice_width # Calculate x-coordinate
y = row * slice_height # Calculate y-coordinate

# Create the image on the canvas and store its reference
image_item = self.canvas.create_image(x, y, image=img, anchor="nw")
self.label_list.append(image_item) # Add to the list of pieces

# Bind click event to each puzzle piece
self.canvas.tag_bind(image_item, "<Button-1>",
lambda event, item=image_item: self.on_click(item))

# Start the game timer
self.start_timer()

    

     - Slice the image into a grid of smaller images.

def slice_image(self, image):
slices = [] # List to store sliced images
width, height = image.size # Get image dimensions

# Calculate dimensions of each slice
slice_width = width // self.grid_size
slice_height = height // self.grid_size

# Create slices by cropping the original image
for row in range(self.grid_size):
for col in range(self.grid_size):
# Calculate the coordinates for cropping
left = col * slice_width
upper = row * slice_height
right = left + slice_width
lower = upper + slice_height

# Crop the image and convert to PhotoImage for tkinter display
slice_img = image.crop((left, upper, right, lower))
photo_img = ImageTk.PhotoImage(slice_img)
slices.append(photo_img) # Add to slices list

return slices


    

     - Handles click events on puzzle pieces.

def on_click(self, item):
# Handle click events on puzzle pieces
if self.first_selected is None:
# First piece selected
self.first_selected = item # Store the selected piece
self.draw_selection_outline(item) # Draw outline around the selected piece
elif self.second_selected is None and item != self.first_selected:
# Second piece selected (and different from first)
self.second_selected = item # Store the second piece
self.canvas.delete("selection_outline") # Clear existing outlines
self.draw_selection_outline(item) # Draw outline around second piece

# Start the swap animation after a short delay
self.root.after(100, self.swap_slices_with_animation)




     - Draw a highlight outline around the selected puzzle piece.

def draw_selection_outline(self, item):
coords = self.canvas.coords(item) # Get coordinates of the piece
if coords:
x, y = coords[0], coords[1] # Extract x and y coordinates
# Calculate width and height of the piece
width = self.image_size // self.grid_size
height = self.image_size // self.grid_size

# Create a rectangle outline around the selected piece
self.selection_outline = self.canvas.create_rectangle(
x, y, x + width, y + height,
outline="#f9c74f", width=3, tags="selection_outline")



     - Animates the swapping of two puzzle pieces.

def swap_slices_with_animation(self):
# Animate the swapping of two selected puzzle pieces
if self.first_selected and self.second_selected:
# Get current positions of both pieces
start_pos1 = self.canvas.coords(self.first_selected)
start_pos2 = self.canvas.coords(self.second_selected)

# Set target positions (swap them)
end_pos1 = start_pos2
end_pos2 = start_pos1

# Start the animation for both pieces
self.animate_swap(self.first_selected, start_pos1, end_pos1, 0)
self.animate_swap(self.second_selected, start_pos2, end_pos2, 0)

# Increment move counter and update display
self.moves += 1
self.moves_label.config(text=f"Moves: {self.moves}")

# Finalize the swap after the animation completes
self.root.after(500, self.finalize_swap)

    

     - Piece Swapping Animations.

def animate_swap(self, item, start_pos, end_pos, step):
# Perform a single step of the swap animation
if step <= 10: # Animation has 10 steps
# Calculate progress with easing function for smooth animation
progress = self.ease_in_out_quad(step / 10)

# Calculate new position
new_x = start_pos[0] + (end_pos[0] - start_pos[0]) * progress
new_y = start_pos[1] + (end_pos[1] - start_pos[1]) * progress

# Move the piece to the new position
self.canvas.coords(item, new_x, new_y)

# Schedule the next animation step
self.root.after(50, lambda:
                            self.animate_swap(item, start_pos, end_pos, step + 1))

    

     - Completes the swap operation after animation.

def finalize_swap(self):
# Complete the swap operation after animation
# Find indices of selected pieces in the label list
idx1 = self.label_list.index(self.first_selected)
idx2 = self.label_list.index(self.second_selected)

# Delete the selection outline
if hasattr(self, 'selection_outline'):
self.canvas.delete(self.selection_outline)

# Swap the images in the label list
self.label_list[idx1], self.label_list[idx2] = self.label_list[idx2],
                self.label_list[idx1]

# Reset selection variables
self.first_selected = None
self.second_selected = None

# Check if the puzzle is solved
self.check_if_solved()

    

     - Easing function for smooth animation.

@staticmethod
def ease_in_out_quad(t):
# Easing function for smooth animation
# Makes animation start and end slowly, with faster movement in the middle
return 2 * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 2) / 2

    

     - Check if puzzle is solved.

def check_if_solved(self):
# Check if the puzzle is solved
# Get current order of images from canvas
current_order = [self.canvas.itemcget(item, 'image')
                            for item in self.label_list]
# Get correct order as strings for comparison
correct_order = [str(img) for img in self.correct_order]

# Compare current order with correct order
if current_order == correct_order:
self.stop_timer() # Stop the timer
# Show congratulations message after a short delay
self.root.after(100, self.show_congratulations)


     - Shows congratulation dialog when puzzle is solved.

def show_congratulations(self):
# Display a congratulations window when the puzzle is solved
congrats_window = tk.Toplevel(self.root) # Create new window
congrats_window.title("Congratulations!") # Set window title
congrats_window.geometry("400x350") # Set window size
congrats_window.configure(bg="#313244") # Set background color

# Add "Puzzle Solved!" title
tk.Label(congrats_window, text="Puzzle Solved!",
font=("Montserrat", 28, "bold"),
                        bg="#313244", fg="#cba6f7").pack(pady=(20, 10))

# Display the number of moves and time taken
tk.Label(congrats_window, text=f"Moves: {self.moves}",
font=("Montserrat", 20), bg="#313244", fg="#b4befe").pack(pady=5)

tk.Label(congrats_window, text=f"Time: {self.format_time(self.elapsed_time)}",
font=("Montserrat", 20), bg="#313244", fg="#b4befe").pack(pady=5)

# Display a congratulatory message
message = "Great job! You've successfully completed the puzzle."
tk.Label(congrats_window, text=message, font=("Montserrat", 16),
bg="#313244", fg="#a6e3a1", wraplength=350).pack(pady=(10, 20))

# Create a close button for the congratulations window
close_button = tk.Button(
congrats_window, text="Close", font=("Montserrat", 16, "bold"),
bg="#f9c74f", fg="#1e1e2e", activebackground="#f3a712",
activeforeground="#1e1e2e", bd=0, command=congrats_window.destroy,
width=20, height=2)

close_button.pack(pady=10)

    

     - Shuffle the pieces.

def shuffle_grid(self):
# Shuffle the puzzle pieces
if hasattr(self, 'sliced_images'): # Check if puzzle is loaded
random.shuffle(self.sliced_images) # Shuffle the pieces

# Update the images on the canvas
for i, img in enumerate(self.sliced_images):
self.canvas.itemconfig(self.label_list[i], image=img)

# Reset game state
self.moves = 0 # Reset moves counter
self.elapsed_time = 0 # Reset timer
self.moves_label.config(text="Moves: 0") # Update moves display
self.timer_label.config(text="Time: 00:00") # Update timer display
self.stop_timer() # Stop the timer before starting again
self.start_timer() # Start the timer again

    

     - Change the game difficulty

def change_difficulty(self, *args):
# Change the grid size based on selected difficulty
self.grid_size = int(self.difficulty_var.get()[0]) # Get first character as number
if hasattr(self, 'original_image'): # Check if an image is loaded
self.reset_game() # Reset the game with the new grid size
    

The Final Result:

Python Tkinter Puzzle Game Source Code

Python Tkinter Puzzle Game

Python Puzzle Game

Puzzle Game Using Python Tkinter With Source Code

Puzzle Game Using Python Tkinter

Image Puzzle Game Using Python Tkinter With Source Code

Image Puzzle Game In Python Tkinter




if you want the source code click on the download button below




disclaimer: you will get the source code and to make it work in your machine is your responsibility and to debug any error/exception is your responsibility this project is for the students who want to see an example and read the code not to get and run.





Share this

Related Posts

Latest
Previous
Next Post »