{"spec_id":"maze-circular","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' maze-circular: Circular Maze Puzzle\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 90/100 | Created: 2026-05-20\n\nlibrary(ggplot2)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens\nTHEME    <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG  <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK      <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nBAND_ALT <- if (THEME == \"light\") \"#EDEAE0\" else \"#242420\"\n\n# --- Maze parameters\nRINGS        <- 7\nSECTORS      <- 12\nANGLE_STEP   <- 2 * pi / SECTORS\nCENTER_R     <- 0.5\nENTRY_SECTOR <- 1\n\nring_in  <- function(r) CENTER_R + (r - 1)\nring_out <- function(r) CENTER_R + r\nOUTER_R  <- ring_out(RINGS)\n\nsec_angle <- function(s, frac = 0) (s - 1 + frac) * ANGLE_STEP - pi / 2\n\n# --- Iterative DFS maze generation\narc_open    <- matrix(FALSE, nrow = RINGS - 1, ncol = SECTORS)\nradial_open <- matrix(FALSE, nrow = RINGS,     ncol = SECTORS)\nvisited     <- matrix(FALSE, nrow = RINGS,     ncol = SECTORS)\n\nvisited[RINGS, ENTRY_SECTOR] <- TRUE\ndfs_stack <- list(list(r = RINGS, s = ENTRY_SECTOR))\n\nwhile (length(dfs_stack) > 0) {\n    top <- dfs_stack[[length(dfs_stack)]]\n    r   <- top$r\n    s   <- top$s\n    sl  <- if (s == 1) SECTORS else s - 1\n    sr  <- if (s == SECTORS) 1 else s + 1\n\n    cands <- character(0)\n    if (r > 1     && !visited[r - 1, s]) cands <- c(cands, \"in\")\n    if (r < RINGS && !visited[r + 1, s]) cands <- c(cands, \"out\")\n    if (!visited[r, sl])                  cands <- c(cands, \"left\")\n    if (!visited[r, sr])                  cands <- c(cands, \"right\")\n\n    if (length(cands) == 0) {\n        dfs_stack <- dfs_stack[-length(dfs_stack)]\n        next\n    }\n\n    dir <- sample(cands, 1)\n\n    if (dir == \"in\") {\n        arc_open[r - 1, s] <- TRUE\n        visited[r - 1, s]  <- TRUE\n        dfs_stack <- c(dfs_stack, list(list(r = r - 1, s = s)))\n    } else if (dir == \"out\") {\n        arc_open[r, s]     <- TRUE\n        visited[r + 1, s]  <- TRUE\n        dfs_stack <- c(dfs_stack, list(list(r = r + 1, s = s)))\n    } else if (dir == \"left\") {\n        radial_open[r, sl] <- TRUE\n        visited[r, sl]     <- TRUE\n        dfs_stack <- c(dfs_stack, list(list(r = r, s = sl)))\n    } else {\n        radial_open[r, s]  <- TRUE\n        visited[r, sr]     <- TRUE\n        dfs_stack <- c(dfs_stack, list(list(r = r, s = sr)))\n    }\n}\n\ncenter_sector <- sample(seq_len(SECTORS), 1)\n\n# --- Alternating ring band shading (visual depth: even rings tinted)\nband_data <- NULL\nfor (r in seq_len(RINGS)) {\n    if (r %% 2 == 0) {\n        angs <- seq(0, 2 * pi, length.out = 120)\n        ri   <- ring_in(r)\n        ro   <- ring_out(r)\n        df   <- data.frame(\n            x    = c(ro * cos(angs), ri * cos(rev(angs))),\n            y    = c(ro * sin(angs), ri * sin(rev(angs))),\n            ring = r\n        )\n        band_data <- rbind(band_data, df)\n    }\n}\n\n# --- Outer boundary (heavier stroke — visual frame, drawn separately)\nouter_a0   <- sec_angle(ENTRY_SECTOR, 0)\nouter_a1   <- sec_angle(ENTRY_SECTOR, 1)\nouter_angs <- seq(outer_a1, outer_a0 + 2 * pi, length.out = 300)\nouter_wall  <- data.frame(x = OUTER_R * cos(outer_angs), y = OUTER_R * sin(outer_angs))\n\n# --- Accumulate inner wall geometry (center boundary + internal arcs + radial walls)\nwalls <- NULL\nseg   <- 0L\n\nappend_seg <- function(x, y) {\n    seg   <<- seg + 1L\n    walls <<- rbind(walls, data.frame(x = x, y = y, seg = seg))\n}\n\n# Center boundary with goal gap\n{\n    a0   <- sec_angle(center_sector, 0)\n    a1   <- sec_angle(center_sector, 1)\n    angs <- seq(a1, a0 + 2 * pi, length.out = 180)\n    append_seg(CENTER_R * cos(angs), CENTER_R * sin(angs))\n}\n\n# Internal ring boundary arcs (where wall is closed)\nfor (r in seq_len(RINGS - 1)) {\n    wr <- ring_out(r)\n    for (s in seq_len(SECTORS)) {\n        if (!arc_open[r, s]) {\n            angs <- seq(sec_angle(s, 0), sec_angle(s, 1), length.out = 20)\n            append_seg(wr * cos(angs), wr * sin(angs))\n        }\n    }\n}\n\n# Radial walls (where wall is closed)\nfor (r in seq_len(RINGS)) {\n    ri <- ring_in(r)\n    ro <- ring_out(r)\n    for (s in seq_len(SECTORS)) {\n        if (!radial_open[r, s]) {\n            angle <- sec_angle(s, 1)\n            append_seg(c(ri, ro) * cos(angle), c(ri, ro) * sin(angle))\n        }\n    }\n}\n\n# --- Goal disc and entry label\ngoal_r    <- CENTER_R * 0.65\ngoal_angs <- seq(0, 2 * pi, length.out = 80)\ngoal_pts  <- data.frame(\n    x = goal_r * cos(goal_angs),\n    y = goal_r * sin(goal_angs)\n)\n\nentry_mid <- sec_angle(ENTRY_SECTOR, 0.5)\nlabel_r   <- OUTER_R * 1.07\nentry_lx  <- label_r * cos(entry_mid)\nentry_ly  <- label_r * sin(entry_mid)\n\nextent <- OUTER_R * 1.20\n\n# --- Plot\np <- ggplot() +\n    # Alternating ring bands for visual depth\n    geom_polygon(\n        data = band_data,\n        aes(x = x, y = y, group = ring),\n        fill = BAND_ALT, color = NA\n    ) +\n    # Inner walls and center boundary\n    geom_path(\n        data = walls,\n        aes(x = x, y = y, group = factor(seg)),\n        color = INK, linewidth = 0.9, lineend = \"round\"\n    ) +\n    # Outer boundary with heavier stroke for visual frame\n    geom_path(\n        data = outer_wall,\n        aes(x = x, y = y),\n        color = INK, linewidth = 1.3, lineend = \"round\"\n    ) +\n    # Goal disc\n    geom_polygon(\n        data = goal_pts, aes(x = x, y = y),\n        fill = \"#009E73\", color = NA\n    ) +\n    annotate(\"text\", x = 0, y = 0,\n             label = \"★\", color = PAGE_BG, size = 2.5) +\n    annotate(\"text\", x = entry_lx, y = entry_ly,\n             label = \"START\", color = INK_SOFT, size = 2.6,\n             fontface = \"bold\", hjust = 0.5, vjust = 0.5) +\n    coord_equal(\n        xlim = c(-extent, extent),\n        ylim = c(-extent, extent)\n    ) +\n    labs(\n        title   = \"maze-circular · r · ggplot2 · anyplot.ai\",\n        caption = \"rings: 7 · sectors: 12 · medium difficulty\"\n    ) +\n    theme_void(base_size = 8) +\n    theme(\n        plot.background = element_rect(fill = PAGE_BG, color = NA),\n        plot.title = element_text(\n            color = INK, size = 12, hjust = 0.5,\n            margin = ggplot2::margin(t = 8, b = 6)\n        ),\n        plot.caption = element_text(\n            color = INK_SOFT, size = 7, hjust = 0.5,\n            margin = ggplot2::margin(t = 6, b = 6)\n        ),\n        plot.margin = ggplot2::margin(8, 8, 8, 8)\n    )\n\n# --- Save\nggsave(\n    filename = sprintf(\"plot-%s.png\", THEME),\n    plot     = p,\n    device   = ragg::agg_png,\n    width    = 6,\n    height   = 6,\n    units    = \"in\",\n    dpi      = 400\n)\n"}