# Hexagonal Puzzles - Useful Code Snippets **Last Updated**: 2025-11-27 A collection of useful code patterns and "hacky" snippets extracted from development and debugging sessions. --- ## Quick Visualization ### Generate Test SVG ```r # Quick test of hexagonal separation with bezier curves source("R/hexagonal_topology.R") source("R/hexagonal_neighbors.R") source("R/hexagonal_bezier_generation.R") source("R/hexagonal_edge_generation_fixed.R") source("R/hexagonal_separation.R") svg <- generate_separated_hexagonal_svg( rings = 2, seed = 42, diameter = 240, offset = 10, arrangement = "hexagonal", use_bezier = TRUE, tabsize = 27, jitter = 5 ) dir.create("output", showWarnings = FALSE) writeLines(svg, "output/quick_test.svg") cat("Saved to output/quick_test.svg\n") ``` ### Create Standalone Bezier Visualization ```r # Visualize hexagonal bezier pieces source("R/hexagonal_topology.R") source("R/hexagonal_bezier_generation.R") pieces <- generate_all_hex_pieces_bezier( rings = 2, seed = 42, diameter = 240, separated = TRUE ) # Calculate SVG dimensions all_x <- sapply(pieces, function(p) p$center_x) all_y <- sapply(pieces, function(p) p$center_y) margin <- 50 min_x <- min(all_x) - margin max_x <- max(all_x) + margin min_y <- min(all_y) - margin max_y <- max(all_y) + margin width <- max_x - min_x height <- max_y - min_y # Create SVG svg_content <- sprintf( ' ', min_x, min_y, width, height, min_x, min_y, width, height ) for (p in pieces) { svg_content <- paste0(svg_content, sprintf( '\n %d', p$path, p$center_x, p$center_y, p$id )) } svg_content <- paste0(svg_content, '\n') writeLines(svg_content, "output/bezier_visualization.svg") ``` --- ## Coordinate System Analysis ### Analyze Grid Iteration Pattern ```r # Understand piece numbering in hexagonal grid source('R/hexagonal_puzzle.R') analyze_hex_grid <- function(rings) { n <- rings yl <- 2 * n - 1 cat(sprintf("Rings: %d\n", rings)) cat(sprintf("yl = %d (2*n - 1)\n\n", yl)) piece_id <- 1 for (yi in seq(-yl + 2, yl - 2, by = 2)) { xl <- 2 * n - 1 - (abs(yi) - 1) / 2 cat(sprintf("yi=%3d: xl=%.1f, xi range: [%.0f, %.0f]\n", yi, xl, -xl + 1, xl - 2)) for (xi in seq(-xl + 1, xl - 2, by = 1)) { cat(sprintf(" Piece %2d: (xi=%.1f, yi=%d)\n", piece_id, xi, yi)) piece_id <- piece_id + 1 } } total <- 3 * rings * (rings - 1) + 1 cat(sprintf("\nTotal pieces: %d\n", total)) } analyze_hex_grid(2) # 7 pieces analyze_hex_grid(3) # 19 pieces ``` ### Map Piece IDs to Axial Coordinates ```r # Convert piece ID to (q, r) axial coordinates piece_to_axial <- function(piece_id, rings) { if (piece_id == 1) return(c(q = 0, r = 0)) # Hexagonal directions (for ring traversal) hex_directions <- list( c(1, 0), c(1, -1), c(0, -1), c(-1, 0), c(-1, 1), c(0, 1) ) remaining <- piece_id - 1 ring <- 1 while (remaining > 6 * ring) { remaining <- remaining - 6 * ring ring <- ring + 1 } # Start position for this ring q <- 0 r <- -ring position <- remaining - 1 direction <- floor(position / ring) steps_in_direction <- position %% ring # Move to starting direction for (d in seq_len(direction)) { for (s in seq_len(ring)) { dir <- hex_directions[[d]] q <- q + dir[1] r <- r + dir[2] } } # Move within direction if (steps_in_direction > 0 && direction < 6) { dir <- hex_directions[[direction + 1]] for (s in seq_len(steps_in_direction)) { q <- q + dir[1] r <- r + dir[2] } } c(q = q, r = r) } ``` --- ## Debugging Utilities ### Debug Path Connectivity ```r # Test if SVG paths connect at endpoints debug_path_connectivity <- function(paths, tolerance = 0.5) { all_segs <- list() for (path in paths) { segs <- split_path_by_move(path) all_segs <- c(all_segs, segs) } cat(sprintf("Total segments: %d\n\n", length(all_segs))) # Print endpoints cat("Segment endpoints:\n") for (i in seq_along(all_segs)) { seg <- all_segs[[i]] cat(sprintf(" %d: (%.2f, %.2f) -> (%.2f, %.2f)\n", i, seg$start[1], seg$start[2], seg$end[1], seg$end[2])) } # Test connectivity cat("\nConnectivity test:\n") for (tol in c(0.1, 0.5, 1.0, 2.0, 5.0)) { connections <- 0 for (i in seq_along(all_segs)) { for (j in seq_along(all_segs)) { if (i != j) { d <- sqrt(sum((all_segs[[i]]$end - all_segs[[j]]$start)^2)) if (d < tol) connections <- connections + 1 } } } cat(sprintf(" Tolerance %.1f: %d connections\n", tol, connections)) } } ``` ### Debug Edge Complementarity ```r # Verify edge complementarity between adjacent pieces debug_edge_complementarity <- function(edge_data, piece1, side1, piece2, side2) { key1 <- sprintf("%d-%d", piece1, side1) key2 <- sprintf("%d-%d", piece2, side2) edge1 <- edge_data$piece_edge_map[[key1]] edge2 <- edge_data$piece_edge_map[[key2]] cat(sprintf("Checking: Piece %d side %d <-> Piece %d side %d\n", piece1, side1, piece2, side2)) if (is.null(edge1)) { cat(" ERROR: edge1 not found\n") return(FALSE) } if (is.null(edge2)) { cat(" ERROR: edge2 not found\n") return(FALSE) } cat(sprintf(" Edge1 key: %s\n", edge1$edge_key)) cat(sprintf(" Edge2 key: %s\n", edge2$edge_key)) cat(sprintf(" Edge1 forward: %s...\n", substr(edge1$forward, 1, 50))) cat(sprintf(" Edge2 reverse: %s...\n", substr(edge2$reverse, 1, 50))) if (edge1$edge_key == edge2$edge_key && edge1$forward == edge2$reverse) { cat(" RESULT: COMPLEMENTARY\n") return(TRUE) } else { cat(" RESULT: NOT COMPLEMENTARY\n") return(FALSE) } } ``` ### Debug Piece Positions ```r # Print all piece positions for verification debug_piece_positions <- function(rings) { source("R/hexagonal_topology.R") num_pieces <- 3 * rings * (rings - 1) + 1 cat(sprintf("Piece positions for %d rings (%d pieces):\n", rings, num_pieces)) cat("=" * 50, "\n") for (i in seq_len(num_pieces)) { ring_info <- map_piece_id_to_ring(i, rings) pos <- calculate_hex_piece_position(i, rings, diameter = 240) cat(sprintf("Piece %2d: ring=%d, pos=%d, dir=%d, angle=%.0f°, ", i, ring_info$ring, ring_info$position_in_ring, ring_info$direction, ring_info$angle * 180 / pi)) cat(sprintf("center=(%.1f, %.1f)\n", pos$x, pos$y)) } } debug_piece_positions(2) ``` --- ## Neighbor Detection ### Calculate Neighbors from Vertex Sharing ```r # Find neighbors by checking shared vertices find_neighbors_by_vertices <- function(pieces, tolerance = 0.5) { neighbors <- list() for (i in seq_along(pieces)) { neighbors[[i]] <- integer(0) vertices_i <- pieces[[i]]$vertices # 6 vertices per hexagon for (j in seq_along(pieces)) { if (i == j) next vertices_j <- pieces[[j]]$vertices # Count shared vertices shared <- 0 for (vi in seq_len(6)) { for (vj in seq_len(6)) { d <- sqrt(sum((vertices_i[, vi] - vertices_j[, vj])^2)) if (d < tolerance) shared <- shared + 1 } } # Adjacent hexagons share exactly 2 vertices (one edge) if (shared >= 2) { neighbors[[i]] <- c(neighbors[[i]], j) } } } neighbors } ``` ### Ring 1 to Ring 2 Neighbor Mapping ```r # Quick lookup for ring 1 neighbors in ring 2 get_ring2_neighbors_of_ring1 <- function(ring1_piece) { # Piece 2 (ring 1, position 0): neighbors pieces 8, 9 in ring 2 # Piece 3 (ring 1, position 1): neighbors pieces 10, 11 in ring 2 # etc. ring1_position <- ring1_piece - 2 # 0-5 ring2_start <- 8 # First piece in ring 2 # Each ring 1 piece neighbors 2 consecutive ring 2 pieces c(ring2_start + ring1_position * 2, ring2_start + ring1_position * 2 + 1) } ``` --- ## SVG Path Manipulation ### Parse SVG Path Commands ```r # Extract commands and coordinates from SVG path parse_svg_path <- function(path) { # Split by command letters commands <- strsplit(path, "(?=[MLCQSZ])", perl = TRUE)[[1]] commands <- commands[nchar(commands) > 0] result <- list() for (cmd in commands) { type <- substr(cmd, 1, 1) coords_str <- trimws(substr(cmd, 2, nchar(cmd))) coords <- as.numeric(strsplit(coords_str, "[, ]+")[[1]]) coords <- coords[!is.na(coords)] result[[length(result) + 1]] <- list(type = type, coords = coords) } result } # Example usage: # parsed <- parse_svg_path("M 10 20 L 30 40 C 50 60 70 80 90 100") # parsed[[1]] # list(type = "M", coords = c(10, 20)) ``` ### Translate SVG Path ```r # Move entire SVG path by (dx, dy) translate_svg_path <- function(path, dx, dy) { parsed <- parse_svg_path(path) result <- "" for (cmd in parsed) { if (cmd$type == "Z") { result <- paste0(result, "Z ") next } coords <- cmd$coords # Translate coordinates (every pair) for (i in seq(1, length(coords), by = 2)) { coords[i] <- coords[i] + dx coords[i + 1] <- coords[i + 1] + dy } result <- paste0(result, cmd$type, " ", paste(sprintf("%.2f", coords), collapse = " "), " ") } trimws(result) } ``` ### Reverse SVG Path ```r # Reverse direction of SVG path (for complementary edges) reverse_svg_path <- function(path) { parsed <- parse_svg_path(path) # Collect all points points <- list() for (cmd in parsed) { if (cmd$type == "M" || cmd$type == "L") { points[[length(points) + 1]] <- cmd$coords } else if (cmd$type == "C") { # Cubic bezier: 3 coordinate pairs points[[length(points) + 1]] <- cmd$coords[1:2] # control 1 points[[length(points) + 1]] <- cmd$coords[3:4] # control 2 points[[length(points) + 1]] <- cmd$coords[5:6] # end point } } # Reverse and rebuild points <- rev(points) # ... rebuild path from reversed points # (Implementation depends on curve type) } ``` --- ## Quick Tests ### Test Shiny App Flow ```r # Trace through Shiny app generation flow test_shiny_flow <- function() { cat("Testing Shiny app flow:\n") # Simulate input rings <- 2 seed <- 42 diameter <- 240 offset <- 10 use_bezier <- TRUE cat("1. Generating SVG...\n") svg <- generate_separated_hexagonal_svg( rings = rings, seed = seed, diameter = diameter, offset = offset, arrangement = "hexagonal", use_bezier = use_bezier ) cat("2. Checking SVG content...\n") cat(sprintf(" Length: %d characters\n", nchar(svg))) cat(sprintf(" Has bezier: %s\n", grepl("C ", svg))) cat(sprintf(" Piece count: %d\n", length(gregexpr("