{"spec_id":"crossword-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\ncrossword-basic: Crossword Puzzle Grid\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-20\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.patches import Rectangle\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nCELL_WHITE = \"#FFFFFF\" if THEME == \"light\" else \"#D0CEC8\"\nCELL_BLACK = \"#1A1A17\" if THEME == \"light\" else \"#050503\"\nNUM_COLOR = \"#009E73\"  # Okabe-Ito position 1 (brand green) — sole colored element\n\n# Data: 15x15 crossword grid with 180-degree rotational symmetry\n# 0 = white (entry cell), 1 = black (blocked cell)\ngrid_size = 15\ngrid = np.zeros((grid_size, grid_size), dtype=int)\n\nblack_cells = [\n    (0, 4),\n    (0, 10),\n    (1, 4),\n    (1, 10),\n    (2, 7),\n    (3, 0),\n    (3, 5),\n    (3, 6),\n    (3, 11),\n    (4, 0),\n    (4, 1),\n    (4, 8),\n    (4, 9),\n    (4, 14),\n    (5, 3),\n    (5, 12),\n    (6, 6),\n    (6, 7),\n    (6, 8),\n    (7, 2),\n    (7, 4),\n    (7, 10),\n    (7, 12),\n]\n\nfor r, c in black_cells:\n    grid[r, c] = 1\n    grid[grid_size - 1 - r, grid_size - 1 - c] = 1\n\n# Calculate clue numbers (cells that start an across or down word)\nnumbers = {}\nclue_num = 1\n\nfor row in range(grid_size):\n    for col in range(grid_size):\n        if grid[row, col] == 1:\n            continue\n        starts_across = (col == 0 or grid[row, col - 1] == 1) and (col < grid_size - 1 and grid[row, col + 1] == 0)\n        starts_down = (row == 0 or grid[row - 1, col] == 1) and (row < grid_size - 1 and grid[row + 1, col] == 0)\n        if starts_across or starts_down:\n            numbers[(row, col)] = clue_num\n            clue_num += 1\n\n# Plot — square format (2400×2400 px) suits the 1:1 crossword grid\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\ncell_size = 1.0\n\nfor row in range(grid_size):\n    for col in range(grid_size):\n        x = col * cell_size\n        y = (grid_size - 1 - row) * cell_size  # flip y so row 0 is at top\n\n        face = CELL_BLACK if grid[row, col] == 1 else CELL_WHITE\n        rect = Rectangle((x, y), cell_size, cell_size, facecolor=face, edgecolor=INK_SOFT, linewidth=0.8)\n        ax.add_patch(rect)\n\n        if (row, col) in numbers:\n            ax.text(\n                x + 0.07,\n                y + cell_size - 0.07,\n                str(numbers[(row, col)]),\n                fontsize=9,\n                fontweight=\"bold\",\n                color=NUM_COLOR,\n                ha=\"left\",\n                va=\"top\",\n            )\n\n# Outer border frame around the full grid\nborder = Rectangle(\n    (0, 0), grid_size * cell_size, grid_size * cell_size, fill=False, edgecolor=INK, linewidth=2.5, zorder=10\n)\nax.add_patch(border)\n\n# Style\nax.set_xlim(0, grid_size * cell_size)\nax.set_ylim(0, grid_size * cell_size)\nax.set_aspect(\"equal\")\nax.axis(\"off\")\nax.set_title(\"crossword-basic · python · matplotlib · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=20)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}