{"spec_id":"scatter-shot-chart","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// scatter-shot-chart: Basketball Shot Chart\n// Library: highcharts 12.6.0 | JavaScript 22.22.3\n// Quality: 88/100 | Created: 2026-06-21\n\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Fixed-seed LCG for deterministic data generation (no Math.random())\nlet _s = 42;\nfunction rnd() {\n  _s = (Math.imul(1664525, _s) + 1013904223) >>> 0;\n  return _s / 4294967296;\n}\nfunction rn() { // standard normal via Box-Muller\n  const u = rnd() || 1e-10;\n  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * rnd());\n}\n\n// --- Court geometry helper ---\nfunction arc(cx, cy, r, a0, a1, steps) {\n  const pts = [];\n  for (let i = 0; i <= steps; i++) {\n    const a = (a0 + (a1 - a0) * i / steps) * Math.PI / 180;\n    pts.push([+(cx + r * Math.cos(a)).toFixed(3), +(cy + r * Math.sin(a)).toFixed(3)]);\n  }\n  return pts;\n}\n\n// --- NBA half-court dimensions (feet, basket at origin) ---\nconst BL = -4.75;                                    // baseline Y\nconst HC = 42.25;                                    // half-court line Y\nconst CY = Math.sqrt(23.75 * 23.75 - 22 * 22);      // three-point corner Y ≈ 8.95\nconst A0 = Math.atan2(CY,  22) * 180 / Math.PI;     // arc start angle ≈ 22.1°\nconst A1 = Math.atan2(CY, -22) * 180 / Math.PI;     // arc end angle  ≈ 157.9°\n\n// Court line series template (neutral ink-soft colour so shots stand out)\nconst CL = {\n  type: 'line',\n  color: t.inkSoft,\n  lineWidth: 2,\n  marker: { enabled: false },\n  enableMouseTracking: false,\n  showInLegend: false,\n  states: { hover: { enabled: false } },\n};\n\nconst courtSeries = [\n  // Outer half-court boundary\n  { ...CL, data: [[-25, BL], [25, BL], [25, HC], [-25, HC], [-25, BL]] },\n  // Three-point corners (straight segments)\n  { ...CL, data: [[-22, BL], [-22, CY]] },\n  { ...CL, data: [[22, BL],  [22, CY]] },\n  // Three-point arc\n  { ...CL, data: arc(0, 0, 23.75, A0, A1, 60) },\n  // Paint / key rectangle (sides + free-throw end)\n  { ...CL, data: [[-8, BL], [-8, 15], [8, 15], [8, BL]] },\n  // Free-throw circle — upper half (solid, away from basket)\n  { ...CL, data: arc(0, 15, 6, 0, 180, 40) },\n  // Free-throw circle — lower half (dashed, toward basket)\n  { ...CL, dashStyle: 'Dash', data: arc(0, 15, 6, 180, 360, 40) },\n  // Restricted area (4 ft arc from basket)\n  { ...CL, data: arc(0, 0, 4, 0, 180, 30) },\n  // Backboard\n  { ...CL, lineWidth: 3, data: [[-3, -1.5], [3, -1.5]] },\n  // Basket rim (small circle)\n  { ...CL, lineWidth: 2, data: arc(0, 0, 0.75, 0, 360, 36) },\n];\n\n// --- Shot data generation: type drives marker shape (● 2PT · △ 3PT · ■ FT) ---\nconst shots = { made2: [], missed2: [], made3: [], missed3: [], madeFT: [], missedFT: [] };\n\nfunction addShots(n, cx, cy, sx, sy, pct, type) {\n  for (let i = 0; i < n; i++) {\n    const x = Math.max(-24, Math.min(24, cx + rn() * sx));\n    const y = Math.max(BL + 0.5, Math.min(41, cy + rn() * sy));\n    const key = (rnd() < pct ? 'made' : 'missed') + type;\n    shots[key].push([+x.toFixed(2), +y.toFixed(2)]);\n  }\n}\n\naddShots(80,  0,    3,    2.5, 2,   0.65, '2');  // under basket\naddShots(45, -13,  12,   3,   3,   0.42, '2');  // left mid-range\naddShots(45,  13,  12,   3,   3,   0.42, '2');  // right mid-range\naddShots(30,  0,   17,   3.5, 2,   0.44, '2');  // top of key\naddShots(40, -22,   4,   1.5, 2.5, 0.38, '3');  // left corner three\naddShots(40,  22,   4,   1.5, 2.5, 0.38, '3');  // right corner three\naddShots(35, -18,  22,   2,   2,   0.36, '3');  // left above-break three\naddShots(35,  18,  22,   2,   2,   0.36, '3');  // right above-break three\naddShots(35,  0,   24,   4,   2,   0.35, '3');  // top-of-arc three\naddShots(22,  0,   15,   0.4, 0.3, 0.78, 'FT'); // free throws (~78% NBA avg)\n\n// --- Chart ---\nHighcharts.chart(\"container\", {\n  chart: {\n    type: 'scatter',\n    backgroundColor: 'transparent',\n    animation: false,\n    style: { fontFamily: 'inherit' },\n    margin: [90, 30, 55, 30],\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: 'scatter-shot-chart · javascript · highcharts · anyplot.ai',\n    style: { color: t.ink, fontSize: '22px', fontWeight: '600' },\n  },\n  subtitle: {\n    text: '● 2-pointer · △ 3-pointer · ■ free-throw',\n    style: { color: t.inkSoft, fontSize: '13px' },\n  },\n  xAxis: {\n    min: -26, max: 26,\n    lineColor: 'transparent',\n    tickColor: 'transparent',\n    gridLineColor: 'transparent',\n    labels: { enabled: false },\n    title: { enabled: false },\n  },\n  yAxis: {\n    min: -6, max: 46,\n    lineColor: 'transparent',\n    tickColor: 'transparent',\n    gridLineColor: 'transparent',\n    labels: { enabled: false },\n    title: { text: null },\n  },\n  legend: {\n    enabled: true,\n    itemStyle: { color: t.inkSoft, fontSize: '14px', fontWeight: 'normal' },\n    itemHoverStyle: { color: t.ink },\n    symbolRadius: 4,\n    verticalAlign: 'bottom',\n    align: 'center',\n  },\n  tooltip: { enabled: false },\n  plotOptions: {\n    series: { animation: false },\n    scatter: {\n      marker: { radius: 5, lineWidth: 1, lineColor: t.pageBg },\n    },\n  },\n  series: [\n    ...courtSeries,\n    // Made shots — brand green (#009E73); shape encodes shot type; only first shown in legend\n    {\n      type: 'scatter', name: 'Made', data: shots.made2,\n      color: t.palette[0], opacity: 0.80,\n      marker: { radius: 5, symbol: 'circle', lineWidth: 1, lineColor: t.pageBg },\n    },\n    {\n      type: 'scatter', name: 'Made 3PT', data: shots.made3,\n      color: t.palette[0], opacity: 0.80, showInLegend: false,\n      marker: { radius: 5, symbol: 'triangle', lineWidth: 1, lineColor: t.pageBg },\n    },\n    {\n      type: 'scatter', name: 'Made FT', data: shots.madeFT,\n      color: t.palette[0], opacity: 0.80, showInLegend: false,\n      marker: { radius: 5, symbol: 'square', lineWidth: 1, lineColor: t.pageBg },\n    },\n    // Missed shots — matte red (#AE3030); shape encodes shot type; only first shown in legend\n    {\n      type: 'scatter', name: 'Missed', data: shots.missed2,\n      color: t.palette[4], opacity: 0.70,\n      marker: { radius: 5, symbol: 'circle', lineWidth: 1, lineColor: t.pageBg },\n    },\n    {\n      type: 'scatter', name: 'Missed 3PT', data: shots.missed3,\n      color: t.palette[4], opacity: 0.70, showInLegend: false,\n      marker: { radius: 5, symbol: 'triangle', lineWidth: 1, lineColor: t.pageBg },\n    },\n    {\n      type: 'scatter', name: 'Missed FT', data: shots.missedFT,\n      color: t.palette[4], opacity: 0.70, showInLegend: false,\n      marker: { radius: 5, symbol: 'square', lineWidth: 1, lineColor: t.pageBg },\n    },\n  ],\n});\n"}