{"spec_id":"chessboard-pieces","library":"echarts","language":"javascript","code":"// anyplot.ai\n// chessboard-pieces: Chess Board with Pieces for Position Diagrams\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-09-01\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Scholar's Mate, final position after 1.e4 e5 2.Bc4 Nc6 3.Qh5 Nf6?? 4.Qxf7#\nconst FILES = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"];\nconst RANKS = [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\"];\n\nconst PIECES = {\n  a1: \"R\", b1: \"N\", c1: \"B\", e1: \"K\", g1: \"N\", h1: \"R\", c4: \"B\", f7: \"Q\",\n  a2: \"P\", b2: \"P\", c2: \"P\", d2: \"P\", e4: \"P\", f2: \"P\", g2: \"P\", h2: \"P\",\n  a8: \"r\", c8: \"b\", d8: \"q\", e8: \"k\", f8: \"b\", h8: \"r\", c6: \"n\", f6: \"n\",\n  a7: \"p\", b7: \"p\", c7: \"p\", d7: \"p\", e5: \"p\", g7: \"p\", h7: \"p\",\n};\n\nconst PIECE_GLYPH = {\n  K: \"♔\", Q: \"♕\", R: \"♖\", B: \"♗\", N: \"♘\", P: \"♙\",\n  k: \"♚\", q: \"♛\", r: \"♜\", b: \"♝\", n: \"♞\", p: \"♟\",\n};\n\n// Chess white/black is a stronger real-world color convention than the\n// abstract Imprint categorical order (Imprint semantic exception — see\n// default-style-guide.md), so piece color stays a fixed ivory/ink pair\n// independent of theme, exactly like the Imprint categorical hues stay\n// constant across light/dark.\nconst PIECE_WHITE_FILL = \"#F5F1E6\";\nconst PIECE_WHITE_STROKE = \"#1A1A17\";\nconst PIECE_BLACK_FILL = \"#1A1A17\";\nconst PIECE_BLACK_STROKE = \"#F5F1E6\";\n\n// Board squares: light square at h1, standard orientation (white at bottom).\n// Both square tones are alpha-blends over the page background rather than a\n// solid theme token, so the light/dark luminance ordering stays correct in\n// both themes (a solid t.elevatedBg is nearly black in dark mode, which would\n// read *darker* than the ochre-blended dark square). Dark squares use the\n// Imprint ochre hue — the palette's documented \"wood\" semantic anchor.\nconst LIGHT_SQUARE = \"rgba(245, 241, 230, 0.45)\";\nconst DARK_SQUARE = \"rgba(189, 130, 51, 0.45)\";\n\n// Checkmate emphasis: 4.Qxf7# — the queen delivers mate from f7, checking the\n// king on e8. A soft amber outline on those two squares calls out the\n// decisive move without adding chart junk (pieces + squares stay untouched).\nconst HIGHLIGHT_SQUARES = new Set([\"f7\", \"e8\"]);\n\nconst squares = [];\nFILES.forEach((file, col) => {\n  RANKS.forEach((rank, row) => {\n    const isLight = (col + row) % 2 === 1;\n    squares.push([file, rank, isLight ? 1 : 0]);\n  });\n});\n\nconst pieces = Object.keys(PIECES).map((square) => {\n  const file = square[0];\n  const rank = square[1];\n  const code = PIECES[square];\n  const isWhite = code === code.toUpperCase();\n  return [file, rank, PIECE_GLYPH[code], isWhite ? 1 : 0];\n});\n\nconst highlights = [...HIGHLIGHT_SQUARES].map((square) => [square[0], square[1]]);\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------\nconst title = \"Scholar's Mate · chessboard-pieces · javascript · echarts · anyplot.ai\";\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: title,\n    left: \"center\",\n    top: 40,\n    textStyle: { color: t.ink, fontSize: 21, fontWeight: 500 },\n  },\n  grid: {\n    left: \"center\",\n    top: 170,\n    width: 900,\n    height: 900,\n    show: true,\n    borderColor: t.inkSoft,\n    borderWidth: 2,\n    backgroundColor: \"transparent\",\n  },\n  xAxis: {\n    type: \"category\",\n    data: FILES,\n    position: \"bottom\",\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n    axisLabel: { color: t.inkSoft, fontSize: 22, margin: 20 },\n  },\n  yAxis: {\n    type: \"category\",\n    data: RANKS,\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { show: false },\n    axisLabel: { color: t.inkSoft, fontSize: 22, margin: 20 },\n  },\n  series: [\n    {\n      type: \"custom\",\n      coordinateSystem: \"cartesian2d\",\n      data: squares,\n      renderItem: (params, api) => {\n        const isLight = api.value(2) === 1;\n        const center = api.coord([api.value(0), api.value(1)]);\n        const size = api.size([1, 1]);\n        return {\n          type: \"rect\",\n          shape: {\n            x: center[0] - size[0] / 2,\n            y: center[1] - size[1] / 2,\n            width: size[0],\n            height: size[1],\n          },\n          style: {\n            fill: isLight ? LIGHT_SQUARE : DARK_SQUARE,\n            stroke: t.grid,\n            lineWidth: 1,\n          },\n          silent: true,\n        };\n      },\n      z: 1,\n    },\n    {\n      type: \"custom\",\n      coordinateSystem: \"cartesian2d\",\n      data: highlights,\n      renderItem: (params, api) => {\n        const center = api.coord([api.value(0), api.value(1)]);\n        const size = api.size([1, 1]);\n        const inset = Math.min(size[0], size[1]) * 0.06;\n        return {\n          type: \"rect\",\n          shape: {\n            x: center[0] - size[0] / 2 + inset,\n            y: center[1] - size[1] / 2 + inset,\n            width: size[0] - inset * 2,\n            height: size[1] - inset * 2,\n            r: inset,\n          },\n          style: {\n            fill: \"transparent\",\n            stroke: t.amber,\n            lineWidth: 3,\n          },\n          silent: true,\n        };\n      },\n      z: 2,\n    },\n    {\n      type: \"custom\",\n      coordinateSystem: \"cartesian2d\",\n      data: pieces,\n      renderItem: (params, api) => {\n        const isWhite = api.value(3) === 1;\n        const center = api.coord([api.value(0), api.value(1)]);\n        const size = api.size([1, 1]);\n        const fontSize = Math.min(size[0], size[1]) * 0.62;\n        return {\n          type: \"text\",\n          style: {\n            text: api.value(2),\n            x: center[0],\n            y: center[1],\n            textAlign: \"center\",\n            textVerticalAlign: \"middle\",\n            fontSize,\n            fontFamily: '\"Noto Sans Symbols 2\", \"DejaVu Sans\", \"Segoe UI Symbol\", sans-serif',\n            fill: isWhite ? PIECE_WHITE_FILL : PIECE_BLACK_FILL,\n            stroke: isWhite ? PIECE_WHITE_STROKE : PIECE_BLACK_STROKE,\n            lineWidth: isWhite ? 2.5 : 1.2,\n          },\n          silent: true,\n        };\n      },\n      z: 3,\n    },\n  ],\n});\n\nchart.on(\"finished\", () => {\n  window.__anyplotReady = true;\n});\n"}