{"spec_id":"maze-printable","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// maze-printable: Printable Maze Puzzle\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-05\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Config ------------------------------------------------------------\nconst WIDTH = 24;\nconst HEIGHT = 24;\nconst SEED = 1337;\n\n// --- Deterministic RNG (tiny LCG — no seeded RNG exists in-browser) -----\nfunction makeRng(seed) {\n  let state = seed >>> 0;\n  return function rng() {\n    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\n\n// --- Maze generation: recursive backtracker (guarantees a single, --------\n// unique path between any two cells — a perfect maze / spanning tree) ----\nfunction generateMaze(width, height, rng) {\n  const cells = [];\n  for (let r = 0; r < height; r++) {\n    const row = [];\n    for (let c = 0; c < width; c++) {\n      row.push({ top: true, right: true, bottom: true, left: true });\n    }\n    cells.push(row);\n  }\n\n  const DIRS = [\n    { dr: -1, dc: 0, wall: \"top\", opposite: \"bottom\" },\n    { dr: 0, dc: 1, wall: \"right\", opposite: \"left\" },\n    { dr: 1, dc: 0, wall: \"bottom\", opposite: \"top\" },\n    { dr: 0, dc: -1, wall: \"left\", opposite: \"right\" },\n  ];\n\n  const visited = Array.from({ length: height }, () => new Array(width).fill(false));\n  const stack = [[0, 0]];\n  visited[0][0] = true;\n\n  while (stack.length) {\n    const [r, c] = stack[stack.length - 1];\n    const candidates = [];\n    for (const d of DIRS) {\n      const nr = r + d.dr;\n      const nc = c + d.dc;\n      if (nr >= 0 && nr < height && nc >= 0 && nc < width && !visited[nr][nc]) {\n        candidates.push({ nr, nc, wall: d.wall, opposite: d.opposite });\n      }\n    }\n    if (candidates.length === 0) {\n      stack.pop();\n      continue;\n    }\n    const pick = candidates[Math.floor(rng() * candidates.length)];\n    cells[r][c][pick.wall] = false;\n    cells[pick.nr][pick.nc][pick.opposite] = false;\n    visited[pick.nr][pick.nc] = true;\n    stack.push([pick.nr, pick.nc]);\n  }\n\n  return cells;\n}\n\n// --- Walls -> a flat list of [x0,y0,x1,y1] segments (each wall is drawn ---\n// exactly once: every cell contributes its own top + left edge, and the\n// outer grid contributes the closing right + bottom border). Segments are\n// drawn as individual SVG paths via the chart renderer rather than as\n// series data, since a maze is vector art, not a plotted series.\nfunction buildWallSegments(cells, width, height) {\n  const segments = [];\n  const segment = (x0, y0, x1, y1) => segments.push([x0, y0, x1, y1]);\n\n  for (let r = 0; r < height; r++) {\n    for (let c = 0; c < width; c++) {\n      const cell = cells[r][c];\n      const yTop = height - r;\n      const yBottom = height - r - 1;\n      if (cell.top) segment(c, yTop, c + 1, yTop);\n      if (cell.left) segment(c, yBottom, c, yTop);\n      if (r === height - 1 && cell.bottom) segment(c, yBottom, c + 1, yBottom);\n      if (c === width - 1 && cell.right) segment(c + 1, yBottom, c + 1, yTop);\n    }\n  }\n\n  return segments;\n}\n\nconst rng = makeRng(SEED);\nconst maze = generateMaze(WIDTH, HEIGHT, rng);\nconst wallData = buildWallSegments(maze, WIDTH, HEIGHT);\n\nconst startPoint = { x: 0.5, y: HEIGHT - 0.5 };\nconst goalPoint = { x: WIDTH - 0.5, y: 0.5 };\n\n// --- Chart ---------------------------------------------------------------\n// Decorative outset frame drawn once the axes are laid out: a thin rounded\n// border around the maze's bounding box, offset outward for breathing room.\n// Purely chrome (never overlaps a wall segment), so it cannot hint at the\n// solution path.\nconst FRAME_PAD = 14;\nconst FRAME_RADIUS = 10;\n\n// Soft drop-shadow applied to the S/G marker graphics after they render,\n// giving the two focal points a subtle lift off the flat maze plane — a\n// second distinctive use of the SVG renderer beyond the wall vector art.\nconst MARKER_SHADOW = { color: t.ink, opacity: 0.28, width: 6 };\n\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"line\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    spacing: [18, 18, 18, 18],\n    style: { fontFamily: \"inherit\" },\n    events: {\n      load: function drawWalls() {\n        const chart = this;\n        const xAxis = chart.xAxis[0];\n        const yAxis = chart.yAxis[0];\n\n        const xPix0 = xAxis.toPixels(0);\n        const xPix1 = xAxis.toPixels(WIDTH);\n        const yPixTop = yAxis.toPixels(HEIGHT);\n        const yPixBottom = yAxis.toPixels(0);\n        chart.renderer\n          .rect(\n            xPix0 - FRAME_PAD,\n            yPixTop - FRAME_PAD,\n            xPix1 - xPix0 + 2 * FRAME_PAD,\n            yPixBottom - yPixTop + 2 * FRAME_PAD,\n            FRAME_RADIUS\n          )\n          .attr({ \"stroke-width\": 2, stroke: t.inkSoft, fill: \"none\", zIndex: 1 })\n          .add();\n\n        wallData.forEach(([x0, y0, x1, y1]) => {\n          chart.renderer\n            .path([\"M\", xAxis.toPixels(x0), yAxis.toPixels(y0), \"L\", xAxis.toPixels(x1), yAxis.toPixels(y1)])\n            .attr({ \"stroke-width\": 5, stroke: t.ink, \"stroke-linecap\": \"square\", zIndex: 5 })\n            .add();\n        });\n\n        chart.series.forEach((series) => {\n          series.points.forEach((point) => {\n            if (point.graphic) point.graphic.shadow(MARKER_SHADOW);\n          });\n        });\n\n        window.__anyplotReady = true;\n      },\n    },\n  },\n  credits: { enabled: false },\n  tooltip: { enabled: false },\n  title: {\n    text: \"maze-printable · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"700\", letterSpacing: \"0.2px\" },\n  },\n  subtitle: {\n    text: `${WIDTH}×${HEIGHT} grid · seed ${SEED} · start (S) to goal (G)`,\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    min: 0,\n    max: WIDTH,\n    lineWidth: 0,\n    tickLength: 0,\n    gridLineWidth: 0,\n    startOnTick: false,\n    endOnTick: false,\n    minPadding: 0,\n    maxPadding: 0,\n    labels: { enabled: false },\n    title: { text: null },\n  },\n  yAxis: {\n    min: 0,\n    max: HEIGHT,\n    lineWidth: 0,\n    tickLength: 0,\n    gridLineWidth: 0,\n    startOnTick: false,\n    endOnTick: false,\n    minPadding: 0,\n    maxPadding: 0,\n    labels: { enabled: false },\n    title: { text: null },\n  },\n  legend: { enabled: false },\n  plotOptions: {\n    series: { animation: false, enableMouseTracking: false, states: { hover: { enabled: false } } },\n  },\n  series: [\n    {\n      type: \"scatter\",\n      name: \"Start\",\n      data: [startPoint],\n      color: t.palette[0],\n      marker: { radius: 15, symbol: \"circle\", lineColor: t.ink, lineWidth: 2 },\n      dataLabels: {\n        enabled: true,\n        format: \"S\",\n        style: { color: \"#FFFFFF\", fontSize: \"16px\", fontWeight: \"700\", textOutline: \"none\" },\n      },\n    },\n    {\n      type: \"scatter\",\n      name: \"Goal\",\n      data: [goalPoint],\n      color: t.palette[4],\n      marker: { radius: 15, symbol: \"circle\", lineColor: t.ink, lineWidth: 2 },\n      dataLabels: {\n        enabled: true,\n        format: \"G\",\n        style: { color: \"#FFFFFF\", fontSize: \"16px\", fontWeight: \"700\", textOutline: \"none\" },\n      },\n    },\n  ],\n});\n"}