In this tutorial, we’ll build a desktop app in Python that generates Number Snake math puzzles. The app:
Creates solvable arithmetic “snake” paths
Supports Easy (3×3), Medium (4×4), and Hard (5×5) grids
Shows step-by-step solutions
Exports puzzles to PDF or JPG
Can batch-generate multiple worksheets
We’ll use:
tkinter for the GUI
ttkbootstrap for modern styling
reportlab for PDF export
Pillow for JPG images
This guide is written for beginners and breaks everything into small, understandable steps.
✅ Prerequisites
Make sure you have Python 3.9+ installed.
Then install the required packages:
pip install ttkbootstrap reportlab pillow
📁 Project Structure
Create a single file:
number_snake.py
We’ll place everything inside this file.
🧱 Step 1 — Imports and Basic Setup
Start by importing the libraries we’ll need:
import tkinter as tk
from tkinter import messagebox, filedialog
import random
import operator
import ttkbootstrap as tb
from ttkbootstrap.constants import *
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from PIL import Image, ImageDraw, ImageFont
from pathlib import Path
What these do
tkinter → core GUI
ttkbootstrap → modern dark theme + widgets
random + operator → puzzle math
reportlab → PDF generation
Pillow → JPG images
Path → clean file handling
🐍 Step 2 — Create the Main App Class
Now we define our application class:
class NumberSnake:
APP_NAME = "Number Snake Generator"
APP_VERSION = "1.0"
OPERATORS = {
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
"/": operator.floordiv
}
Explanation
APP_NAME and APP_VERSION are just labels
OPERATORS maps symbols to real Python math functions
This lets us randomly choose operations later.
🖥️ Step 3 — Initialize the Window
Inside init, we configure the GUI:
def __init__(self):
self.root = tk.Tk()
tb.Style(theme="darkly")
self.root.title(f"{self.APP_NAME} v{self.APP_VERSION}")
self.root.geometry("1100x680")
self.difficulty_var = tk.StringVar(value="Easy")
self.num_puzzles_var = tk.IntVar(value=1)
self.grid_numbers = []
self.grid_ops = []
self.solution_path = []
self.target_number = None
self.rows = self.cols = 0
self._build_ui()
What’s happening
Creates the main window
Applies a dark theme
Sets defaults for difficulty and puzzle count
Initializes empty puzzle data
Calls _build_ui() to draw the interface
🎛️ Step 4 — Build the User Interface
Now we create labels, dropdowns, and buttons:
def _build_ui(self):
tb.Label(self.root, text=self.APP_NAME,
font=("Segoe UI", 22, "bold")).pack(pady=10)
opts = tb.Labelframe(self.root, text="Options", padding=10)
opts.pack(fill="x", padx=10)
tb.Label(opts, text="Difficulty:").pack(side="left")
tb.Combobox(opts,
values=["Easy","Medium","Hard"],
textvariable=self.difficulty_var,
width=10
).pack(side="left", padx=5)
tb.Label(opts, text="Number of Puzzles:").pack(side="left", padx=10)
tb.Spinbox(opts, from_=1, to=20,
textvariable=self.num_puzzles_var,
width=5).pack(side="left")
This gives us:
Difficulty selector
Number-of-puzzles input
Buttons
ctrl = tb.Frame(self.root)
ctrl.pack(fill="x", padx=10, pady=10)
tb.Button(ctrl, text="Generate Single Puzzle",
bootstyle="success",
command=self.generate_single_puzzle).pack(side="left", padx=5)
tb.Button(ctrl, text="Multiple PDFs",
bootstyle="warning",
command=self.generate_multiple_combined_pdf).pack(side="left", padx=5)
tb.Button(ctrl, text="JPG Export",
bootstyle="secondary",
command=self.generate_multiple_jpgs).pack(side="left", padx=5)
Each button simply calls a method we’ll define later.
🧠 Step 5 — Generate the Snake Puzzle
This is the heart of the project.
def create_puzzle_data(self):
diff = self.difficulty_var.get()
self.rows, self.cols = (3,3) if diff=="Easy" else (4,4) if diff=="Medium" else (5,5)
visited = [[False]*self.cols for _ in range(self.rows)]
r = c = 0
self.solution_path = [(0,0)]
visited[0][0] = True
Explanation
Grid size depends on difficulty
We start in the top-left
visited tracks where we’ve been
Create the Snake Path
moves = [(0,1),(1,0),(0,-1),(-1,0)]
while len(self.solution_path) < self.rows * self.cols:
random.shuffle(moves)
for dr, dc in moves:
nr, nc = r+dr, c+dc
if 0<=nr<self.rows and 0<=nc<self.cols and not visited[nr][nc]:
r, c = nr, nc
self.solution_path.append((r,c))
visited[r][c] = True
break
This randomly walks through the grid, touching every cell once.
That’s your “snake”.
➕ Step 6 — Fill Numbers and Operations
numbers = [[0]*self.cols for _ in range(self.rows)]
ops = [[None]*self.cols for _ in range(self.rows)]
current = random.randint(1,9)
numbers[0][0] = current
steps = [f"Start: {current}"]
Then for every next cell:
for r,c in self.solution_path[1:]:
valid = False
while not valid:
op = random.choice(list(self.OPERATORS.keys()))
num = random.randint(1,9)
if op == "/" and current % num != 0:
continue
if op == "-" and current - num <= 0:
continue
next_val = self.OPERATORS[op](current, num)
valid = True
ops[r][c] = op
numbers[r][c] = num
steps.append(f"{current} {op} {num} = {next_val}")
current = next_val
Why the checks?
Avoid negative results
Avoid fractional division
Ensure every puzzle is solvable with integers
🎯 Step 7 — Display the Grid and Solution
We draw labels for each cell:
def display_grid(self):
for r in range(self.rows):
for c in range(self.cols):
text = str(self.grid_numbers[r][c]) \
if self.grid_ops[r][c] is None \
else f"{self.grid_ops[r][c]}{self.grid_numbers[r][c]}"
Green cells highlight the snake path.
The solution panel prints each math step:
def show_solution(self):
self.solution_text.delete("1.0", tk.END)
self.solution_text.insert(tk.END, "\n".join(self.steps))
📄 Step 8 — Export to PDF
Using ReportLab:
c = canvas.Canvas("puzzle.pdf", pagesize=A4)
c.drawString(50, 800, "Number Snake Puzzle")
We draw:
Title
Grid
Target
Step-by-step solution
Each puzzle can be saved separately or combined.
🖼️ Step 9 — Export to JPG
With Pillow:
img = Image.new("RGB",(900,700),(34,34,34))
draw = ImageDraw.Draw(img)
draw.text((20,20),"Number Snake Puzzle", fill="white")
Then we:
Draw grid squares
Add numbers
Write solution steps
Save as NumberSnake_1.jpg, NumberSnake_2.jpg, etc.
Perfect for printable worksheets.
▶️ Final Step — Run the App
At the bottom of your file:
if __name__ == "__main__":
NumberSnake().run()
Run it:
python number_snake.py
🎉 Done!
You now have a full desktop app that:
Generates arithmetic snake puzzles
Shows solutions
Exports PDFs and JPGs
Supports batch worksheet creation
Source code on GitHub:
👉 https://github.com/rogers-cyber/NumberSnakeGenerator

Top comments (0)