Random Puzzles

Unique random shapes with configurable complexity

Overview

Random puzzles generate unique, irregular piece shapes using a randomized approach. Unlike the regular patterns of rectangular or hexagonal puzzles, random puzzles create organic, unpredictable boundaries that make each puzzle truly one-of-a-kind.

Piece Count Variations

make_rand_pieces <- function(n) {
  ggplot() +
    geom_puzzle_random(
      aes(fill = after_stat(piece_id)),
      n_interior = n,
      seed = 42
    ) +
    scale_fill_viridis_c(option = "inferno", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0(n, " pieces"))
}

print(make_rand_pieces(5) + make_rand_pieces(10) + make_rand_pieces(20))
Figure 1
# Generate piece count previews in a grid
counts <- c(5, 10, 20)
items <- lapply(counts, function(n) {
  generate_puzzle(type = "random", grid = c(n), size = c(200, 200), seed = 42, fill_palette = "inferno")
})
render_puzzle_grid(items, ncol = 3, labels = paste(counts, "pieces"))
5 pieces
10 pieces
20 pieces
Figure 2

Corner Variations

The n_corner parameter controls the complexity of each piece’s shape:

make_rand_corners <- function(nc, label) {
  ggplot() +
    geom_puzzle_random(
      aes(fill = after_stat(piece_id)),
      n_interior = 8,
      n_corner = nc,
      seed = 42
    ) +
    scale_fill_viridis_c(option = "plasma", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = label)
}

print(make_rand_corners(3, "3 corners") + make_rand_corners(4, "4 corners") +
make_rand_corners(6, "6 corners") + make_rand_corners(8, "8 corners"))
Figure 3
# Generate corner variation previews in a grid
corners <- c(3, 4, 6, 8)
items <- lapply(corners, function(nc) {
  generate_puzzle(type = "random", grid = c(8), size = c(200, 200), seed = 42, n_corner = nc, fill_palette = "plasma")
})
render_puzzle_grid(items, ncol = 4, labels = paste(corners, "corners"))
3 corners
4 corners
6 corners
8 corners
Figure 4

Color Palettes

make_rand_palette <- function(pal) {
  ggplot() +
    geom_puzzle_random(
      aes(fill = after_stat(piece_id)),
      n_interior = 10,
      seed = 42
    ) +
    scale_fill_viridis_c(option = pal, guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = pal)
}

print((make_rand_palette("viridis") + make_rand_palette("magma") + make_rand_palette("plasma")) /
(make_rand_palette("inferno") + make_rand_palette("cividis") + make_rand_palette("turbo")))
Figure 5
# Generate all palette previews and combine into a grid
palettes <- c("viridis", "magma", "plasma", "inferno", "cividis", "turbo")
items <- lapply(palettes, function(pal) {
  generate_puzzle(type = "random", grid = c(10), size = c(200, 200), seed = 42, fill_palette = pal)
})
render_puzzle_grid(items, ncol = 3, labels = palettes)
viridis
magma
plasma
inferno
cividis
turbo
Figure 6

Fill Direction

Control the spatial order of color assignment. This is distinct from palette_invert which reverses the dark↔︎light ends of the palette.

make_direction_plot <- function(dir) {
  ggplot() +
    geom_puzzle_random(
      aes(fill = after_stat(fill_order)),
      n_interior = 8,
      fill_direction = dir,
      seed = 42
    ) +
    scale_fill_viridis_c(option = "viridis", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("fill_direction = \"", dir, "\""))
}

print(make_direction_plot("forward") + make_direction_plot("reverse"))
Figure 7
directions <- c("forward", "reverse")
items <- lapply(directions, function(dir) {
  generate_puzzle(type = "random", grid = c(8), size = c(200, 200), seed = 42, fill_palette = "viridis", fill_direction = dir)
})
render_puzzle_grid(items, ncol = 2, labels = paste0("fill_direction = \"", directions, "\""))
fill_direction = "forward"
fill_direction = "reverse"
Figure 8

Offset (Separation)

make_rand_offset <- function(off) {
  ggplot() +
    geom_puzzle_random(
      aes(fill = after_stat(piece_id)),
      n_interior = 9,
      offset = off, seed = 42
    ) +
    scale_fill_viridis_c(option = "cividis", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("offset = ", off))
}

print(make_rand_offset(0) + make_rand_offset(5) + make_rand_offset(10) + make_rand_offset(20))
Figure 9
# Generate offset previews in a grid
offsets <- c(0, 5, 10, 20)
items <- lapply(offsets, function(off) {
  generate_puzzle(type = "random", grid = c(9), size = c(200, 200), seed = 42, offset = off, fill_palette = "cividis")
})
render_puzzle_grid(items, ncol = 4, labels = paste("offset =", offsets))
offset = 0
offset = 5
offset = 10
offset = 20
Figure 10

Seed Variations

Each seed creates a completely unique puzzle:

make_rand_seed <- function(s) {
  ggplot() +
    geom_puzzle_random(
      aes(fill = after_stat(piece_id)),
      n_interior = 8,
      seed = s
    ) +
    scale_fill_viridis_c(option = "turbo", guide = "none") +
    coord_fixed() +
    theme_puzzle() +
    labs(title = paste0("seed = ", s))
}

print(make_rand_seed(1) + make_rand_seed(42) + make_rand_seed(123) + make_rand_seed(999))
Figure 11
# Generate seed variation previews in a grid
seeds <- c(1, 42, 123, 999)
items <- lapply(seeds, function(s) {
  generate_puzzle(type = "random", grid = c(8), size = c(200, 200), seed = s, fill_palette = "turbo")
})
render_puzzle_grid(items, ncol = 4, labels = paste("seed =", seeds))
seed = 1
seed = 42
seed = 123
seed = 999
Figure 12
TipAspect Ratios

Random puzzles handle any aspect ratio - wide, square, or tall. See the Aspect Ratios Tutorial for examples comparing all puzzle types.

Fusion Groups

Merge pieces together using PILES notation:

# Standard
print(ggplot() +
  geom_puzzle_random(
    aes(fill = after_stat(piece_id)),
    n_interior = 6,
    seed = 42
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "Standard"))

# Adjacent pieces fused
print(ggplot() +
  geom_puzzle_random(
    aes(fill = after_stat(piece_id)),
    n_interior = 6,
    seed = 42,
    fusion_groups = "1-2-3,5-6"
  ) +
  scale_fill_viridis_c(option = "magma", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "Pieces fused"))
Figure 13: Standard (6 interior points)
Figure 14: Adjacent pieces fused
# Standard
result_standard <- generate_puzzle(
  type = "random",
  grid = c(6),
  size = c(200, 200),
  seed = 42,
  fill_palette = "magma"
)
render_puzzle_preview(result_standard, title = "Standard")

# Adjacent pieces fused
result_fused <- generate_puzzle(
  type = "random",
  grid = c(6),
  size = c(200, 200),
  seed = 42,
  fill_palette = "magma",
  fusion_groups = "1-2-3,5-6"
)
render_puzzle_preview(result_fused, title = "Pieces fused")
Figure 15: Standard (6 interior points)
Figure 16: Standard (6 interior points)

Large Random Puzzle

ggplot() +
  geom_puzzle_random(
    aes(fill = after_stat(piece_id)),
    n_interior = 30,
    width = 400, height = 300,
    seed = 456
  ) +
  scale_fill_viridis_c(option = "turbo", guide = "none") +
  coord_fixed() +
  theme_puzzle() +
  labs(title = "30-Piece Random Puzzle")
Figure 17
result <- generate_puzzle(
  type = "random",
  grid = c(30),
  size = c(300, 400),
  seed = 456,
  fill_palette = "turbo"
)
render_puzzle_preview(result, max_width = "600px")
Figure 18

Parameters Reference

Parameter Type Default Description
n_interior integer 12 Number of interior points (influences piece count)
n_corner integer 4 Corners per piece (3-8)
seed integer random Random seed for reproducibility
offset numeric 0 (mm) Piece separation distance
tabsize numeric 6 (%) Tab size as percentage of edge length
jitter numeric 2 (%) Randomness in tab shape as percentage
min_tab_size numeric 0 (mm) Minimum tab size in millimeters
max_tab_size numeric auto (random) / Inf (others) Maximum tab size in mm. Auto-calculated for random puzzles to prevent overlapping tabs
fusion_groups string NULL PILES notation for fusing pieces

Corner Complexity Guide

n_corner Shape Complexity
3 Triangular Simple, angular
4 Quadrilateral Classic, balanced
5 Pentagonal Natural, organic
6 Hexagonal Honeycomb-like
7-8 Complex Highly irregular

When to Use Random Puzzles

Random puzzles are perfect for:

  • Maximum uniqueness: No two puzzles alike
  • Artistic applications: Abstract, modern designs
  • Challenge modes: Irregular shapes are harder to assemble
  • Custom piece counts: Exact control over difficulty

Code Example

ggplot() +
  geom_puzzle_random(
    aes(fill = after_stat(piece_id)),
    n_interior = 15,
    width = 400, height = 300,  # width=400mm, height=300mm
    n_corner = 5,
    seed = 42, offset = 5
  ) +
  scale_fill_viridis_c(option = "inferno", guide = "none") +
  coord_fixed() +
  theme_puzzle()

result <- generate_puzzle(
  type = "random",
  grid = c(15),          # 15 pieces
  size = c(300, 400),    # height=300mm, width=400mm
  seed = 42,
  n_corner = 5,          # Pentagonal pieces
  offset = 5
)

render_puzzle_preview(result)
NoteSaving to File

The API approach returns a result object that can be saved to SVG:

writeLines(result$svg_content, "random_puzzle.svg")