add-puzzle-type
About
This Claude Skill scaffolds a new puzzle type across all 10+ integration points in the jigsawR package. It automates the creation of the core module, pipeline wiring, ggplot layers, configuration updates, Shiny app extensions, and a test suite. Use it when adding a completely new puzzle type to ensure no integration step is missed.
Quick Install
Claude Code
Recommendednpx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/add-puzzle-typeCopy and paste this command in Claude Code to install this skill
Documentation
Add Puzzle Type
Scaffold new puzzle type across pipeline integration points jigsawR.
Use When
- Add new puzzle type to pkg
- Follow 10-point pipeline checklist (CLAUDE.md)
- Nothing missed wiring new type end-to-end
In
- Required: New type name (lowercase, e.g.
"triangular") - Required: Geometry desc (piece shape/arrangement)
- Required: Needs external pkgs? (Suggests)
- Optional: Params beyond standard (grid, size, seed, tabsize, offset)
- Optional: Ref impl or algo source
Do
Step 1: Core Puzzle Module
Create R/<type>_puzzle.R:
#' Generate <type> puzzle pieces (internal)
#' @noRd
generate_<type>_pieces_internal <- function(params, seed) {
# 1. Initialize RNG state
# 2. Generate piece geometries
# 3. Build edge paths (SVG path data)
# 4. Compute adjacency
# 5. Return list: pieces, edges, adjacency, metadata
}
Follow pattern R/voronoi_puzzle.R or R/snic_puzzle.R.
→ Fn returns list: $pieces, $edges, $adjacency, $metadata.
If err: Compare return structure against generate_voronoi_pieces_internal() → missing elements or wrong types.
Step 2: Wire jigsawR_clean.R
Edit R/jigsawR_clean.R:
- Add
"<type>"tovalid_types - Type-specific param extraction
- Valid. logic type-specific constraints
- Filename prefix mapping (e.g.,
"<type>"->"<type>_")
# In valid_types
valid_types <- c("rectangular", "hexagonal", "concentric", "voronoi", "snic", "<type>")
→ generate_puzzle(type = "<type>") accepted, no "unknown type" err.
If err: Verify type string in valid_types exact, param extraction covers required type-specific args.
Step 3: Wire unified_piece_generation.R
Edit R/unified_piece_generation.R:
- Dispatch case
generate_pieces_internal() - Fusion handling if PILES notation
# In the switch/dispatch
"<type>" = generate_<type>_pieces_internal(params, seed)
→ Pieces generated on dispatch.
If err: Confirm dispatch string exact, generate_<type>_pieces_internal defined + exported.
Step 4: Wire piece_positioning.R
Edit R/piece_positioning.R:
Add positioning dispatch. Most use shared, some need custom.
→ apply_piece_positioning() handles new type no err, pieces at correct coords.
If err: Check if needs custom or reuse shared path. Add dispatch if default no apply.
Step 5: Wire unified_renderer.R
Edit R/unified_renderer.R:
- Render case
render_puzzle_svg() - Edge path fn:
get_<type>_edge_paths() - Piece name fn:
get_<type>_piece_name()
→ SVG out generated new type, correct outlines + edge paths.
If err: Verify get_<type>_edge_paths() returns valid SVG path, get_<type>_piece_name() unique IDs.
Step 6: Wire adjacency_api.R
Edit R/adjacency_api.R:
Neighbor dispatch → get_neighbors() + get_adjacency() work.
→ get_neighbors(result, piece_id) returns correct neighbors.
If err: Check dispatch returns correct structure. Test small grid, manually verify against geometry.
Step 7: ggpuzzle Geom Layer
Edit R/geom_puzzle.R:
geom_puzzle_<type>() using make_puzzle_layer():
#' @export
geom_puzzle_<type> <- function(mapping = NULL, data = NULL, ...) {
make_puzzle_layer(type = "<type>", mapping = mapping, data = data, ...)
}
→ ggplot() + geom_puzzle_<type>(aes(...)) renders no err.
If err: Verify make_puzzle_layer() correct type, geom exported via @export.
Step 8: Stat Dispatch
Edit R/stat_puzzle.R:
- Type-specific default params
- Dispatch case
compute_panel()
→ Stat layer computes geometry, produces expected polygons.
If err: compute_panel() dispatch returns df w/ x, y, group, piece_id, defaults sensible.
Step 9: DESCRIPTION
Edit DESCRIPTION:
- New type in Description text
- New pkgs →
Suggests: Collate:include new R file (alphabetical)
→ devtools::document() succeeds. No NOTE unlisted files.
If err: New R file in Collate: alphabetical, new Suggests pkgs spelled correct w/ ver constraints.
Step 10: config.yml
Edit inst/config.yml:
Defaults + constraints:
<type>:
grid:
default: [3, 3]
min: [2, 2]
max: [20, 20]
size:
default: [300, 300]
min: [100, 100]
max: [2000, 2000]
tabsize:
default: 20
min: 5
max: 50
# Add type-specific params here
→ Config valid YAML. Defaults → working puzzle via generate_puzzle().
If err: Validate yaml::yaml.load_file("inst/config.yml"). Defaults sensible (not too small/large).
Step 11: Shiny App
Edit inst/shiny-app/app.R:
- Add type → UI selector
- Conditional UI panels type-specific params
- Server-side generation
→ Shiny shows new type, generates on select.
If err: Type in choices of selector, conditional panel conditionalPanel(condition = "input.type == '<type>'"), server passes correct params.
Step 12: Test Suite
Create tests/testthat/test-<type>-puzzles.R:
test_that("<type> puzzle generates correct piece count", { ... })
test_that("<type> puzzle respects seed reproducibility", { ... })
test_that("<type> adjacency returns valid neighbors", { ... })
test_that("<type> fusion merges pieces correctly", { ... })
test_that("<type> geom layer renders without error", { ... })
test_that("<type> SVG output is well-formed", { ... })
test_that("<type> config constraints are enforced", { ... })
External dep → skip_if_not_installed().
→ All pass. No skips unless dep missing.
If err: Check each point individually. Common: missing dispatch → grep -rn "switch\|valid_types" R/.
Check
-
generate_puzzle(type = "<type>")produces valid out - All 10 points wired
-
devtools::test()passes new tests -
devtools::check()→ 0 err, 0 warn - Shiny renders new type
- Config constraints enforced (min/max valid.)
- Adjacency + fusion work
- ggpuzzle geom renders no err
-
devtools::document()succeeds (NAMESPACE updated)
Traps
- Missing dispatch: One of 10+ files forgotten → silent fail or "unknown type"
- strsplit neg nums:
paste(a, b, sep = "-")→"1--1". Use"|"+ split"\\|". cat()for out: Alwaysclilogging wrappers (log_info,log_warn, etc.)- Collate order: alphabetical or dep-ordered
- config.yml format: Valid YAML; test
yaml::yaml.load_file("inst/config.yml")
→
generate-puzzle— test new type after scaffoldrun-puzzle-tests— full suite verify integrationvalidate-piles-notation— test fusion w/ new typewrite-testthat-tests— general test patternswrite-roxygen-docs— document new geom fn
GitHub Repository
Related Skills
content-collections
MetaThis skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.
polymarket
MetaThis skill enables developers to build applications with the Polymarket prediction markets platform, including API integration for trading and market data. It also provides real-time data streaming via WebSocket to monitor live trades and market activity. Use it for implementing trading strategies or creating tools that process live market updates.
creating-opencode-plugins
MetaThis skill helps developers create OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It provides the plugin structure, event API specifications, and implementation patterns for JavaScript/TypeScript modules. Use it when you need to intercept, monitor, or extend the OpenCode AI assistant's lifecycle with custom event-driven logic.
sglang
MetaSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
