Basic Sudoku Grid — Altair

A standard 9×9 Sudoku grid with proper visual hierarchy showing 3×3 box regions. Thick lines separate the nine boxes while thin lines divide individual cells. The clean black-and-white design is optimized for printing and solving puzzles.

Basic Sudoku Grid rendered with Altair

Python source (Altair)

""" anyplot.ai
sudoku-basic: Basic Sudoku Grid
Library: altair 6.2.2 | Python 3.13.14
Quality: 88/100 | Updated: 2026-06-25
"""

import os

import altair as alt
import pandas as pd
from PIL import Image


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Sudoku grid data - "World's Hardest Sudoku"
grid = [
    [5, 3, 0, 0, 7, 0, 0, 0, 0],
    [6, 0, 0, 1, 9, 5, 0, 0, 0],
    [0, 9, 8, 0, 0, 0, 0, 6, 0],
    [8, 0, 0, 0, 6, 0, 0, 0, 3],
    [4, 0, 0, 8, 0, 3, 0, 0, 1],
    [7, 0, 0, 0, 2, 0, 0, 0, 6],
    [0, 6, 0, 0, 0, 0, 2, 8, 0],
    [0, 0, 0, 4, 1, 9, 0, 0, 5],
    [0, 0, 0, 0, 8, 0, 0, 7, 9],
]

# Cell data with tooltip metadata
cell_data = []
for row in range(9):
    for col in range(9):
        value = grid[row][col]
        if value != 0:
            cell_data.append(
                {
                    "col": col + 0.5,
                    "row": 8 - row + 0.5,
                    "value": str(value),
                    "row_num": row + 1,
                    "col_num": col + 1,
                    "box": (row // 3) * 3 + col // 3 + 1,
                }
            )

df_numbers = pd.DataFrame(cell_data)

# Alternating 3×3 box backgrounds — checkerboard pattern for visual depth
shaded_boxes = [
    {"x": bc * 3, "x2": bc * 3 + 3, "y": br * 3, "y2": br * 3 + 3}
    for br in range(3)
    for bc in range(3)
    if (br + bc) % 2 == 0
]
df_shaded = pd.DataFrame(shaded_boxes)

# Grid lines — thin for cells, thick for 3×3 box boundaries
thin_lines = []
thick_lines = []
for i in range(10):
    v_line = {"x": i, "x2": i, "y": 0, "y2": 9}
    h_line = {"x": 0, "x2": 9, "y": i, "y2": i}
    if i % 3 == 0:
        thick_lines.append(v_line)
        thick_lines.append(h_line)
    else:
        thin_lines.append(v_line)
        thin_lines.append(h_line)

df_thin = pd.DataFrame(thin_lines)
df_thick = pd.DataFrame(thick_lines)

# Layer 1: subtle checkerboard shading for 3×3 box regions
box_bg = alt.Chart(df_shaded).mark_rect(color=INK, fillOpacity=0.07).encode(x="x:Q", x2="x2:Q", y="y:Q", y2="y2:Q")

# Layer 2 & 3: thin cell lines, thick box-separator lines
thin_grid = alt.Chart(df_thin).mark_rule(color=INK_SOFT, strokeWidth=1.0).encode(x="x:Q", x2="x2:Q", y="y:Q", y2="y2:Q")

thick_grid = alt.Chart(df_thick).mark_rule(color=INK, strokeWidth=4.5).encode(x="x:Q", x2="x2:Q", y="y:Q", y2="y2:Q")

# Layer 4: numbers with interactive tooltips for the HTML output
numbers = (
    alt.Chart(df_numbers)
    .mark_text(fontSize=40, fontWeight="bold", color=INK)
    .encode(
        x="col:Q",
        y="row:Q",
        text="value:N",
        tooltip=[
            alt.Tooltip("row_num:Q", title="Row"),
            alt.Tooltip("col_num:Q", title="Column"),
            alt.Tooltip("value:N", title="Digit"),
            alt.Tooltip("box:Q", title="Box"),
        ],
    )
)

chart = (
    alt.layer(box_bg, thin_grid, thick_grid, numbers)
    .properties(
        width=500,
        height=500,
        background=PAGE_BG,
        title=alt.Title(
            "sudoku-basic · python · altair · anyplot.ai",
            fontSize=16,
            fontWeight="normal",
            color=INK,
            anchor="middle",
            offset=16,
        ),
    )
    .configure_view(fill=PAGE_BG, stroke=None)
    .configure_axis(grid=False, domain=False, ticks=False, labels=False, title=None)
    .configure_scale(bandPaddingInner=0, bandPaddingOuter=0)
)

# Save — square target 2400×2400
TW, TH = 2400, 2400
chart.save(f"plot-{THEME}.png", scale_factor=4.0)
chart.save(f"plot-{THEME}.html")

# Pad to exact target dimensions (vl-convert inner-view is smaller than the PNG target)
_img = Image.open(f"plot-{THEME}.png").convert("RGB")
_w, _h = _img.size
if _w > TW or _h > TH:
    raise SystemExit(
        f"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. "
        f"Shrink chart .properties(width=, height=) values and re-render."
    )
if _w < TW or _h < TH:
    _canvas = Image.new("RGB", (TW, TH), PAGE_BG)
    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))
    _canvas.save(f"plot-{THEME}.png")

Part of Basic Sudoku Grid on anyplot.ai.

Other implementations