{"spec_id":"chessboard-pieces","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nchessboard-pieces: Chess Board with Pieces for Position Diagrams\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 86/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, Label\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\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\"\n\n# Unicode chess piece symbols\nPIECE_SYMBOLS = {\n    \"K\": \"♔\",\n    \"Q\": \"♕\",\n    \"R\": \"♖\",\n    \"B\": \"♗\",\n    \"N\": \"♘\",\n    \"P\": \"♙\",  # White\n    \"k\": \"♚\",\n    \"q\": \"♛\",\n    \"r\": \"♜\",\n    \"b\": \"♝\",\n    \"n\": \"♞\",\n    \"p\": \"♟\",  # Black\n}\n\n# Scholar's Mate position - a famous quick checkmate\npieces = {\n    # White pieces\n    \"a1\": \"R\",\n    \"b1\": \"N\",\n    \"c1\": \"B\",\n    \"d1\": \"Q\",\n    \"e1\": \"K\",\n    \"f1\": \"B\",\n    \"h1\": \"R\",\n    \"a2\": \"P\",\n    \"b2\": \"P\",\n    \"c2\": \"P\",\n    \"d2\": \"P\",\n    \"f2\": \"P\",\n    \"g2\": \"P\",\n    \"h2\": \"P\",\n    \"e4\": \"P\",\n    \"f3\": \"N\",\n    \"f7\": \"Q\",  # Moved pieces including checkmate queen\n    # Black pieces\n    \"a8\": \"r\",\n    \"b8\": \"n\",\n    \"c8\": \"b\",\n    \"d8\": \"q\",\n    \"f8\": \"b\",\n    \"g8\": \"n\",\n    \"h8\": \"r\",\n    \"a7\": \"p\",\n    \"b7\": \"p\",\n    \"c7\": \"p\",\n    \"d7\": \"p\",\n    \"g7\": \"p\",\n    \"h7\": \"p\",\n    \"e5\": \"p\",\n    \"e8\": \"k\",  # King in check\n}\n\n# Create figure with square aspect ratio for chess board\np = figure(\n    width=3600,\n    height=3600,\n    title=\"Scholar's Mate · chessboard-pieces · bokeh · anyplot.ai\",\n    x_range=(-0.5, 7.5),\n    y_range=(-0.5, 7.5),\n    tools=\"pan,wheel_zoom,reset\",\n    toolbar_location=\"right\",\n)\n\n# Draw the chess board squares\nlight_squares_x = []\nlight_squares_y = []\ndark_squares_x = []\ndark_squares_y = []\n\nfor row in range(8):\n    for col in range(8):\n        is_light = (row + col) % 2 == 1  # h1 (7, 0) should be light\n        if is_light:\n            light_squares_x.append(col)\n            light_squares_y.append(row)\n        else:\n            dark_squares_x.append(col)\n            dark_squares_y.append(row)\n\n# Draw squares using rect glyphs\nlight_source = ColumnDataSource(data={\"x\": light_squares_x, \"y\": light_squares_y})\ndark_source = ColumnDataSource(data={\"x\": dark_squares_x, \"y\": dark_squares_y})\n\np.rect(x=\"x\", y=\"y\", width=1, height=1, source=light_source, fill_color=\"#F0D9B5\", line_color=\"#B58863\", line_width=1)\np.rect(x=\"x\", y=\"y\", width=1, height=1, source=dark_source, fill_color=\"#B58863\", line_color=\"#B58863\", line_width=1)\n\n# Add pieces using Label annotations with Unicode symbols\nfor square, piece in pieces.items():\n    col = ord(square[0]) - ord(\"a\")\n    row = int(square[1]) - 1\n    symbol = PIECE_SYMBOLS[piece]\n\n    # White pieces get a slight shadow effect for visibility on light squares\n    piece_color = \"#1a1a1a\" if piece.islower() else \"#FFFFFF\"\n\n    # Add piece label\n    label = Label(\n        x=col,\n        y=row,\n        text=symbol,\n        text_font_size=\"110pt\",\n        text_align=\"center\",\n        text_baseline=\"middle\",\n        text_color=piece_color,\n        x_offset=0,\n        y_offset=0,\n    )\n    p.add_layout(label)\n\n# Add file labels (a-h)\nfiles = \"abcdefgh\"\nfor i, f in enumerate(files):\n    p.add_layout(\n        Label(x=i, y=-0.3, text=f, text_font_size=\"36pt\", text_align=\"center\", text_baseline=\"top\", text_color=INK_SOFT)\n    )\n\n# Add rank labels (1-8)\nfor i in range(8):\n    p.add_layout(\n        Label(\n            x=-0.3,\n            y=i,\n            text=str(i + 1),\n            text_font_size=\"36pt\",\n            text_align=\"right\",\n            text_baseline=\"middle\",\n            text_color=INK_SOFT,\n        )\n    )\n\n# Style the plot\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\np.outline_line_width = 4\n\np.title.text_font_size = \"48pt\"\np.title.align = \"center\"\np.title.text_color = INK\n\n# Hide axes and grid\np.axis.visible = False\np.grid.visible = False\n\n# Add board border\np.rect(x=3.5, y=3.5, width=8, height=8, fill_alpha=0, line_color=INK_SOFT, line_width=8)\n\n# Save HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome — Selenium 4 / Selenium Manager\nW, H = 3600, 3600\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)  # let bokeh's JS render the canvas\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}