# PILES: Puzzle Input Line Entry System A SMILES-inspired notation for specifying puzzle piece fusion groups in jigsawR. ## Overview PILES (Puzzle Input Line Entry System) is a domain-specific notation for describing which puzzle pieces should be fused together. It is inspired by chemistry's [SMILES notation](https://en.wikipedia.org/wiki/Simplified_Molecular_Input_Line_Entry_System) for representing molecular structures. Just as SMILES uses simple ASCII strings to represent complex molecular bonds, PILES uses simple strings to represent puzzle piece connections. ## Quick Reference | Syntax | Meaning | Example | |--------|---------|---------| | `n` | Piece ID | `1`, `15`, `42` | | `-` | Fusion bond | `1-2` (fuse pieces 1 and 2) | | `,` | Separate groups | `1-2,3-4` (two independent groups) | | `.` | Separate groups (alt) | `1-2.3-4` (same as comma) | | `n:m` | Range | `1:6` (pieces 1 through 6) | | `()` | Branching | `1-2(-3)-4` (2 bonds to 1, 3, and 4) | | `@n` | Ring closure | `1-2-3-4@1` (4 connects back to 1) | | `[D]` | Direction hint | `1-2[E]-3` (optional validation) | | `ALL-n` | Exclusion | `ALL-5` (all except piece 5) | | `!n` | Exclusion (alt) | `!5` (same as ALL-5) | ## Basic Syntax ### Piece IDs Numbers represent piece IDs (1-indexed): ``` 1 # Piece 1 alone 15 # Piece 15 alone ``` ### Fusion Bonds (-) Hyphens indicate pieces that should be fused together: ``` 1-2 # Fuse pieces 1 and 2 1-2-3 # Linear chain: 1 + 2 + 3 fused together 1-2-3-4-5 # Longer chain ``` ### Multiple Groups (,) Commas separate independent fusion groups: ``` 1-2,3-4 # Two groups: (1,2) and (3,4) 1-2-3,4-5,6-7 # Three groups ``` ### Range Notation (:) Colons specify consecutive piece ranges: ``` 1:6 # Pieces 1, 2, 3, 4, 5, 6 fused together 10:15 # Pieces 10 through 15 ``` ## Advanced Syntax ### Branching Parentheses define branches from a junction piece: ``` 1-2(-3)-4 # Piece 2 connects to 1, 3, AND 4 # Forms a T-junction 1-2(-3)(-4)-5 # Piece 2 connects to 1, 3, 4, AND 5 # Multiple branches from piece 2 ``` ### Ring Closures The `@` symbol with a piece number indicates a ring closure (connecting back to form a cycle): ``` 1-2-3-4@1 # Piece 4 connects back to piece 1 # Forms a closed ring: 1-2-3-4-1 1-2-3-4-5-6@1 # Hexagonal ring closure ``` ### Direction Specifiers (Optional) Square brackets can specify connection directions for validation: ``` # Rectangular puzzles 1-2[E]-3 # 2's East edge connects to 3 1[S]-5 # 1's South edge connects to 5 # Directions: [N]orth, [E]ast, [S]outh, [W]est # Hexagonal puzzles 1-2[0]-3 # Side 0 connection # Sides: [0] through [5] # Concentric puzzles 1-2[O]-3 # Outer edge connection # Directions: [I]nner, [R]ight, [O]uter, [L]eft ``` ## Special Keywords PILES supports special keywords that expand to groups of pieces based on puzzle structure. > **Note:** All keywords are **case-insensitive**. `all`, `ALL`, and `All` are equivalent. ### Universal Keywords | Keyword | Meaning | |---------|---------| | `all` | All pieces in the puzzle (creates a single meta piece) | | `boundary` | All boundary/edge pieces | | `border` | Same as `boundary` | | `edge` | Same as `boundary` | | `inner` | All non-boundary pieces | | `center` | Center piece (hexagonal/concentric) | ### The `all` Keyword - Single Meta Piece The `all` keyword fuses every piece in the puzzle into a single "meta piece". This is useful for: - Creating a puzzle where all internal edges are styled differently (dashed/hidden) - Generating a single connected piece with complex internal structure - Testing fusion rendering across all edges ```r # Create a puzzle where all pieces are fused into one result <- generate_puzzle( type = "rectangular", grid = c(3, 3), size = c(300, 300), fusion_groups = "all", # Fuse ALL pieces fusion_style = "dashed", # Show internal edges as dashed save_files = FALSE ) # All 9 pieces will have the same fusion_group # All internal edges will be rendered with dashed style # Only the outer boundary remains solid ``` ### Hexagonal & Concentric Keywords | Keyword | Meaning | |---------|---------| | `ring0` | Center piece (same as `center`) | | `ring1` | All pieces in ring 1 | | `ring2` | All pieces in ring 2 | | `ringN` | All pieces in ring N | ### Rectangular Keywords | Keyword | Meaning | |---------|---------| | `R1` or `row1` | All pieces in row 1 (top) | | `R2` or `row2` | All pieces in row 2 | | `C1` or `col1` | All pieces in column 1 (left) | | `C2` or `col2` | All pieces in column 2 | ### Keyword Examples ```r # Fuse all boundary pieces together parse_piles("boundary", puzzle_result) # Fuse center with first ring parse_piles("center-ring1", puzzle_result) # Fuse entire first row parse_piles("R1", puzzle_result) # Fuse columns 1 and 2 parse_piles("C1,C2", puzzle_result) ``` ## Exclusion Syntax PILES supports exclusion patterns to fuse "all pieces except..." This is useful when you want to fuse most pieces but leave a few separate. ### Exclusion Patterns | Syntax | Meaning | Example | |--------|---------|---------| | `ALL-n` | All pieces except n | `ALL-5` (all except piece 5) | | `ALL-n-m` | All pieces except n and m | `ALL-1-9` (all except 1 and 9) | | `!n` | All pieces except n | `!5` (same as `ALL-5`) | | `!n!m` | All pieces except n and m | `!1!7` (all except 1 and 7) | ### Exclusion Examples ```r # Create a 3x3 puzzle (9 pieces) puzzle <- generate_puzzle( type = "rectangular", grid = c(3, 3), seed = 42 ) # Fuse all except the center piece (piece 5) parse_piles("ALL-5", puzzle) # Returns: list(c(1L, 2L, 3L, 4L, 6L, 7L, 8L, 9L)) # Fuse all except corners (pieces 1, 3, 7, 9) parse_piles("!1!3!7!9", puzzle) # Returns: list(c(2L, 4L, 5L, 6L, 8L)) # Direct use with generate_puzzle result <- generate_puzzle( type = "rectangular", grid = c(3, 3), fusion_groups = "ALL-5", # Fuse all except center save_files = FALSE ) ``` ### Combining Exclusion with Other Syntax Exclusion patterns create single fusion groups. To create multiple groups with exclusions, use comma separation: ```r # NOT YET SUPPORTED: combining exclusion with regular groups # "ALL-5,1-2" would need future implementation ``` **Note**: Exclusion syntax requires puzzle context to know the total number of pieces. It cannot be used with `parse_piles()` without providing a `puzzle_result`. ## R Functions ### Core Functions #### `parse_piles()` Parse PILES notation into fusion groups: ```r parse_piles("1-2-3,4-5") # Returns: list(c(1L, 2L, 3L), c(4L, 5L)) parse_piles("1:6") # Returns: list(c(1L, 2L, 3L, 4L, 5L, 6L)) # With puzzle context for keywords puzzle <- generate_puzzle(type = "rectangular", grid = c(3, 2)) parse_piles("R1", puzzle) # Returns: list(c(1L, 2L, 3L)) # First row ``` #### `to_piles()` Convert fusion groups back to PILES notation: ```r to_piles(list(c(1, 2), c(3, 4, 5))) # Returns: "1-2,3-4-5" to_piles(list(1:6)) # Returns: "1:6" # Compact range notation to_piles(list(c(3, 1, 2)), compact = FALSE) # Returns: "1-2-3" # Sorted, hyphen-separated ``` #### `parse_fusion()` Universal parser that auto-detects format: ```r # PILES format parse_fusion("1-2-3,4-5") # Legacy format (also supported) parse_fusion("(1,2),(3,4)") # List format (also supported) parse_fusion(list(c(1, 2), c(3, 4))) ``` #### `validate_piles_syntax()` Validate PILES string syntax: ```r validate_piles_syntax("1-2-3,4-5") # Returns: list(valid = TRUE, message = "Valid PILES syntax", warnings = character()) validate_piles_syntax("1-2((3") # Returns: list(valid = FALSE, message = "Unbalanced parentheses: 2 open, 1 close") ``` ## Examples ### Rectangular Puzzle Fusions ```r puzzle <- generate_puzzle(type = "rectangular", grid = c(4, 3), seed = 42) # Fuse horizontal pairs parse_piles("1-2,3-4,5-6,7-8,9-10,11-12", puzzle) # Fuse entire rows parse_piles("R1,R2,R3", puzzle) # Create 2x2 meta-pieces parse_piles("1-2-5-6,3-4-7-8,9-10,11-12", puzzle) ``` ### Hexagonal Puzzle Fusions ```r puzzle <- generate_puzzle(type = "hexagonal", grid = c(3), seed = 42) # Fuse center with first ring parse_piles("center-ring1", puzzle) # Fuse adjacent pairs in outer ring parse_piles("8-9,10-11,12-13,14-15,16-17,18-19", puzzle) ``` ### Concentric Puzzle Fusions ```r puzzle <- generate_puzzle(type = "concentric", grid = c(3), seed = 42) # Fuse entire rings parse_piles("ring0,ring1,ring2", puzzle) # Fuse center to all neighbors parse_piles("1-2-3-4-5-6-7", puzzle) ``` ## Comparison with SMILES | Feature | SMILES (Chemistry) | PILES (Puzzles) | |---------|-------------------|-----------------| | Atoms/Pieces | C, N, O... | 1, 2, 3... | | Bonds | `-`, `=`, `#` | `-` only | | Branching | `()` | `()` | | Ring closure | Digit pairs | `@n` | | Groups | `.` | `,` or `.` | ### Key Differences 1. **PILES uses explicit ring closure syntax** (`@n`) instead of SMILES' implicit digit matching 2. **PILES has puzzle-specific keywords** (`ring1`, `R1`, `boundary`, etc.) 3. **PILES only has single bonds** (fusion is binary: connected or not) 4. **PILES uses numeric IDs** instead of element symbols ## Technical Details ### Parsing Order 1. Split by group separators (`,` or `.`) 2. Check for special keywords 3. Handle range notation (`:`) 4. Process ring closures (`@n`) 5. Remove direction specifiers (`[...]`) 6. Parse structure with branching 7. Validate adjacency (optional) ### Auto-Detection The `parse_fusion()` function auto-detects format: - **Legacy format**: Starts with `(` or contains `(n,n)` pattern - **PILES format**: Contains `-` or keywords ### Performance - Tokenization is O(n) where n is string length - Keyword expansion may require puzzle structure lookup - Pre-allocated vectors used for O(n) performance ## References - [SMILES Notation (Wikipedia)](https://en.wikipedia.org/wiki/Simplified_Molecular_Input_Line_Entry_System) - [OpenSMILES Specification](http://opensmiles.org/opensmiles.html) - [jigsawR Package Documentation](../README.md)