Project 30: Sorting Race Visualizer 🏁¶

Difficulty: 🟡 Intermediate

Run cells top to bottom. Each step builds on the previous one and saves real files under outputs/sorting_race_visualizer/.

← Back to index

Description¶

Animate bubble sort vs insertion sort as frame sequences and save a side-by-side race GIF.

Libraries & Modules¶

  • numpy, matplotlib
  • PIL.Image for GIF assembly

Python Concepts You'll Practice¶

Algorithms, snapshots, animation export

🎛️ Parameter Variations¶

  • N — array length
  • DURATION_MS — GIF speed
In [1]:
# Install dependencies for this project (safe to re-run)
import sys
!{sys.executable} -m pip install pillow numpy matplotlib wordcloud qrcode[pil] python-barcode fpdf2 jinja2 folium plotly pandas scipy ipywidgets -q
Skipping pip install (already installed)
In [2]:
# Interactive Jupyter setup — run this cell first
%matplotlib inline

from pathlib import Path
from IPython.display import display, Image as IPImage, HTML, Markdown, Audio, IFrame

OUTPUT_DIR = Path("outputs") / "sorting_race_visualizer"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
print(f"✅ Outputs folder: {OUTPUT_DIR.resolve()}")
✅ Outputs folder: C:\Users\Hansel Yan\Projects\CodeItAll\outputs\sorting_race_visualizer

Step 1 — Capture sort frames¶

In [3]:
import numpy as np
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image

# 🎛️ TWEAK THESE
N = 24
SEED = 1
DURATION_MS = 80

rng = np.random.default_rng(SEED)
base = rng.permutation(np.arange(1, N + 1))

def frames_bubble(arr):
    a = arr.copy()
    snaps = [a.copy()]
    for i in range(len(a)):
        for j in range(0, len(a) - i - 1):
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]
                snaps.append(a.copy())
    return snaps

def frames_insertion(arr):
    a = arr.copy()
    snaps = [a.copy()]
    for i in range(1, len(a)):
        key = a[i]
        j = i - 1
        while j >= 0 and a[j] > key:
            a[j + 1] = a[j]
            j -= 1
            snaps.append(a.copy())
        a[j + 1] = key
        snaps.append(a.copy())
    return snaps

bubble = frames_bubble(base)
insert = frames_insertion(base)
print(f"Bubble frames: {len(bubble)} | Insertion frames: {len(insert)}")
Bubble frames: 156 | Insertion frames: 179

Step 2 — Render race GIF¶

In [4]:
def render(arr, title):
    fig, ax = plt.subplots(figsize=(4.5, 3))
    ax.bar(range(len(arr)), arr, color="#222222")
    ax.set_title(title)
    ax.set_ylim(0, N + 1)
    ax.set_xticks([])
    buf = BytesIO()
    fig.savefig(buf, format="png", dpi=100)
    plt.close(fig)
    buf.seek(0)
    return Image.open(buf).convert("RGB")

max_len = max(len(bubble), len(insert))
gif_frames = []
for i in range(max_len):
    b = bubble[min(i, len(bubble) - 1)]
    s = insert[min(i, len(insert) - 1)]
    left = render(b, "Bubble sort")
    right = render(s, "Insertion sort")
    canvas = Image.new("RGB", (left.width + right.width, max(left.height, right.height)), (255, 255, 255))
    canvas.paste(left, (0, 0))
    canvas.paste(right, (left.width, 0))
    gif_frames.append(canvas)

out = OUTPUT_DIR / "sorting_race.gif"
gif_frames[0].save(out, save_all=True, append_images=gif_frames[1:], duration=DURATION_MS, loop=0)
print(f"Saved race GIF → {out} ({len(gif_frames)} frames)")
Saved race GIF → outputs\sorting_race_visualizer\sorting_race.gif (179 frames)

✅ Project complete¶

Check your output folder: outputs/sorting_race_visualizer/

Next: Project 31: Markov Meme Writer · Index