diff --git a/.amazonq/plans/.gitkeep b/.amazonq/plans/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.amazonq/plans/02-cookiecutter-template-improvement.md b/.amazonq/plans/02-cookiecutter-template-improvement.md deleted file mode 100644 index acc78a5..0000000 --- a/.amazonq/plans/02-cookiecutter-template-improvement.md +++ /dev/null @@ -1,266 +0,0 @@ -# Plan: Improve Cookiecutter Template for LeetCode Problems - -## Overview - -Improve the cookiecutter template to be more general and cover all LeetCode question types, including special cases like LRU Cache that use custom class names and multiple methods. - -## Current Issues Identified - -1. **Hard-coded class name**: Template assumes `Solution` class but some problems use custom classes (e.g., `LRUCache`) -2. **Single method assumption**: Template assumes one method but some problems have multiple methods (`__init__`, `get`, `put`) -3. **Complex test setup**: Current template doesn't handle operation-based testing for design problems -4. **Import handling**: Need better solution import structure -5. **Template parameter explosion**: Too many derived parameters that could be simplified - -## Target Structure - -``` -.templates/leetcode/ -├── {{cookiecutter.problem_name}}/ -│ ├── __init__.py -│ ├── solution.py -│ ├── tests.py -│ ├── README.md -│ └── playground.ipynb -└── cookiecutter.json -``` - -## Phase 1: Analyze Problem Types - -### 1.1 Universal Template Variables - -- `solution_imports`: Required imports for solution.py (empty, TreeNode, ListNode, etc.) -- `test_imports`: All import lines for tests.py (pytest, loguru, TreeNode, test_utils, solution class) -- `solution_class_name`: Dynamic class name (Solution, LRUCache, etc.) -- `readme_description`: Problem description for README (supports HTML including images, preserves code snippets like `x`, `y`, `some_params`) -- `readme_examples`: Examples for README (supports HTML including images, typically uses code blocks for input/output) -- `readme_constraints`: Constraints for README -- `readme_additional`: Additional README sections -- `solution_methods`: List of methods with parameters/return types -- `test_methods`: List of test methods with parametrize and body -- `test_helper_methods`: List of test helper methods (setup_method, teardown_method, utility methods, etc.) - -## Phase 2: Create New cookiecutter.json - -### 2.1 Complete Template Variables Example - -````json -{ - "problem_name": "two_sum", - "solution_class_name": "Solution", - "problem_number": "1", - "problem_title": "Two Sum", - "difficulty": "Easy", - "topics": "Array, Hash Table", - "tags": ["grind-75"], - - "readme_description": "Given an array of integers `nums` and an integer `target`, return indices of the two numbers such that they add up to `target`.\n\n\"Array\n\nYou may assume that each input would have exactly one solution, and you may not use the same element twice.", - "readme_examples": [ - { - "content": "```\nInput: nums = [2,7,11,15], target = 9\nOutput: [0,1]\n```\n**Explanation:** Because nums[0] + nums[1] == 9, we return [0, 1]." - }, - { - "content": "\"Binary\n\n```\nInput: root = [4,2,7,1,3,6,9]\nOutput: [4,7,2,9,6,3,1]\n```\n**Explanation:** The tree is inverted as shown in the image above." - } - ], - "readme_constraints": "- 2 <= nums.length <= 10^4\n- -10^9 <= nums[i] <= 10^9\n- -10^9 <= target <= 10^9\n- Only one valid answer exists.", - "readme_additional": "", - - "solution_imports": "", - "solution_methods": [ - { - "name": "two_sum", - "parameters": "nums: list[int], target: int", - "return_type": "list[int]", - "dummy_return": "[]" - } - ], - - "test_imports": "import pytest\nfrom loguru import logger\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", - "test_helper_methods": [ - { - "name": "setup_method", - "parameters": "", - "body": "self.solution = Solution()" - } - ], - "test_methods": [ - { - "name": "test_two_sum", - "parametrize": "nums, target, expected", - "test_cases": "[([2, 7, 11, 15], 9, [0, 1]), ([3, 2, 4], 6, [1, 2])]", - "body": "result = self.solution.two_sum(nums, target)\nassert result == expected" - } - ], - - "playground_imports": "from solution import Solution", - "playground_test_case": "# Example test case\nnums = [2, 7, 11, 15]\ntarget = 9\nexpected = [0, 1]", - "playground_execution": "result = Solution().two_sum(nums, target)\nresult", - "playground_assertion": "assert result == expected" -} -```` - -## Phase 3: Update Template Files - -### 3.1 solution.py Template - -```python -{{cookiecutter.solution_imports}} - -class {{cookiecutter.solution_class_name}}: - {% for method in cookiecutter.solution_methods %} - # Time: O(?) - # Space: O(?) - def {{method.name}}(self, {{method.parameters}}) -> {{method.return_type}}: - # TODO: Implement {{method.name}} - return {{method.dummy_return}} - - {% endfor %} -``` - -### 3.2 tests.py Template - -```python -{{cookiecutter.test_imports}} - -class Test{{cookiecutter.solution_class_name}}: - {% for method in cookiecutter.test_helper_methods %} - def {{method.name}}(self{% if method.parameters %}, {{method.parameters}}{% endif %}): - {{method.body}} - - {% endfor %} - - {% for method in cookiecutter.test_methods %} - @pytest.mark.parametrize("{{method.parametrize}}", {{method.test_cases}}) - @logged_test - def {{method.name}}(self, {{method.parametrize}}): - {{method.body}} - - {% endfor %} -``` - -### 3.3 README.md Template - -```markdown -# {{cookiecutter.problem_title}} - -**Difficulty:** {{cookiecutter.difficulty}} -**Topics:** {{cookiecutter.topics}} -**Tags:** {{cookiecutter.tags | join(', ')}} -{% if cookiecutter.problem_number %} -**LeetCode:** [Problem {{cookiecutter.problem_number}}](https://leetcode.com/problems/{{cookiecutter.problem_name.replace('_', "-")}}/description/) -{% endif %} - -## Problem Description - -{{cookiecutter.readme_description}} - -## Examples - -{% for example in cookiecutter.readme_examples %} - -### Example {{loop.index}}: - -{{example.content}} -{% endfor %} - -## Constraints - -{{cookiecutter.readme_constraints}} - -{% if cookiecutter.readme_additional %} -{{cookiecutter.readme_additional}} -{% endif %} -``` - -### 3.4 playground.ipynb Template - -```json -{ - "cells": [ - { - "cell_type": "code", - "id": "imports", - "source": ["{{cookiecutter.playground_imports}}"] - }, - { - "cell_type": "code", - "id": "setup", - "source": ["{{cookiecutter.playground_test_case}}"] - }, - { - "cell_type": "code", - "id": "execute", - "source": ["{{cookiecutter.playground_execution}}"] - }, - { - "cell_type": "code", - "id": "test", - "source": ["{{cookiecutter.playground_assertion}}"] - } - ] -} -``` - -## Phase 4: Simplify Parameter Generation - -### 4.1 Reduce Template Complexity - -- Remove derived parameters like `param_names`, `input_description`, etc. -- Generate these dynamically in the template using Jinja2 filters -- Focus on core data: methods, test_cases, problem metadata - -### 4.2 Smart Defaults - -- Auto-generate method names from problem names -- Auto-generate class names from problem names -- Default to "basic" problem type unless specified - -## Phase 5: Testing & Validation - -### 5.1 Test with Existing Problems - -- Generate all current problems using new template -- Compare output with existing generated files -- Ensure no regression in functionality - -### 5.2 Test Special Cases - -- LRU Cache (design problem) -- Tree problems (TreeNode imports) -- Linked list problems (ListNode imports) -- Matrix problems (2D arrays) - -## Phase 6: Integration - -### 6.1 Update Generation Scripts - -- Modify `gen.py` to work with new template structure -- Update Makefile commands -- Ensure backward compatibility with existing JSON files - -### 6.2 Documentation - -- Update problem creation guide -- Create examples for each problem type -- Document new template variables - -## Success Criteria - -1. ✅ Single cookiecutter template handles all problem types -2. ✅ Reduced template complexity (fewer derived parameters) -3. ✅ Support for design problems with multiple methods -4. ✅ Proper imports for tree/linked list problems -5. ✅ Clean, maintainable template structure -6. ✅ All existing problems can be regenerated without issues -7. ✅ New problem types can be easily added - -## Implementation Order - -1. Create new `cookiecutter.json` structure -2. Update template files with conditional logic -3. Test with basic problems (Two Sum) -4. Test with design problems (LRU Cache) -5. Test with tree/linked list problems -6. Validate all existing problems -7. Update generation scripts and documentation diff --git a/.amazonq/plans/03-regenerate-all-problems.md b/.amazonq/plans/03-regenerate-all-problems.md deleted file mode 100644 index e993f26..0000000 --- a/.amazonq/plans/03-regenerate-all-problems.md +++ /dev/null @@ -1,228 +0,0 @@ -# Plan: Regenerate All Problems with Fresh LeetCode Data - -## Overview - -Scrape fresh data from LeetCode for existing problems and generate new JSON files using the new universal cookiecutter template, ensuring all generated code passes linting. - -## Current State Analysis - -- Old JSON files: `.templates/leetcode/old/template/json/*.json` (for problem names only) -- Scraper: `.templates/leetcode/scrape.py` (existing) -- New template: `.templates/leetcode/cookiecutter.json` + templates -- Target: `.templates/leetcode/json/*.json` (new format with fresh data) -- Generated problems: `leetcode/*/` - -## Phase 1: Extract Problem Names - -### 1.1 Get Problem Names from Old JSON - -```bash -ls .templates/leetcode/old/template/json/ -``` - -Expected problems: - -- container_with_most_water.json -- insert_interval.json -- invert_binary_tree.json -- lru_cache.json -- reverse_linked_list_ii.json -- spiral_matrix.json - -### 1.2 Extract Problem Numbers/Slugs - -From old JSON files, extract: - -- Problem numbers (for scraping by number) -- Problem names/slugs (for scraping by slug) - -## Phase 2: Create Fresh Scraping Script - -### 2.1 Use Existing Scraper - -Following the problem creation rules: - -```bash -# Fetch by number -poetry run python .templates/leetcode/scrape.py -n 1 - -# Fetch by slug -poetry run python .templates/leetcode/scrape.py -s "two-sum" -``` - -### 2.2 Scraping Script: `scrape_all_problems.py` - -✅ **Created** - Uses existing scraper to fetch fresh data for all problems - -## Phase 3: Execute Fresh Scraping - -### 3.1 Run Scraping Script - -```bash -cd .templates/leetcode -python scrape_all_problems.py -``` - -### 3.2 Transform JSON to New Template Format - -The scraper creates JSON in new template format automatically. - -### 3.3 Verify New JSON Files - -```bash -ls .templates/leetcode/json/ -# Should show all freshly scraped JSON files -``` - -## Phase 4: Test Generation Pipeline - -### 4.1 Test Single Problem Generation - -```bash -make p-gen PROBLEM=container_with_most_water FORCE=1 -``` - -### 4.2 Verify Generated Structure - -```bash -ls leetcode/container_with_most_water/ -# Should show: __init__.py, solution.py, tests.py, README.md, playground.ipynb -``` - -### 4.3 Test Linting on Single Problem - -```bash -make lint -# Should pass without errors -``` - -## Phase 5: Full Regeneration - -### 5.1 Update Makefile (if needed) - -Verify `gen-all-problems` target works with new template: - -```makefile -gen-all-problems: - @echo "This will DELETE all existing problems and regenerate from JSON templates." - @read -p "Are you sure? (y/N): " confirm && [ "$$confirm" = "y" ] || exit 1 - @echo "Deleting existing problems..." - @rm -rf leetcode/*/ - @echo "Generating all problems..." - @for json_file in .templates/leetcode/json/*.json; do \ - problem=$$(basename "$$json_file" .json); \ - echo "Generating: $$problem"; \ - poetry run python .templates/leetcode/gen.py "$$json_file" --force; \ - done -``` - -### 5.2 Run Full Regeneration - -```bash -make gen-all-problems FORCE=1 -``` - -### 5.3 Verify All Problems Generated - -```bash -ls leetcode/ -# Should show all problem directories -``` - -## Phase 6: Validation & Iteration - -### 6.1 Run Linting on All Problems - -```bash -make lint -``` - -### 6.2 Fix Issues (Iterative Process) - -**If linting fails:** - -1. **Identify Issue Type:** - - Template formatting issues → Fix template files - - JSON data issues → Re-scrape or manually fix JSON - - Import issues → Fix template imports - -2. **Fix and Regenerate:** - - ```bash - # Fix issue in template or JSON - make gen-all-problems FORCE=1 - make lint - ``` - -3. **Repeat until clean:** - - Continue iteration until `make lint` passes - - Ensure reproducible generation - -### 6.3 Common Issues & Fixes - -**Template Issues:** - -- Indentation problems → Fix Jinja2 `indent` filters in templates -- Missing imports → Update template import generation -- Method signatures → Fix solution_methods in JSON - -**JSON Issues:** - -- Missing required fields → Re-scrape with updated scraper -- Incorrect data types → Fix scraper output format -- Test case format → Update scraper test case generation - -## Phase 7: Validation Testing - -### 7.1 Test Individual Problems - -```bash -make p-test PROBLEM=container_with_most_water -make p-test PROBLEM=lru_cache -make p-test PROBLEM=invert_binary_tree -``` - -### 7.2 Run Full Test Suite - -```bash -make test -``` - -### 7.3 Verify Notebooks Work - -- Check playground.ipynb files can be opened -- Verify cell content is properly formatted - -## Success Criteria - -1. ✅ All old JSON files migrated to new format -2. ✅ `make gen-all-problems` works with new template -3. ✅ All generated problems pass `make lint` -4. ✅ All generated problems pass `make test` -5. ✅ Generation is reproducible (no manual fixes needed) -6. ✅ All problem types work (basic, design, tree, linked list) - -## Implementation Order - -1. **Create scraping script** - Get fresh data from LeetCode -2. **Run fresh scraping** - Generate new JSON files -3. **Test single problem** - Verify pipeline works -4. **Fix template issues** - Ensure clean generation -5. **Run full regeneration** - Generate all problems -6. **Iterate on linting** - Fix issues until clean -7. **Final validation** - Test all problems work - -## Rollback Plan - -If issues arise: - -1. Keep old template in `.templates/leetcode/old/` -2. Can revert to old generation method -3. New template is additive, doesn't break existing workflow - -## Notes - -- Focus on reproducible generation without manual intervention -- Prioritize linting compliance over perfect formatting -- Document any template limitations discovered during migration -- Ensure backward compatibility with existing JSON structure diff --git a/.amazonq/plans/compare_template_files.py b/.amazonq/plans/compare_template_files.py deleted file mode 100644 index aeb083a..0000000 --- a/.amazonq/plans/compare_template_files.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -"""Reusable file comparison tool for template validation.""" - -import difflib -from pathlib import Path - -import typer - - -def compare_files(file1: Path, file2: Path, label1: str, label2: str) -> bool: - """Compare two files and show differences. Returns True if identical.""" - typer.echo(f"\n{'='*60}") - typer.echo(f"COMPARING: {file1.name}") - typer.echo(f"{label1}: {file1}") - typer.echo(f"{label2}: {file2}") - typer.echo(f"{'='*60}") - - if not file1.exists(): - typer.echo(f"❌ MISSING: {file1} does not exist") - return False - - if not file2.exists(): - typer.echo(f"❌ MISSING: {file2} does not exist") - return False - - content1 = file1.read_text().splitlines(keepends=True) - content2 = file2.read_text().splitlines(keepends=True) - - diff = list( - difflib.unified_diff( - content1, - content2, - fromfile=f"{label1}/{file1.name}", - tofile=f"{label2}/{file2.name}", - lineterm="", - ) - ) - - if not diff: - typer.echo("✅ FILES IDENTICAL") - return True - else: - typer.echo("❌ DIFFERENCES FOUND:") - for line in diff: - typer.echo(line) - return False - - -def main( - mode: str = typer.Argument(help="Compare template files or generated files (template|generated)"), - problem: str = typer.Option("invert_binary_tree", help="Problem name for comparison"), -): - """Compare files for template validation.""" - if mode not in ["template", "generated"]: - typer.echo(f"❌ ERROR: Invalid mode '{mode}'. Use 'template' or 'generated'") - return - - base_dir = Path(__file__).parent.parent.parent - - files_to_compare = ["solution.py", "tests.py", "README.md", "playground.ipynb", "__init__.py"] - - if mode == "template": - # Compare reference vs template source - dir1 = base_dir / "leetcode" / ".example" / problem - dir2 = base_dir / ".templates" / "leetcode" / ".example" / "{{cookiecutter.problem_name}}" - label1, label2 = "Reference", "Template" - typer.echo("TEMPLATE SOURCE ANALYSIS") - - elif mode == "generated": - # Compare reference vs currently generated - dir1 = base_dir / "leetcode" / ".example" / problem - dir2 = base_dir / "leetcode" / problem - label1, label2 = "Reference", "Generated" - typer.echo("GENERATED FILES VALIDATION") - - if not dir2.exists(): - typer.echo(f"\n❌ ERROR: Generated directory does not exist: {dir2}") - typer.echo(f"Run: make p-gen PROBLEM={problem}") - return - - typer.echo(f"{label1}: {dir1}") - typer.echo(f"{label2}: {dir2}") - - identical_count = 0 - for filename in files_to_compare: - file1 = dir1 / filename - file2 = dir2 / filename - if compare_files(file1, file2, label1, label2): - identical_count += 1 - - typer.echo(f"\n{'='*60}") - typer.echo(f"SUMMARY: {identical_count}/{len(files_to_compare)} files identical") - typer.echo("- ✅ = Files identical") - typer.echo("- ❌ = Differences found or missing files") - - -if __name__ == "__main__": - typer.run(main) diff --git a/.amazonq/plans/cookiecutter-template-plan.md b/.amazonq/plans/cookiecutter-template-plan.md deleted file mode 100644 index ae762c2..0000000 --- a/.amazonq/plans/cookiecutter-template-plan.md +++ /dev/null @@ -1,224 +0,0 @@ -# Cookiecutter Template Modernization Plan - -## TASK PURPOSE & CRITICAL RULES - -**PURPOSE:** Update the cookiecutter template to generate files that exactly match the reference structure in `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` - -**REFERENCE DIRECTORIES (NEVER MODIFY - THESE ARE EXAMPLES):** - -- `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` - Shows what the template SHOULD generate -- `leetcode/.example/` - Generated file examples for comparison - -**ACTUAL TEMPLATE DIRECTORY (MODIFY THIS):** - -- `.templates/leetcode/{{cookiecutter.problem_name}}/` - The cookiecutter template files to update - -**WORKFLOW:** - -1. Look at `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` to see target structure -2. Modify `.templates/leetcode/{{cookiecutter.problem_name}}/` to match the reference -3. Generate with `make p-gen` -4. Compare generated files vs reference with `make p-validate` - -**ERROR PREVENTION:** The template directory does NOT have `.example` in the path! - -## Analysis Summary - -**Target Structure**: `leetcode/.example/` contains the reference implementation -**Key Differences Found:** - -- `leetcode/.example/` has `__init__.py` files (missing in old template) -- `leetcode/.example/` uses modern Python syntax (`TreeNode | None` vs `Optional[TreeNode]`) -- `leetcode/.example/` follows project's coding standards more closely -- Template must generate files identical to `leetcode/.example/` structure - -## Implementation Plan - -### 0. Explicit File Content Analysis - -- **Tool**: `.amazonq/plan/compare_template_files.py` (already exists - no need to implement) -- **Usage**: - - `poetry run python .amazonq/plan/compare_template_files.py generated --problem=PROBLEM_NAME` - Compare generated files vs reference -- **Analysis**: Line-by-line diff of all file types -- **Document**: Exact differences and required changes -- **Verify**: Template variables handle all variations - -### 1. Incremental Template Updates (File-by-File Approach) - -#### Phase 1: Add `__init__.py` - -- **Add**: Empty `__init__.py` file to template -- **Validate**: `make p-gen` → `make p-validate` → `make lint` - -#### Phase 2: Fix `solution.py` - -- **Update**: Modern syntax (`TreeNode | None`), clean template logic -- **Validate**: `make p-gen` → `make p-validate` → `make lint` - -#### Phase 3: Fix `tests.py` - -- **Update**: Relative imports (`from .solution`), clean structure -- **Validate**: `make p-gen` → `make p-validate` → `make lint` - -#### Phase 4: Fix `README.md` - -- **Update**: Clean formatting, proper markdown -- **Validate**: `make p-gen` → `make p-validate` → `make lint` - -#### Phase 5: Fix `playground.ipynb` - -- **Update**: Clean cells without execution state -- **Validate**: `make p-gen` → `make p-validate` → `make lint` - -**Benefits**: Isolated debugging, safer progression, easier rollback - -### 2. Multi-Problem Type Testing - -- **Test Cases**: Ensure template handles all problem types - - Basic array: `two_sum` (return `list[int]`) - - Tree: `invert_binary_tree` (return `TreeNode | None`) - - String: problems returning `str` - - Boolean: problems returning `bool` - - No imports vs TreeNode/ListNode imports -- **Validation**: Each type generates correctly - -### 3. Modernize cookiecutter.json Schema - -- **Base on**: Existing `.templates/leetcode/.example/examples/` JSON5 files -- **Ensure**: All template variables are properly defined -- **Add**: Missing fields found in real examples -- **Note**: Variable mapping handled by `gen.py` `convert_arrays_to_nested()` - -### 4. Update Template Files - -#### solution.py - -- Use modern type hints (`TreeNode | None` not `Optional[TreeNode]`) -- Match exact import patterns from real examples -- Ensure proper TODO placeholder format - -#### tests.py - -- Follow `@logged_test` decorator pattern -- Use parametrized pytest structure -- Match logging format from real examples - -#### README.md - -- Follow exact format from real examples -- Include proper problem description formatting - -#### playground.ipynb - -- Ensure notebook structure matches real examples - -### 5. Template Generation Logic - -- **File**: `.templates/leetcode/gen.py` (already handles variable mapping) -- **Integration**: Works with `make p-gen PROBLEM=name` (verified in Makefile) -- **Update**: Handle new `__init__.py` file -- **Process**: JSON → `gen.py` → cookiecutter → `leetcode/$(PROBLEM)/` - -### 6. Automated Validation System - -- **Tool**: Reusable `.amazonq/plan/compare_template_files.py` -- **Usage**: - ```bash - # Validate current template generates correct files - poetry run python .amazonq/plan/compare_template_files.py generated --problem=invert_binary_tree - ``` -- **Makefile**: `make p-validate PROBLEM=name` (implemented) -- **Test**: Template regression testing -- **Ensure**: `make p-gen` + `make lint` + `make p-test` all pass - -### 7. Testing & Validation - -- **Test**: Template generation with existing JSON files -- **Verify**: Generated files match `leetcode/.example/` structure exactly -- **Compare**: Automated diff against reference files -- **Ensure**: `make p-gen` works seamlessly -- **Test**: Recreation process from `.prompt/` files -- **Validate**: Multi-problem type generation - -## Key Template Variables to Ensure - -```json -{ - "problem_name": "snake_case_name", - "class_name": "PascalCaseName", - "method_name": "snake_case_method", - "problem_number": "226", - "problem_title": "Display Title", - "difficulty": "Easy|Medium|Hard", - "topics": "Comma, Separated, Topics", - "tags": ["grind-75", "blind-75"], - "problem_description": "Full description", - "examples": [{"input": "...", "output": "..."}], - "constraints": "Formatted constraints", - "parameters": "typed_params: list[int]", - "return_type": "TreeNode | None", - "imports": "from leetcode_py import TreeNode", - "test_cases": [{"args": [...], "expected": ...}] -} -``` - -## Success Criteria - -### Phase-by-Phase Validation (File-by-File) - -1. ✅ **Phase 1**: `__init__.py` files generated correctly -2. ✅ **Phase 2**: `solution.py` with modern syntax (`TreeNode | None`) -3. ✅ **Phase 3**: `tests.py` with relative imports and clean structure -4. ✅ **Phase 4**: `README.md` with clean formatting -5. ✅ **Phase 5**: `playground.ipynb` with clean cells - -### Multi-Problem Type Validation - -5. ✅ Basic problems (array/string) generate correctly -6. ✅ Tree problems generate correctly -7. ✅ Different return types handled (`bool`, `int`, `str`, `list`, etc.) - -### Automated Validation - -8. ✅ Automated diff shows no differences vs `leetcode/.example/` -9. ✅ `make p-validate` passes for all problem types -10. ✅ Recreation from `.prompt/` works flawlessly -11. ✅ All linting passes (`make lint`) -12. ✅ Tests run successfully (`make p-test`) - -## Files to Modify - -### Template Files - -1. `.templates/leetcode/{{cookiecutter.problem_name}}/` - - **Add**: `__init__.py` (empty file) - - **Update**: `solution.py` (modern syntax, imports) - - **Update**: `tests.py` (match `leetcode/.example/` format) - - **Update**: `README.md` (match `leetcode/.example/` format) - - **Update**: `playground.ipynb` (match structure) - -### Configuration - -2. `.templates/leetcode/cookiecutter.json` - - Align with JSON5 examples - - Add missing variables - -### Generation Logic - -3. `.templates/leetcode/gen.py` - - Handle `__init__.py` generation - - Maintain existing variable mapping - -### Validation Tools - -4. **Reusable**: `.amazonq/plan/compare_template_files.py` (handles both template and generated comparisons) -5. **New**: Makefile target `make p-validate` - -## Risk Mitigation - -- **Incremental phases** prevent all-or-nothing failures -- **Automated validation** catches regressions immediately -- **Multi-problem testing** ensures template generalization -- **Explicit file comparison** documents exact requirements - -This plan ensures the template generates files that exactly match `leetcode/.example/` while maintaining the robust generation process described in the rules. diff --git a/.amazonq/rules/problem-creation.md b/.amazonq/rules/problem-creation.md index 0cc55af..d67a88a 100644 --- a/.amazonq/rules/problem-creation.md +++ b/.amazonq/rules/problem-creation.md @@ -29,64 +29,101 @@ Required fields for `.templates/leetcode/json/{problem_name}.json`: **Reference examples in `.templates/leetcode/examples/` for complete templates:** - `basic.json5` - Array, string, number problems +- `design.json5` - Data structure design problems (LRU Cache, etc.) - `tree.json5` - Binary tree problems - `linked_list.json5` - Linked list problems -- `string.json5` - String manipulation problems - `matrix.json5` - 2D array/matrix problems -```json +````json { "problem_name": "two_sum", - "class_name": "TwoSum", - "method_name": "two_sum", + "solution_class_name": "Solution", "problem_number": "1", "problem_title": "Two Sum", "difficulty": "Easy", "topics": "Array, Hash Table", "tags": ["grind-75"], - "problem_description": "Given an array...", - "examples": [{ "input": "nums = [2,7,11,15], target = 9", "output": "[0,1]" }], - "constraints": "- 2 <= nums.length <= 10^4", - "parameters": "nums: list[int], target: int", - "return_type": "list[int]", - "dummy_return": "[]", - "imports": "", - "test_cases": [{ "args": [[2, 7, 11, 15], 9], "expected": [0, 1] }], - "param_names": "nums, target, expected", - "param_names_with_types": "nums: list[int], target: int, expected: list[int]", - "input_description": "nums={nums}, target={target}", - "input_params": "nums, target", - "expected_param": "expected", - "method_args": "nums, target", - "test_setup": "", - "test_logging": "", - "assertion_code": "assert result == expected", - "test_input_setup": "nums = [2, 7, 11, 15]\ntarget = 9", - "expected_output_setup": "expected = [0, 1]" + "readme_description": "Given an array of integers `nums` and an integer `target`, return indices of the two numbers such that they add up to `target`.", + "readme_examples": [ + { + "content": "```\nInput: nums = [2,7,11,15], target = 9\nOutput: [0,1]\n```\n**Explanation:** Because nums[0] + nums[1] == 9, we return [0, 1]." + } + ], + "readme_constraints": "- 2 <= nums.length <= 10^4\n- -10^9 <= nums[i] <= 10^9\n- -10^9 <= target <= 10^9\n- Only one valid answer exists.", + "readme_additional": "", + "solution_imports": "", + "solution_methods": [ + { + "name": "two_sum", + "parameters": "nums: list[int], target: int", + "return_type": "list[int]", + "dummy_return": "[]" + } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "TwoSum", + "test_helper_methods": [ + { + "name": "setup_method", + "parameters": "", + "body": "self.solution = Solution()" + } + ], + "test_methods": [ + { + "name": "test_two_sum", + "parametrize": "nums, target, expected", + "parametrize_typed": "nums: list[int], target: int, expected: list[int]", + "test_cases": "[([2, 7, 11, 15], 9, [0, 1]), ([3, 2, 4], 6, [1, 2])]", + "body": "result = self.solution.two_sum(nums, target)\nassert result == expected" + } + ], + "playground_imports": "from solution import Solution", + "playground_test_case": "# Example test case\nnums = [2, 7, 11, 15]\ntarget = 9\nexpected = [0, 1]", + "playground_execution": "result = Solution().two_sum(nums, target)\nresult", + "playground_assertion": "assert result == expected" } -``` +```` ## Naming Conventions - **problem_name**: snake_case (e.g., "two_sum", "valid_palindrome") -- **class_name**: PascalCase (e.g., "TwoSum", "ValidPalindrome") +- **solution_class_name**: Usually "Solution", except for design problems (e.g., "LRUCache") +- **test_class_name**: PascalCase (e.g., "TwoSum", "ValidPalindrome") - **method_name**: snake_case (e.g., "two_sum", "is_palindrome") - **parameters**: Use snake_case for all parameter names +### PascalCase Rules for Properties + +When creating JSON properties that use PascalCase (solution_class_name, test_class_name): + +- **Acronyms**: Keep all caps (e.g., "LRUCache" not "LruCache") +- **Roman numerals**: Keep all caps (e.g., "ReverseLinkedListII" not "ReverseLinkedListIi") +- **Common patterns**: "BST", "DFS", "BFS", "API", "URL", "HTML", "JSON", "XML" + ## Special Problem Types ### Tree Problems -- Add `"imports": "from leetcode_py import TreeNode"` +- Add `"solution_imports": "from leetcode_py import TreeNode"` - Use `TreeNode | None` for nullable tree parameters +- Test imports: Include TreeNode in test_imports - Test setup: `root = TreeNode.from_list(root_list)` ### Linked List Problems -- Add `"imports": "from leetcode_py import ListNode"` +- Add `"solution_imports": "from leetcode_py import ListNode"` - Use `ListNode | None` for nullable list parameters +- Test imports: Include ListNode in test_imports - Test setup: `head = ListNode.from_list(head_list)` +### Design Problems + +- Set `"solution_class_name"` to custom class name (e.g., "LRUCache") +- Multiple methods including `__init__` +- Complex test setup with operation sequences +- Import custom class in test_imports + ## Generation Commands ```bash diff --git a/.templates/leetcode/cookiecutter.json b/.templates/leetcode/cookiecutter.json index bf9c13a..adc4fcb 100644 --- a/.templates/leetcode/cookiecutter.json +++ b/.templates/leetcode/cookiecutter.json @@ -31,6 +31,7 @@ }, "test_imports": "import pytest\nfrom loguru import logger\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "TwoSum", "_test_helper_methods": { "list": [ { diff --git a/.templates/leetcode/examples/README.md b/.templates/leetcode/examples/README.md index 2678b3a..6dbfef8 100644 --- a/.templates/leetcode/examples/README.md +++ b/.templates/leetcode/examples/README.md @@ -84,6 +84,14 @@ This directory contains comprehensive JSON5 template examples for different type - **Test Cases**: String representation of Python data structures - **Imports**: Include necessary helper classes (TreeNode, ListNode) +#### PascalCase Naming Rules + +For `solution_class_name` and `test_class_name` properties: + +- **Acronyms**: Keep all caps ("LRUCache" not "LruCache") +- **Roman numerals**: Keep all caps ("ReverseLinkedListII" not "ReverseLinkedListIi") +- **Common patterns**: "BST", "DFS", "BFS", "API", "URL", "HTML", "JSON", "XML" + ### Template Selection Process 1. Identify problem type from description/title diff --git a/.templates/leetcode/examples/basic.json5 b/.templates/leetcode/examples/basic.json5 index a1caf73..c00a02f 100644 --- a/.templates/leetcode/examples/basic.json5 +++ b/.templates/leetcode/examples/basic.json5 @@ -1,6 +1,7 @@ { // Basic problem template - for array, string, number problems // Example: Container With Most Water, Spiral Matrix + // NOTE: PascalCase naming - keep acronyms/Roman numerals ALL CAPS (LRUCache, ReverseLinkedListII) // === PROBLEM IDENTIFICATION === "problem_name": "container_with_most_water", // snake_case: used for directory/file names @@ -48,6 +49,7 @@ // === TEST CONFIGURATION === "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "ContainerWithMostWater", // PascalCase: TestClassName for pytest class "test_helper_methods": [ { "name": "setup_method", diff --git a/.templates/leetcode/examples/design.json5 b/.templates/leetcode/examples/design.json5 index 324dead..9839fc7 100644 --- a/.templates/leetcode/examples/design.json5 +++ b/.templates/leetcode/examples/design.json5 @@ -1,81 +1,83 @@ { - // Design problem template - for data structure design problems - // Example: LRU Cache - // Key differences: Custom class name, multiple methods, complex test setup + // Design problem template - for data structure design problems + // Example: LRU Cache + // Key differences: Custom class name, multiple methods, complex test setup + // NOTE: PascalCase naming - keep acronyms/Roman numerals ALL CAPS (LRUCache, ReverseLinkedListII) - // === PROBLEM IDENTIFICATION === - "problem_name": "lru_cache", // snake_case: used for directory/file names - "solution_class_name": "LRUCache", // IMPORTANT: Custom class name for design problems - "problem_number": "146", // LeetCode problem number as string - "problem_title": "LRU Cache", // Exact title from LeetCode - "difficulty": "Medium", // Easy, Medium, Hard - "topics": "Hash Table, Linked List, Design, Doubly-Linked List", // Design-related topics - "tags": ["grind-75"], // Optional: common problem set tags + // === PROBLEM IDENTIFICATION === + problem_name: "lru_cache", // snake_case: used for directory/file names + solution_class_name: "LRUCache", // IMPORTANT: Custom class name for design problems + problem_number: "146", // LeetCode problem number as string + problem_title: "LRU Cache", // Exact title from LeetCode + difficulty: "Medium", // Easy, Medium, Hard + topics: "Hash Table, Linked List, Design, Doubly-Linked List", // Design-related topics + tags: ["grind-75"], // Optional: common problem set tags - // === README CONTENT === - // IMPORTANT: Preserve rich HTML content from LeetCode including: - // - Code snippets with backticks: `code` - // - Bold text: **bold** or bold - // - Italic text: *italic* or italic - // - Images: tags with proper src and styling - // - HTML formatting:

,
,