|
| 1 | +# Plan: Improve Cookiecutter Template for LeetCode Problems |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +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. |
| 6 | + |
| 7 | +## Current Issues Identified |
| 8 | + |
| 9 | +1. **Hard-coded class name**: Template assumes `Solution` class but some problems use custom classes (e.g., `LRUCache`) |
| 10 | +2. **Single method assumption**: Template assumes one method but some problems have multiple methods (`__init__`, `get`, `put`) |
| 11 | +3. **Complex test setup**: Current template doesn't handle operation-based testing for design problems |
| 12 | +4. **Import handling**: Need better solution import structure |
| 13 | +5. **Template parameter explosion**: Too many derived parameters that could be simplified |
| 14 | + |
| 15 | +## Target Structure |
| 16 | + |
| 17 | +``` |
| 18 | +.templates/leetcode/ |
| 19 | +├── {{cookiecutter.problem_name}}/ |
| 20 | +│ ├── __init__.py |
| 21 | +│ ├── solution.py |
| 22 | +│ ├── tests.py |
| 23 | +│ ├── README.md |
| 24 | +│ └── playground.ipynb |
| 25 | +└── cookiecutter.json |
| 26 | +``` |
| 27 | + |
| 28 | +## Phase 1: Analyze Problem Types |
| 29 | + |
| 30 | +### 1.1 Universal Template Variables |
| 31 | + |
| 32 | +- `solution_imports`: Required imports for solution.py (empty, TreeNode, ListNode, etc.) |
| 33 | +- `test_imports`: All import lines for tests.py (pytest, loguru, TreeNode, test_utils, solution class) |
| 34 | +- `solution_class_name`: Dynamic class name (Solution, LRUCache, etc.) |
| 35 | +- `readme_description`: Problem description for README (supports HTML including images, preserves code snippets like `x`, `y`, `some_params`) |
| 36 | +- `readme_examples`: Examples for README (supports HTML including images, typically uses code blocks for input/output) |
| 37 | +- `readme_constraints`: Constraints for README |
| 38 | +- `readme_additional`: Additional README sections |
| 39 | +- `solution_methods`: List of methods with parameters/return types |
| 40 | +- `test_methods`: List of test methods with parametrize and body |
| 41 | +- `test_helper_methods`: List of test helper methods (setup_method, teardown_method, utility methods, etc.) |
| 42 | + |
| 43 | +## Phase 2: Create New cookiecutter.json |
| 44 | + |
| 45 | +### 2.1 Complete Template Variables Example |
| 46 | + |
| 47 | +````json |
| 48 | +{ |
| 49 | + "problem_name": "two_sum", |
| 50 | + "solution_class_name": "Solution", |
| 51 | + "problem_number": "1", |
| 52 | + "problem_title": "Two Sum", |
| 53 | + "difficulty": "Easy", |
| 54 | + "topics": "Array, Hash Table", |
| 55 | + "tags": ["grind-75"], |
| 56 | + |
| 57 | + "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<img src=\"example1.png\" alt=\"Array Example\" width=\"300\">\n\nYou may assume that each input would have exactly one solution, and you may not use the same element twice.", |
| 58 | + "readme_examples": [ |
| 59 | + { |
| 60 | + "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]." |
| 61 | + }, |
| 62 | + { |
| 63 | + "content": "<img src=\"tree_example.png\" alt=\"Binary Tree\" width=\"200\">\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." |
| 64 | + } |
| 65 | + ], |
| 66 | + "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.", |
| 67 | + "readme_additional": "", |
| 68 | + |
| 69 | + "solution_imports": "", |
| 70 | + "solution_methods": [ |
| 71 | + { |
| 72 | + "name": "two_sum", |
| 73 | + "parameters": "nums: list[int], target: int", |
| 74 | + "return_type": "list[int]", |
| 75 | + "dummy_return": "[]" |
| 76 | + } |
| 77 | + ], |
| 78 | + |
| 79 | + "test_imports": "import pytest\nfrom loguru import logger\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", |
| 80 | + "test_helper_methods": [ |
| 81 | + { |
| 82 | + "name": "setup_method", |
| 83 | + "parameters": "", |
| 84 | + "body": "self.solution = Solution()" |
| 85 | + } |
| 86 | + ], |
| 87 | + "test_methods": [ |
| 88 | + { |
| 89 | + "name": "test_two_sum", |
| 90 | + "parametrize": "nums, target, expected", |
| 91 | + "test_cases": "[([2, 7, 11, 15], 9, [0, 1]), ([3, 2, 4], 6, [1, 2])]", |
| 92 | + "body": "result = self.solution.two_sum(nums, target)\nassert result == expected" |
| 93 | + } |
| 94 | + ], |
| 95 | + |
| 96 | + "playground_imports": "from solution import Solution", |
| 97 | + "playground_test_case": "# Example test case\nnums = [2, 7, 11, 15]\ntarget = 9\nexpected = [0, 1]", |
| 98 | + "playground_execution": "result = Solution().two_sum(nums, target)\nresult", |
| 99 | + "playground_assertion": "assert result == expected" |
| 100 | +} |
| 101 | +```` |
| 102 | + |
| 103 | +## Phase 3: Update Template Files |
| 104 | + |
| 105 | +### 3.1 solution.py Template |
| 106 | + |
| 107 | +```python |
| 108 | +{{cookiecutter.solution_imports}} |
| 109 | + |
| 110 | +class {{cookiecutter.solution_class_name}}: |
| 111 | + {% for method in cookiecutter.solution_methods %} |
| 112 | + # Time: O(?) |
| 113 | + # Space: O(?) |
| 114 | + def {{method.name}}(self, {{method.parameters}}) -> {{method.return_type}}: |
| 115 | + # TODO: Implement {{method.name}} |
| 116 | + return {{method.dummy_return}} |
| 117 | + |
| 118 | + {% endfor %} |
| 119 | +``` |
| 120 | + |
| 121 | +### 3.2 tests.py Template |
| 122 | + |
| 123 | +```python |
| 124 | +{{cookiecutter.test_imports}} |
| 125 | + |
| 126 | +class Test{{cookiecutter.solution_class_name}}: |
| 127 | + {% for method in cookiecutter.test_helper_methods %} |
| 128 | + def {{method.name}}(self{% if method.parameters %}, {{method.parameters}}{% endif %}): |
| 129 | + {{method.body}} |
| 130 | + |
| 131 | + {% endfor %} |
| 132 | + |
| 133 | + {% for method in cookiecutter.test_methods %} |
| 134 | + @pytest.mark.parametrize("{{method.parametrize}}", {{method.test_cases}}) |
| 135 | + @logged_test |
| 136 | + def {{method.name}}(self, {{method.parametrize}}): |
| 137 | + {{method.body}} |
| 138 | + |
| 139 | + {% endfor %} |
| 140 | +``` |
| 141 | + |
| 142 | +### 3.3 README.md Template |
| 143 | + |
| 144 | +```markdown |
| 145 | +# {{cookiecutter.problem_title}} |
| 146 | + |
| 147 | +**Difficulty:** {{cookiecutter.difficulty}} |
| 148 | +**Topics:** {{cookiecutter.topics}} |
| 149 | +**Tags:** {{cookiecutter.tags | join(', ')}} |
| 150 | +{% if cookiecutter.problem_number %} |
| 151 | +**LeetCode:** [Problem {{cookiecutter.problem_number}}](https://leetcode.com/problems/{{cookiecutter.problem_name.replace('_', "-")}}/description/) |
| 152 | +{% endif %} |
| 153 | + |
| 154 | +## Problem Description |
| 155 | + |
| 156 | +{{cookiecutter.readme_description}} |
| 157 | + |
| 158 | +## Examples |
| 159 | + |
| 160 | +{% for example in cookiecutter.readme_examples %} |
| 161 | + |
| 162 | +### Example {{loop.index}}: |
| 163 | + |
| 164 | +{{example.content}} |
| 165 | +{% endfor %} |
| 166 | + |
| 167 | +## Constraints |
| 168 | + |
| 169 | +{{cookiecutter.readme_constraints}} |
| 170 | + |
| 171 | +{% if cookiecutter.readme_additional %} |
| 172 | +{{cookiecutter.readme_additional}} |
| 173 | +{% endif %} |
| 174 | +``` |
| 175 | + |
| 176 | +### 3.4 playground.ipynb Template |
| 177 | + |
| 178 | +```json |
| 179 | +{ |
| 180 | + "cells": [ |
| 181 | + { |
| 182 | + "cell_type": "code", |
| 183 | + "id": "imports", |
| 184 | + "source": ["{{cookiecutter.playground_imports}}"] |
| 185 | + }, |
| 186 | + { |
| 187 | + "cell_type": "code", |
| 188 | + "id": "setup", |
| 189 | + "source": ["{{cookiecutter.playground_test_case}}"] |
| 190 | + }, |
| 191 | + { |
| 192 | + "cell_type": "code", |
| 193 | + "id": "execute", |
| 194 | + "source": ["{{cookiecutter.playground_execution}}"] |
| 195 | + }, |
| 196 | + { |
| 197 | + "cell_type": "code", |
| 198 | + "id": "test", |
| 199 | + "source": ["{{cookiecutter.playground_assertion}}"] |
| 200 | + } |
| 201 | + ] |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +## Phase 4: Simplify Parameter Generation |
| 206 | + |
| 207 | +### 4.1 Reduce Template Complexity |
| 208 | + |
| 209 | +- Remove derived parameters like `param_names`, `input_description`, etc. |
| 210 | +- Generate these dynamically in the template using Jinja2 filters |
| 211 | +- Focus on core data: methods, test_cases, problem metadata |
| 212 | + |
| 213 | +### 4.2 Smart Defaults |
| 214 | + |
| 215 | +- Auto-generate method names from problem names |
| 216 | +- Auto-generate class names from problem names |
| 217 | +- Default to "basic" problem type unless specified |
| 218 | + |
| 219 | +## Phase 5: Testing & Validation |
| 220 | + |
| 221 | +### 5.1 Test with Existing Problems |
| 222 | + |
| 223 | +- Generate all current problems using new template |
| 224 | +- Compare output with existing generated files |
| 225 | +- Ensure no regression in functionality |
| 226 | + |
| 227 | +### 5.2 Test Special Cases |
| 228 | + |
| 229 | +- LRU Cache (design problem) |
| 230 | +- Tree problems (TreeNode imports) |
| 231 | +- Linked list problems (ListNode imports) |
| 232 | +- Matrix problems (2D arrays) |
| 233 | + |
| 234 | +## Phase 6: Integration |
| 235 | + |
| 236 | +### 6.1 Update Generation Scripts |
| 237 | + |
| 238 | +- Modify `gen.py` to work with new template structure |
| 239 | +- Update Makefile commands |
| 240 | +- Ensure backward compatibility with existing JSON files |
| 241 | + |
| 242 | +### 6.2 Documentation |
| 243 | + |
| 244 | +- Update problem creation guide |
| 245 | +- Create examples for each problem type |
| 246 | +- Document new template variables |
| 247 | + |
| 248 | +## Success Criteria |
| 249 | + |
| 250 | +1. ✅ Single cookiecutter template handles all problem types |
| 251 | +2. ✅ Reduced template complexity (fewer derived parameters) |
| 252 | +3. ✅ Support for design problems with multiple methods |
| 253 | +4. ✅ Proper imports for tree/linked list problems |
| 254 | +5. ✅ Clean, maintainable template structure |
| 255 | +6. ✅ All existing problems can be regenerated without issues |
| 256 | +7. ✅ New problem types can be easily added |
| 257 | + |
| 258 | +## Implementation Order |
| 259 | + |
| 260 | +1. Create new `cookiecutter.json` structure |
| 261 | +2. Update template files with conditional logic |
| 262 | +3. Test with basic problems (Two Sum) |
| 263 | +4. Test with design problems (LRU Cache) |
| 264 | +5. Test with tree/linked list problems |
| 265 | +6. Validate all existing problems |
| 266 | +7. Update generation scripts and documentation |
0 commit comments