Skip to content

Commit 4858879

Browse files
feat(matplotlib): implement crossword-basic (#7451)
## Implementation: `crossword-basic` - python/matplotlib Implements the **python/matplotlib** version of `crossword-basic`. **File:** `plots/crossword-basic/implementations/python/matplotlib.py` **Parent Issue:** #3805 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26136422760)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent db1a9d3 commit 4858879

2 files changed

Lines changed: 183 additions & 140 deletions

File tree

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
crossword-basic: Crossword Puzzle Grid
3-
Library: matplotlib 3.10.8 | Python 3.13.11
4-
Quality: 93/100 | Created: 2026-01-15
3+
Library: matplotlib 3.10.9 | Python 3.13.13
4+
Quality: 88/100 | Updated: 2026-05-20
55
"""
66

7+
import os
8+
79
import matplotlib.pyplot as plt
810
import numpy as np
911
from matplotlib.patches import Rectangle
1012

1113

14+
# Theme tokens
15+
THEME = os.getenv("ANYPLOT_THEME", "light")
16+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
17+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
18+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
19+
CELL_WHITE = "#FFFFFF" if THEME == "light" else "#D0CEC8"
20+
CELL_BLACK = "#1A1A17" if THEME == "light" else "#050503"
21+
NUM_COLOR = "#009E73" # Okabe-Ito position 1 (brand green) — sole colored element
22+
1223
# Data: 15x15 crossword grid with 180-degree rotational symmetry
1324
# 0 = white (entry cell), 1 = black (blocked cell)
14-
np.random.seed(42)
15-
1625
grid_size = 15
17-
18-
# Create symmetric black cell pattern (traditional newspaper style)
19-
# Start with empty grid
2026
grid = np.zeros((grid_size, grid_size), dtype=int)
2127

22-
# Define black cell positions for one half (will mirror for symmetry)
2328
black_cells = [
2429
(0, 4),
2530
(0, 10),
@@ -46,68 +51,63 @@
4651
(7, 12),
4752
]
4853

49-
# Set black cells and their symmetric counterparts
5054
for r, c in black_cells:
5155
grid[r, c] = 1
52-
# 180-degree rotational symmetry
5356
grid[grid_size - 1 - r, grid_size - 1 - c] = 1
5457

55-
# Calculate clue numbers (cells that start words across or down)
58+
# Calculate clue numbers (cells that start an across or down word)
5659
numbers = {}
5760
clue_num = 1
5861

5962
for row in range(grid_size):
6063
for col in range(grid_size):
6164
if grid[row, col] == 1:
62-
continue # Skip black cells
63-
65+
continue
6466
starts_across = (col == 0 or grid[row, col - 1] == 1) and (col < grid_size - 1 and grid[row, col + 1] == 0)
6567
starts_down = (row == 0 or grid[row - 1, col] == 1) and (row < grid_size - 1 and grid[row + 1, col] == 0)
66-
6768
if starts_across or starts_down:
6869
numbers[(row, col)] = clue_num
6970
clue_num += 1
7071

71-
# Create plot (square format for crossword)
72-
fig, ax = plt.subplots(figsize=(12, 12))
72+
# Plot — square format (2400×2400 px) suits the 1:1 crossword grid
73+
fig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)
74+
ax.set_facecolor(PAGE_BG)
7375

74-
# Draw the grid
7576
cell_size = 1.0
7677

7778
for row in range(grid_size):
7879
for col in range(grid_size):
7980
x = col * cell_size
80-
y = (grid_size - 1 - row) * cell_size # Flip y-axis for proper orientation
81+
y = (grid_size - 1 - row) * cell_size # flip y so row 0 is at top
8182

82-
if grid[row, col] == 1:
83-
# Black cell
84-
rect = Rectangle((x, y), cell_size, cell_size, facecolor="#1a1a1a", edgecolor="#333333", linewidth=1.5)
85-
else:
86-
# White entry cell
87-
rect = Rectangle((x, y), cell_size, cell_size, facecolor="white", edgecolor="#333333", linewidth=1.5)
83+
face = CELL_BLACK if grid[row, col] == 1 else CELL_WHITE
84+
rect = Rectangle((x, y), cell_size, cell_size, facecolor=face, edgecolor=INK_SOFT, linewidth=0.8)
8885
ax.add_patch(rect)
8986

90-
# Add clue number if this cell starts a word
9187
if (row, col) in numbers:
9288
ax.text(
93-
x + 0.08,
94-
y + cell_size - 0.08,
89+
x + 0.07,
90+
y + cell_size - 0.07,
9591
str(numbers[(row, col)]),
96-
fontsize=11,
92+
fontsize=9,
9793
fontweight="bold",
98-
color="#306998",
94+
color=NUM_COLOR,
9995
ha="left",
10096
va="top",
10197
)
10298

103-
# Set axis properties
99+
# Outer border frame around the full grid
100+
border = Rectangle(
101+
(0, 0), grid_size * cell_size, grid_size * cell_size, fill=False, edgecolor=INK, linewidth=2.5, zorder=10
102+
)
103+
ax.add_patch(border)
104+
105+
# Style
104106
ax.set_xlim(0, grid_size * cell_size)
105107
ax.set_ylim(0, grid_size * cell_size)
106108
ax.set_aspect("equal")
107109
ax.axis("off")
108-
109-
# Title
110-
ax.set_title("crossword-basic · matplotlib · pyplots.ai", fontsize=24, fontweight="bold", pad=20, color="#306998")
110+
ax.set_title("crossword-basic · python · matplotlib · anyplot.ai", fontsize=12, fontweight="medium", color=INK, pad=20)
111111

112112
plt.tight_layout()
113-
plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor="white")
113+
plt.savefig(f"plot-{THEME}.png", dpi=400, bbox_inches="tight", facecolor=PAGE_BG)

0 commit comments

Comments
 (0)