Basic Sudoku Grid — lets-plot

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 lets-plot

Python source (lets-plot)

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

import os

from lets_plot import (
    LetsPlot,
    aes,
    coord_fixed,
    element_blank,
    element_rect,
    element_text,
    geom_segment,
    geom_text,
    geom_tile,
    ggplot,
    ggsave,
    ggsize,
    labs,
    layer_tooltips,
    scale_x_continuous,
    scale_y_continuous,
    theme,
    theme_void,
)


LetsPlot.setup_html()

# Imprint palette theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Classic Sudoku puzzle (0 = empty cell)
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],
]

# Build cell data with position metadata for tooltips
xs, ys, labels, row_nums, col_nums, box_nums, statuses = [], [], [], [], [], [], []
for row in range(9):
    for col in range(9):
        val = grid[row][col]
        xs.append(col + 0.5)
        ys.append(8 - row + 0.5)
        labels.append(str(val) if val != 0 else "")
        row_nums.append(row + 1)
        col_nums.append(col + 1)
        box_nums.append((row // 3) * 3 + (col // 3) + 1)
        statuses.append("given" if val != 0 else "empty")

cells = {"x": xs, "y": ys, "label": labels, "row": row_nums, "col": col_nums, "box": box_nums, "status": statuses}

# Thin grid lines (cell boundaries, skipping 3x3 box positions)
thin_x, thin_y, thin_xend, thin_yend = [], [], [], []
for i in range(10):
    if i % 3 != 0:
        thin_x.append(i)
        thin_y.append(0)
        thin_xend.append(i)
        thin_yend.append(9)
        thin_x.append(0)
        thin_y.append(i)
        thin_xend.append(9)
        thin_yend.append(i)
thin_lines = {"x": thin_x, "y": thin_y, "xend": thin_xend, "yend": thin_yend}

# Thick grid lines (3x3 box boundaries)
thick_x, thick_y, thick_xend, thick_yend = [], [], [], []
for i in (0, 3, 6, 9):
    thick_x.append(i)
    thick_y.append(0)
    thick_xend.append(i)
    thick_yend.append(9)
    thick_x.append(0)
    thick_y.append(i)
    thick_xend.append(9)
    thick_yend.append(i)
thick_lines = {"x": thick_x, "y": thick_y, "xend": thick_xend, "yend": thick_yend}

# Tooltips: show cell position and box number on hover (HTML interactivity)
cell_tooltips = layer_tooltips().line("Row @row, Col @col").line("Box @box").line("@status")

plot = (
    ggplot()
    + geom_tile(aes(x="x", y="y"), data=cells, fill=PAGE_BG, color=PAGE_BG, width=1, height=1, tooltips=cell_tooltips)
    + geom_segment(aes(x="x", y="y", xend="xend", yend="yend"), data=thin_lines, color=INK_SOFT, size=0.4)
    + geom_segment(aes(x="x", y="y", xend="xend", yend="yend"), data=thick_lines, color=INK, size=2.0)
    + geom_text(aes(x="x", y="y", label="label"), data=cells, size=9, color=INK, fontface="bold")
    + coord_fixed(ratio=1)
    + scale_x_continuous(limits=[-0.1, 9.1], expand=[0, 0])
    + scale_y_continuous(limits=[-0.1, 9.1], expand=[0, 0])
    + labs(title="sudoku-basic · python · letsplot · anyplot.ai")
    + theme_void()
    + theme(
        plot_title=element_text(size=16, color=INK, hjust=0.5, face="bold"),
        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        axis_title=element_blank(),
        axis_text=element_blank(),
        axis_ticks=element_blank(),
        plot_margin=[40, 40, 40, 40],
    )
    + ggsize(600, 600)
)

# Save — scale=4 yields 2400×2400 px (canonical square canvas)
ggsave(plot, f"plot-{THEME}.png", path=".", scale=4)
ggsave(plot, f"plot-{THEME}.html", path=".")

Part of Basic Sudoku Grid on anyplot.ai.

Other implementations