{"spec_id":"maze-circular","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nmaze-circular: Circular Maze Puzzle\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-20\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\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\"\n\nBRAND = \"#009E73\"  # Okabe-Ito position 1 — START marker\nACCENT = \"#C475FD\"  # Okabe-Ito position 2 — GOAL marker\n\n# Maze parameters\nnp.random.seed(42)\nnum_rings = 7\nsectors_per_ring = [1]  # Center has 1 sector, each ring has more sectors\nfor ring in range(1, num_rings + 1):\n    sectors_per_ring.append(max(6, ring * 4))  # Increasing sectors per ring\n\n# Build adjacency graph for cells\n# Each cell is (ring, sector) where ring 0 is center\ncells = [(0, 0)]  # Center cell\nfor ring in range(1, num_rings + 1):\n    for sector in range(sectors_per_ring[ring]):\n        cells.append((ring, sector))\n\n# Find neighbors for each cell\nneighbors = {cell: [] for cell in cells}\n\n# Center connects to all sectors in ring 1\nfor sector in range(sectors_per_ring[1]):\n    neighbors[(0, 0)].append((1, sector))\n    neighbors[(1, sector)].append((0, 0))\n\n# Connections within same ring (adjacent sectors)\nfor ring in range(1, num_rings + 1):\n    n_sectors = sectors_per_ring[ring]\n    for sector in range(n_sectors):\n        next_sector = (sector + 1) % n_sectors\n        neighbors[(ring, sector)].append((ring, next_sector))\n        neighbors[(ring, next_sector)].append((ring, sector))\n\n# Connections between adjacent rings (radial passages)\nfor ring in range(1, num_rings):\n    inner_sectors = sectors_per_ring[ring]\n    outer_sectors = sectors_per_ring[ring + 1]\n    ratio = outer_sectors / inner_sectors\n    for inner_sector in range(inner_sectors):\n        outer_start = int(inner_sector * ratio)\n        outer_end = int((inner_sector + 1) * ratio)\n        for outer_sector in range(outer_start, outer_end):\n            neighbors[(ring, inner_sector)].append((ring + 1, outer_sector))\n            neighbors[(ring + 1, outer_sector)].append((ring, inner_sector))\n\n# DFS maze generation\nvisited = set()\npassages = set()  # Edges in the spanning tree\nstack = [(0, 0)]\nvisited.add((0, 0))\n\nwhile stack:\n    current = stack[-1]\n    unvisited_neighbors = [n for n in neighbors[current] if n not in visited]\n\n    if unvisited_neighbors:\n        next_cell = unvisited_neighbors[np.random.randint(len(unvisited_neighbors))]\n        visited.add(next_cell)\n        passages.add((current, next_cell))\n        passages.add((next_cell, current))\n        stack.append(next_cell)\n    else:\n        stack.pop()\n\n# Geometry parameters\nring_width = 1.0\ncenter_radius = 0.5\nentrance_sector = 0\nn_arc_points = 100  # Increased for smoother arcs\n\n# Collect wall segments — outer ring gets heavier weight for visual depth\nwall_segments = []\n\n# Draw arc walls (circular walls between rings)\nfor ring in range(1, num_rings + 1):\n    outer_r = center_radius + ring * ring_width\n    n_sectors = sectors_per_ring[ring]\n    lw = 3.0 if ring == num_rings else 1.5  # Outer boundary emphasized\n\n    for sector in range(n_sectors):\n        # Check if there's a passage to the outer ring\n        if ring < num_rings:\n            has_passage = False\n            outer_sectors_count = sectors_per_ring[ring + 1]\n            ratio = outer_sectors_count / n_sectors\n            outer_start = int(sector * ratio)\n            outer_end = int((sector + 1) * ratio)\n            for outer_sector in range(outer_start, outer_end):\n                if ((ring, sector), (ring + 1, outer_sector)) in passages:\n                    has_passage = True\n                    break\n            if has_passage:\n                continue  # Don't draw outer wall — there's a passage\n\n        # Skip entrance opening on outer wall\n        if ring == num_rings and sector == entrance_sector:\n            continue\n\n        # Draw arc for outer wall of this sector\n        theta_start = 2 * np.pi * sector / n_sectors - np.pi / 2\n        theta_end = 2 * np.pi * (sector + 1) / n_sectors - np.pi / 2\n        theta_vals = np.linspace(theta_start, theta_end, n_arc_points)\n        for i in range(len(theta_vals) - 1):\n            x1, y1 = outer_r * np.cos(theta_vals[i]), outer_r * np.sin(theta_vals[i])\n            x2, y2 = outer_r * np.cos(theta_vals[i + 1]), outer_r * np.sin(theta_vals[i + 1])\n            wall_segments.append({\"x\": x1, \"y\": y1, \"xend\": x2, \"yend\": y2, \"lw\": lw})\n\n# Draw radial walls (between sectors in same ring)\nfor ring in range(1, num_rings + 1):\n    n_sectors = sectors_per_ring[ring]\n    inner_r = center_radius + (ring - 1) * ring_width if ring > 1 else center_radius\n    outer_r = center_radius + ring * ring_width\n    lw = 3.0 if ring == num_rings else 1.5  # Outer boundary emphasized\n\n    for sector in range(n_sectors):\n        next_sector = (sector + 1) % n_sectors\n        if ((ring, sector), (ring, next_sector)) in passages:\n            continue  # Don't draw wall — there's a passage\n\n        theta = 2 * np.pi * (sector + 1) / n_sectors - np.pi / 2\n        x1, y1 = inner_r * np.cos(theta), inner_r * np.sin(theta)\n        x2, y2 = outer_r * np.cos(theta), outer_r * np.sin(theta)\n        wall_segments.append({\"x\": x1, \"y\": y1, \"xend\": x2, \"yend\": y2, \"lw\": lw})\n\n# Draw inner walls for ring 1 connecting to center\ninner_r = center_radius\nfor sector in range(sectors_per_ring[1]):\n    if ((0, 0), (1, sector)) not in passages:\n        n_inner_sectors = sectors_per_ring[1]\n        theta_start = 2 * np.pi * sector / n_inner_sectors - np.pi / 2\n        theta_end = 2 * np.pi * (sector + 1) / n_inner_sectors - np.pi / 2\n        theta_vals = np.linspace(theta_start, theta_end, n_arc_points // 2)\n        for i in range(len(theta_vals) - 1):\n            x1, y1 = inner_r * np.cos(theta_vals[i]), inner_r * np.sin(theta_vals[i])\n            x2, y2 = inner_r * np.cos(theta_vals[i + 1]), inner_r * np.sin(theta_vals[i + 1])\n            wall_segments.append({\"x\": x1, \"y\": y1, \"xend\": x2, \"yend\": y2, \"lw\": 1.5})\n\ndf_walls = pd.DataFrame(wall_segments)\n\n# Markers for start and goal\nouter_r = center_radius + num_rings * ring_width\nentrance_n_sectors = sectors_per_ring[num_rings]\nentrance_theta = (\n    2 * np.pi * entrance_sector / entrance_n_sectors + 2 * np.pi * (entrance_sector + 1) / entrance_n_sectors\n) / 2 - np.pi / 2\nstart_x = (outer_r + 0.8) * np.cos(entrance_theta)\nstart_y = (outer_r + 0.8) * np.sin(entrance_theta)\n\ndf_markers = pd.DataFrame(\n    {\n        \"x\": [start_x, 0],\n        \"y\": [start_y, 0.15],  # GOAL shifted up to clear innermost ring walls\n        \"label\": [\"START\", \"GOAL\"],\n        \"color\": [BRAND, ACCENT],\n        \"info\": [f\"Entry — outer ring, sector {entrance_sector}\", f\"Navigate {num_rings} rings, seed 42\"],\n    }\n)\n\n# Plot — wall size aesthetic drives visual hierarchy; tooltips + ggtb() use lets_plot interactivity\nplot_radius = outer_r + 1.5\nplot = (\n    ggplot()\n    + geom_segment(aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\", size=\"lw\"), data=df_walls, color=INK, show_legend=False)\n    + scale_size_identity()\n    + geom_text(\n        aes(x=\"x\", y=\"y\", label=\"label\", color=\"color\"),\n        data=df_markers,\n        size=8,\n        fontface=\"bold\",\n        show_legend=False,\n        tooltips=layer_tooltips().line(\"@label\").line(\"@info\"),\n    )\n    + scale_color_identity()\n    + coord_fixed(ratio=1, xlim=(-plot_radius, plot_radius), ylim=(-plot_radius, plot_radius))\n    + theme_void()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        plot_title=element_text(size=16, color=INK, hjust=0.5),\n        plot_margin=[40, 40, 40, 40],\n    )\n    + labs(title=\"maze-circular · python · letsplot · anyplot.ai\")\n    + ggsize(600, 600)\n    + ggtb()\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}