File size: 22,537 Bytes
67acd34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
# ASCII Parser Implementation Template

This template provides step-by-step instructions for implementing ASCII parsers for puzzle games. Use this guide to create parsers for new puzzle types following the established pattern from bridges and undead implementations.

## Table of Contents

1. [Overview](#overview)
2. [Prerequisites](#prerequisites)
3. [Step-by-Step Implementation](#step-by-step-implementation)
4. [Code Structure](#code-structure)
5. [Testing Strategy](#testing-strategy)
6. [Integration Checklist](#integration-checklist)
7. [Common Patterns and Examples](#common-patterns-and-examples)
8. [Troubleshooting](#troubleshooting)

---

## Overview

### Purpose

The ASCII parser converts ASCII text representations of puzzle states into Python dictionaries that match the format produced by `get_puzzle_state_<puzzle_name>()` functions. This enables:

- **Verification**: Check if an ASCII state (from LLM or other sources) is solved
- **State Loading**: Load arbitrary puzzle states from ASCII text
- **Round-trip Testing**: Verify ASCII → state dict → load → format → ASCII works correctly
- **Integration**: Use ASCII states with existing `load_state_dict` functionality

### Pipeline

```
ASCII Text → Structural Validation → Parse (Python) → State Dict → Load (C) → Game State → Verify/Format
```

1. **Structural Validation** (Python): Quick check if ASCII looks valid
2. **Parse ASCII** (Python): Convert ASCII text to state dictionary
3. **Load State Dict** (C): Use existing `load_state_dict_<puzzle>()` function
4. **Verify/Format** (C): Check `completed`/`solved` flag or format back to ASCII

---

## Prerequisites

Before starting, you need:

1. **Access to C source code**: `puzzles/<puzzle_name>.c`
2. **Understanding of ASCII format**: Study `game_text_format()` function in C
3. **Understanding of state dict format**: Review `get_puzzle_state_<puzzle_name>()` in `rlp/specific_api.py`
4. **Understanding of load function**: Review `load_state_dict_<puzzle_name>()` in `rlp/specific_api.py`
5. **Example ASCII states**: Have sample problem and solution states to test with

---

## Step-by-Step Implementation

### Phase 1: Research and Understanding

#### Step 1.1: Study the C ASCII Format Function

**Location**: `puzzles/<puzzle_name>.c`

**Function**: `game_text_format()`

**What to look for**:
- How the function formats the puzzle state to ASCII
- What characters represent different cell types
- How dimensions are represented
- What the header/prefix looks like (if any)
- How multi-line structures are formatted

**Example questions to answer**:
- What characters represent different cell states?
- Are there special characters for different values?
- How are dimensions encoded?
- Is there a header line (like "G: X V: Y Z: Z" for undead)?
- How are edge clues/borders represented?

**Document your findings**:
```python
# ASCII Format for <puzzle_name>:
# - Header: <description>
# - Cell types:
#   - <char1>: <meaning>
#   - <char2>: <meaning>
# - Grid structure: <description>
```

#### Step 1.2: Study the State Dict Format

**Location**: `rlp/specific_api.py`

**Function**: `get_puzzle_state_<puzzle_name>()`

**What to look for**:
- Required top-level fields
- Nested structures (like `common` for undead)
- Array types and lengths
- Field names and types
- Which fields are canonical (required) vs derived (computed by C)

**Key insight**: Only include canonical fields in the parser output. Derived fields should be initialized to zeros/False and computed by C code.

**Document the structure**:
```python
# State dict format for <puzzle_name>:
{
    "field1": <type>,      # Required
    "field2": <type>,      # Required
    "nested": {            # If applicable
        "subfield1": <type>,
    },
    "derived_array": [0] * size,  # Will be computed by C
}
```

#### Step 1.3: Study Cell Type Constants

**Location**: `puzzles/<puzzle_name>.c`

**What to look for**:
- `#define` statements for grid flags
- `enum` definitions for cell types
- Any constants used to represent cell states

**Example**:
```c
// From undead.c:
enum {
    CELL_EMPTY,
    CELL_MIRROR_L,
    CELL_MIRROR_R,
    CELL_GHOST,
    CELL_VAMPIRE,
    CELL_ZOMBIE,
};
```

**Action**: Copy these exact values to Python constants in `rlp/ascii_parser.py`

#### Step 1.4: Study the Load Function

**Location**: `rlp/specific_api.py`

**Function**: `load_state_dict_<puzzle_name>()`

**What to look for**:
- Required fields validation
- Array length requirements
- Type requirements
- Which fields are optional vs required

**This tells you**: What fields your parser MUST produce for the state dict to load successfully.

---

### Phase 2: Structural Validity Checker (Optional but Recommended)

#### Step 2.1: Implement `check_<puzzle_name>_structural_validity()`

**Purpose**: Quick validation before parsing to catch obviously invalid inputs.

**Location**: `rlp/ascii_parser.py`

**What to check**:
- [ ] Has required header/prefix (if applicable)
- [ ] Has grid structure present
- [ ] Grid has minimum dimensions
- [ ] Not empty text
- [ ] Not just long unstructured text
- [ ] Basic format sanity checks

**Template**:
```python
def check_<puzzle_name>_structural_validity(ascii_text: str) -> bool:
    """
    Check structural validity of a <puzzle_name> ASCII state.
    
    Validates:
    1. <Check 1>
    2. <Check 2>
    3. <Check 3>
    
    Args:
        ascii_text: The ASCII representation of the puzzle state
        
    Returns:
        bool: True if structurally valid, False otherwise
    """
    if not ascii_text or not ascii_text.strip():
        return False
    
    lines = ascii_text.strip().split('\n')
    if len(lines) == 0:
        return False
    
    # Check for required header/prefix
    # <implementation>
    
    # Check for grid structure
    # <implementation>
    
    # Check minimum dimensions
    # <implementation>
    
    # Check for invalid patterns (long text, etc.)
    # <implementation>
    
    return True
```

**Testing**: Create test cases with valid and invalid inputs.

---

### Phase 3: Parser Implementation

#### Step 3.1: Add Constants

**Location**: `rlp/ascii_parser.py`

Add cell type constants matching C definitions:

```python
# Cell types matching <puzzle_name>.c enum/defines
CONSTANT_NAME_1 = <value>
CONSTANT_NAME_2 = <value>
# ... etc
```

**Important**: Values must match C exactly (copy hex values, enum values, etc.)

#### Step 3.2: Implement Dimension Inference

**Pattern**:
```python
# First pass: infer dimensions
lines = ascii_text.strip().split('\n')
h = len(lines)
if h == 0:
    raise ValueError("ASCII text must contain at least one line")

# Find maximum width (handle variable line lengths)
max_w = 0
for line in lines:
    stripped = line.rstrip()
    w = len(stripped)
    if w > max_w:
        max_w = w

if max_w == 0:
    raise ValueError("ASCII text must contain at least one non-whitespace character")

w = max_w
```

**Variations**:
- Some puzzles have fixed-width cells (e.g., undead: 2 characters per cell)
- Some puzzles have headers to skip
- Some puzzles have border cells to account for

#### Step 3.3: Implement Cell-by-Cell Parsing

**Pattern**:
```python
# Initialize arrays
wh = w * h  # or (w+2)*(h+2) if including border
grid = [0] * wh
other_array = [0] * wh
structures = []  # For puzzle-specific structures (islands, etc.)

# Parse each cell
for y, line in enumerate(lines):
    stripped = line.rstrip()
    for x in range(w):
        if x >= len(stripped):
            # Line is shorter, treat as empty
            continue
        
        c = stripped[x]  # or cell_str = stripped[x*2:(x+1)*2] for fixed-width
        idx = y * w + x  # or y * grid_w + x if including border
        
        # Parse based on character/cell
        if c == '<char1>':
            grid[idx] = CONSTANT_1
            # ... set other fields
        elif c == '<char2>':
            grid[idx] = CONSTANT_2
            # ... set other fields
        # ... etc
```

**Key considerations**:
- Handle variable line lengths gracefully
- Map ASCII characters to cell type constants
- Build puzzle-specific structures (islands, monsters, etc.)
- Track indices correctly (especially if grid includes border)

#### Step 3.4: Build State Dict

**Pattern**:
```python
state_dict = {
    # Required top-level fields
    "field1": value1,
    "field2": value2,
    
    # Nested structures (if applicable)
    "nested": {
        "params": {
            "w": w,
            "h": h,
            # Only include canonical params, omit generation params
        },
        "array1": array1,
        "array2": array2,
    },
    
    # Derived arrays - initialized to zeros, computed by C code
    "derived_array": [0] * size,
    
    # Status flags - will be computed by C code
    "completed": False,
    "solved": False,
}
```

**Important**:
- Only include canonical fields (required for `load_state_dict`)
- Initialize derived arrays to zeros
- Set `completed`/`solved` to False (C will compute)
- Omit generation params (islands, expansion, difficulty, etc.)

#### Step 3.5: Add Error Handling

**Pattern**:
```python
def parse_ascii_<puzzle_name>(ascii_text: str) -> dict:
    """
    Parse ASCII text representation of a <puzzle_name> puzzle and return a state dict.
    
    Args:
        ascii_text: The ASCII representation of the puzzle state
        
    Returns:
        dict: State dictionary matching get_puzzle_state_<puzzle_name> format
        
    Raises:
        ValueError: If the ASCII text is invalid or empty
    """
    if not ascii_text or not ascii_text.strip():
        raise ValueError("ASCII text cannot be empty")
    
    # ... parsing logic ...
    
    # Validate dimensions
    if w < 1 or h < 1:
        raise ValueError(f"Invalid grid dimensions: w={w}, h={h}")
    
    # ... return state_dict ...
```

---

### Phase 4: Testing

#### Step 4.1: Create Test File

**Location**: `test_ascii_parser_<puzzle_name>.py`

**Template structure**:
```python
"""
Test script for <puzzle_name> ASCII parser round-trip functionality.
"""
import sys
import os
import pandas as pd
from rlp import puzzle as rp
from rlp.ascii_parser import parse_ascii_<puzzle_name>, check_<puzzle_name>_structural_validity


def test_parse_example():
    """Test parsing with a known example."""
    # <example ASCII>
    # ... test logic ...


def test_structural_validity():
    """Test structural validity checker."""
    # Valid examples
    # Invalid examples
    # ... test logic ...


def test_round_trip():
    """Test round-trip: ASCII → parse → load → format → ASCII."""
    # ... test logic ...


def test_csv_predictions_round_trip():
    """Test round-trip for all problems and solutions in CSV."""
    # ... test logic ...


if __name__ == "__main__":
    # Run all tests
    # ... test runner ...
```

#### Step 4.2: Test Cases to Include

1. **Basic parsing test**: Known valid ASCII state
2. **Structural validity test**: Valid and invalid examples
3. **Round-trip test**: Generate puzzle → get ASCII → parse → load → format → compare
4. **Edge cases**: Empty states, single cell, maximum size, special characters
5. **CSV round-trip**: Test all problems/solutions from dataset

#### Step 4.3: Round-Trip Test Pattern

**Critical test**:
```python
# 1. Get ASCII from a puzzle state
ascii_original = game.text_format(state).decode('utf-8')

# 2. Parse with Python parser
state_dict = parse_ascii_<puzzle_name>(ascii_original)

# 3. Load into C
loaded_state_ptr = puzzle.load_state_dict(state_dict)

# 4. Format back to ASCII
ascii_loaded = game.text_format(loaded_state_ptr.contents).decode('utf-8')

# 5. Compare (should match exactly)
assert ascii_original.strip() == ascii_loaded.strip()
```

---

### Phase 5: Integration

#### Step 5.1: Update Verifier

**Location**: `verifier.py`

**Changes needed**:
1. Import parser function: `from rlp.ascii_parser import parse_ascii_<puzzle_name>, check_<puzzle_name>_structural_validity`
2. Add puzzle type case in `verify_ascii_state()`:
```python
elif puzzle_type == "<puzzle_name>":
    # First check structural validity
    if not check_<puzzle_name>_structural_validity(str(ascii_text)):
        return "NOT SOLVED"
    
    # Parse ASCII with Python parser
    state_dict = parse_ascii_<puzzle_name>(str(ascii_text))
    
    # Load state dict
    loaded_state_ptr = puzzle.load_state_dict(state_dict)
    
    # Get free_game function
    me = puzzle.fe.contents.me.contents
    game = me.ourgame.contents
    free_game_func = game.free_game
    
    try:
        # Check if solved (use correct field: completed or solved)
        is_solved = loaded_state_ptr.contents.<field>  # completed or solved
        
        if is_solved:
            return "SOLVED"
        else:
            return "NOT SOLVED"
    finally:
        if loaded_state_ptr:
            free_game_func(loaded_state_ptr)
```

**Important**: Check which field the puzzle uses (`completed` for bridges, `solved` for undead)

#### Step 5.2: Update Test Verifier

**Location**: `test_verifier.py`

**Changes needed**:
1. Add puzzle type parameter to test functions
2. Add puzzle-specific configuration (difficulty args, puzzle name in CSV)
3. Add puzzle type to command-line argument choices

#### Step 5.3: Update Evaluate Predictions (if applicable)

**Location**: `evaluate-predictions.py`

**Changes needed**:
1. Add puzzle type to command-line arguments
2. Add puzzle-specific configuration
3. Update puzzle name filtering

---

## Code Structure

### File: `rlp/ascii_parser.py`

**Structure**:
```python
# Constants for puzzle 1
CONSTANT_1 = value1
CONSTANT_2 = value2

# Structural validity for puzzle 1
def check_puzzle1_structural_validity(ascii_text: str) -> bool:
    ...

# Parser for puzzle 1
def parse_ascii_puzzle1(ascii_text: str) -> dict:
    ...

# Constants for puzzle 2
CONSTANT_3 = value3

# Structural validity for puzzle 2
def check_puzzle2_structural_validity(ascii_text: str) -> bool:
    ...

# Parser for puzzle 2
def parse_ascii_puzzle2(ascii_text: str) -> dict:
    ...
```

### Function Signature Template

```python
def parse_ascii_<puzzle_name>(ascii_text: str) -> dict:
    """
    Parse ASCII text representation of a <puzzle_name> puzzle and return a state dict.
    
    ASCII Format:
    - <Description of format>
    - <Cell types and meanings>
    - <Special structures>
    
    Args:
        ascii_text: The ASCII representation of the puzzle state
        
    Returns:
        dict: State dictionary matching get_puzzle_state_<puzzle_name> format
        
    Raises:
        ValueError: If the ASCII text is invalid or empty
    """
    # Implementation
```

---

## Testing Strategy

### Unit Tests (Parser-Specific)

**File**: `test_ascii_parser_<puzzle_name>.py`

**Test categories**:
1. **Basic parsing**: Valid ASCII states parse correctly
2. **Structural validity**: Valid vs invalid inputs
3. **Edge cases**: Empty, single cell, max size, special characters
4. **Round-trip**: ASCII → parse → load → format → ASCII (must match)
5. **CSV integration**: Test with real dataset

### Integration Tests (Verifier)

**File**: `test_verifier.py`

**Test categories**:
1. **CSV predictions**: Problems return `solved=False`, solutions return `solved=True`
2. **State comparison**: Compare two similar ASCII states
3. **Large-scale testing**: Test all problems/solutions from dataset

---

## Integration Checklist

### Parser Implementation
- [ ] Constants defined matching C values
- [ ] Structural validity checker implemented (optional but recommended)
- [ ] Parser function implemented
- [ ] State dict format matches `get_puzzle_state_<puzzle_name>()` exactly
- [ ] Error handling added
- [ ] Documentation added

### Testing
- [ ] Unit tests created (`test_ascii_parser_<puzzle_name>.py`)
- [ ] Structural validity tests pass
- [ ] Round-trip tests pass
- [ ] Edge case tests pass
- [ ] CSV round-trip tests pass (if applicable)

### Integration
- [ ] Verifier updated (`verifier.py`)
- [ ] Test verifier updated (`test_verifier.py`)
- [ ] Evaluate predictions updated (if applicable)
- [ ] All tests pass

---

## Common Patterns and Examples

### Pattern 1: Simple Grid (like bridges)

```python
# Dimension inference
lines = ascii_text.strip().split('\n')
h = len(lines)
w = max(len(line.rstrip()) for line in lines)

# Cell-by-cell parsing
for y, line in enumerate(lines):
    for x in range(w):
        c = line[x] if x < len(line.rstrip()) else None
        idx = y * w + x
        # Parse character
```

### Pattern 2: Grid with Border (like undead)

```python
# Grid is (w+2) x (h+2) including border
grid_w = max_cells  # from parsing
grid_h = len(grid_lines)
w = grid_w - 2
h = grid_h - 2

# Cell-by-cell parsing with border
for y, line in enumerate(grid_lines):
    for x in range(grid_w):
        cell_str = line[x*2:(x+1)*2]  # 2-character cells
        idx = y * grid_w + x
        # Parse cell
```

### Pattern 3: Header + Grid

```python
# Parse header
first_line = lines[0].strip()
# Extract header information

# Find grid start
grid_start = 1
if len(lines) > 1 and lines[1].strip() == '':
    grid_start = 2

grid_lines = lines[grid_start:]
# Parse grid
```

### Pattern 4: Helper Functions

```python
def helper_function_for_puzzle(x, y, w, h):
    """
    Helper function ported from C.
    
    Args:
        x, y: Coordinates
        w, h: Dimensions
        
    Returns:
        <return type>
    """
    # Ported logic from C
    pass
```

---

## Troubleshooting

### Common Issues and Solutions

#### Issue 1: State Dict Format Mismatch

**Symptom**: `load_state_dict()` raises ValueError about missing fields

**Solution**:
- Compare your state dict with output from `get_puzzle_state_<puzzle_name>()`
- Ensure all required fields are present
- Check field names match exactly (case-sensitive)
- Verify array lengths match requirements

#### Issue 2: Round-Trip Mismatch

**Symptom**: ASCII after round-trip doesn't match original

**Solution**:
- Check dimension inference is correct
- Verify cell parsing maps characters correctly
- Ensure border cells are handled correctly (if applicable)
- Check if whitespace differences matter (use `.strip()` for comparison)

#### Issue 3: Grid Flags Don't Match

**Symptom**: Parsed state doesn't behave correctly

**Solution**:
- Verify constants match C `#define` values exactly
- Check hex values are correct (0x0001 vs 0x001, etc.)
- Ensure bitwise operations are correct

#### Issue 4: Derived Arrays Issues

**Symptom**: Errors about array access or computation

**Solution**:
- Initialize derived arrays to zeros (don't try to compute in Python)
- Let C code recompute them
- Ensure array lengths are correct

#### Issue 5: Memory Leaks

**Symptom**: Memory usage grows over time

**Solution**:
- Always use try/finally to free loaded states
- Call `free_game_func(loaded_state_ptr)` in finally block
- Don't forget to free states in test loops

#### Issue 6: Structural Validity Too Strict/Loose

**Symptom**: Valid states rejected or invalid states accepted

**Solution**:
- Adjust validation criteria
- Test with edge cases
- Balance between catching errors and being lenient

---

## Quick Reference

### Key Files

- **Parser implementation**: `rlp/ascii_parser.py`
- **State dict format**: `rlp/specific_api.py` (`get_puzzle_state_<puzzle_name>()`)
- **Load function**: `rlp/specific_api.py` (`load_state_dict_<puzzle_name>()`)
- **C ASCII format**: `puzzles/<puzzle_name>.c` (`game_text_format()`)
- **C state structure**: `puzzles/<puzzle_name>.c` (struct definitions)
- **Verifier**: `verifier.py`
- **Tests**: `test_ascii_parser_<puzzle_name>.py`, `test_verifier.py`

### Key Functions

- `parse_ascii_<puzzle_name>(ascii_text: str) -> dict`: Main parser
- `check_<puzzle_name>_structural_validity(ascii_text: str) -> bool`: Quick validation
- `verify_ascii_state(puzzle, ascii_text: str) -> str`: Verifier integration
- `puzzle.load_state_dict(state_dict: dict)`: Load state into C

### Key Principles

1. **Match formats exactly**: State dict must match `get_puzzle_state_<puzzle_name>()` exactly
2. **Minimal canonical fields**: Only include what's needed for reconstruction
3. **Let C compute derived fields**: Initialize derived arrays to zeros
4. **Round-trip testing is critical**: ASCII → parse → load → format → ASCII must match
5. **Handle edge cases**: Variable line lengths, special characters, empty states
6. **Memory management**: Always free loaded states
7. **Test with real data**: Use actual problems and solutions from datasets

---

## Example: Complete Implementation Checklist

For a new puzzle called "example":

### Phase 1: Research
- [ ] Study `puzzles/example.c` `game_text_format()` function
- [ ] Review `get_puzzle_state_example()` in `rlp/specific_api.py`
- [ ] Review `load_state_dict_example()` in `rlp/specific_api.py`
- [ ] Identify all ASCII characters and their meanings
- [ ] Identify cell type constants from C source
- [ ] Understand the puzzle's state structure

### Phase 2: Implementation
- [ ] Add constants to `rlp/ascii_parser.py`
- [ ] Implement `check_example_structural_validity()` (optional)
- [ ] Implement `parse_ascii_example()` function
- [ ] Test with known examples

### Phase 3: Testing
- [ ] Create `test_ascii_parser_example.py`
- [ ] Test structural validity (if implemented)
- [ ] Test basic parsing
- [ ] Test round-trip
- [ ] Test edge cases
- [ ] Test with CSV (if applicable)

### Phase 4: Integration
- [ ] Update `verifier.py` to use new parser
- [ ] Update `test_verifier.py` to test example puzzle
- [ ] Update `evaluate-predictions.py` (if applicable)
- [ ] Verify all tests pass

---

## References

- Bridges parser: `rlp/ascii_parser.py` (`parse_ascii_bridges`, `check_bridges_structural_validity`)
- Undead parser: `rlp/ascii_parser.py` (`parse_ascii_undead`, `check_undead_structural_validity`)
- Original guide: `ASCII_PARSER_IMPLEMENTATION_GUIDE_new.md`
- State dict format: `rlp/specific_api.py`
- C text format: `puzzles/<puzzle_name>.c` (`game_text_format()`)
- Tests: `test_ascii_parser.py`, `test_ascii_parser_undead.py`, `test_verifier.py`

---

## Notes

- This template is based on successful implementations for bridges and undead puzzles
- Adapt patterns to your specific puzzle's requirements
- When in doubt, refer to existing implementations (bridges, undead) as examples
- Test thoroughly with real data before considering implementation complete
- Round-trip testing is the most important validation