Skip to content

Commit fe56805

Browse files
committed
feat: complete first problem for new template
1 parent 53c5aff commit fe56805

File tree

16 files changed

+620
-41
lines changed

16 files changed

+620
-41
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python3
2+
"""Find next problem to update from json_old to json."""
3+
4+
from pathlib import Path
5+
6+
7+
def find_next_problem():
8+
"""Return next problem from json_old that's not in json."""
9+
json_old_dir = Path(".templates/leetcode/json_old")
10+
json_dir = Path(".templates/leetcode/json")
11+
12+
if not json_old_dir.exists():
13+
return "json_old directory not found"
14+
15+
if not json_dir.exists():
16+
return "json directory not found"
17+
18+
# Get all problems in json_old
19+
old_problems = {f.stem for f in json_old_dir.glob("*.json")}
20+
21+
# Get all problems in json
22+
new_problems = {f.stem for f in json_dir.glob("*.json")}
23+
24+
# Find problems that need updating
25+
missing_problems = old_problems - new_problems
26+
27+
if not missing_problems:
28+
return "All problems updated!"
29+
30+
# Return first missing problem (sorted for consistency)
31+
return sorted(missing_problems)[0]
32+
33+
34+
if __name__ == "__main__":
35+
print(find_next_problem())
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Migrate Problems to New Template Format
2+
3+
## Overview
4+
5+
Migrate all problems from old JSON format (`.templates/leetcode/json_old/`) to new template format (`.templates/leetcode/json/`) one by one.
6+
7+
## Prerequisites
8+
9+
- New template system is ready in `.templates/leetcode/{{cookiecutter.problem_name}}/`
10+
- Helper script exists: `.amazonq/plans/find_next_problem.py`
11+
12+
## Process
13+
14+
### Step 1: Find Next Problem
15+
16+
```bash
17+
python .amazonq/plans/find_next_problem.py
18+
```
19+
20+
This returns the next problem to migrate (e.g., `accounts_merge`).
21+
22+
### Step 2: Analyze Old JSON
23+
24+
```bash
25+
# Check the old format
26+
cat .templates/leetcode/json_old/accounts_merge.json
27+
```
28+
29+
Understand the structure and required variables.
30+
31+
### Step 3: Create New JSON
32+
33+
Create `.templates/leetcode/json/accounts_merge.json` with new template variables:
34+
35+
**Required Variables:**
36+
37+
- `problem_name` - Snake case name
38+
- `solution_class_name` - Usually "Solution"
39+
- `problem_number` - LeetCode number
40+
- `problem_title` - Exact title
41+
- `difficulty` - Easy/Medium/Hard
42+
- `topics` - Comma-separated topics
43+
44+
**Template-Specific Variables:**
45+
46+
- `helpers_imports` - Imports for helpers.py
47+
- `helpers_content` - Helper functions
48+
- `helpers_run_name` - Main method name
49+
- `helpers_run_signature` - Method signature
50+
- `helpers_run_body` - Method implementation
51+
- `helpers_assert_name` - Assert function name
52+
- `helpers_assert_signature` - Assert signature
53+
- `helpers_assert_body` - Assert implementation
54+
- `solution_imports` - Imports for solution.py
55+
- `solution_class_content` - Class-level content
56+
- `solution_methods` - Array of method objects
57+
- `test_imports` - Test imports
58+
- `test_methods` - Array of test method objects
59+
- `playground_imports` - Notebook imports
60+
- `playground_setup` - Test case setup
61+
- `playground_run` - Execution code
62+
- `playground_assert` - Assertion code
63+
- `readme_description` - Problem description
64+
- `readme_constraints` - Constraints
65+
66+
### Step 4: Generate and Test
67+
68+
```bash
69+
# Generate problem structure
70+
make p-gen PROBLEM=accounts_merge
71+
72+
# Lint to catch issues
73+
make p-lint PROBLEM=accounts_merge
74+
75+
# Fix any linting errors by updating JSON or generated files
76+
```
77+
78+
### Step 5: Verify Generation
79+
80+
Check generated files:
81+
82+
- `leetcode/accounts_merge/README.md`
83+
- `leetcode/accounts_merge/solution.py`
84+
- `leetcode/accounts_merge/helpers.py`
85+
- `leetcode/accounts_merge/test_solution.py`
86+
- `leetcode/accounts_merge/playground.ipynb`
87+
88+
### Step 6: Implement Solution
89+
90+
```bash
91+
# Copy implementation from old repository
92+
# Find the solution in leetcode_old/ and implement in solution.py
93+
# Replace TODO with actual working solution
94+
```
95+
96+
### Step 7: Test Functionality
97+
98+
```bash
99+
# Run tests to ensure everything works
100+
make p-test PROBLEM=accounts_merge
101+
```
102+
103+
### Step 8: Repeat
104+
105+
```bash
106+
# Find next problem
107+
python .amazonq/plans/find_next_problem.py
108+
```
109+
110+
Repeat process until script returns "All problems updated!"
111+
112+
## Migration Checklist
113+
114+
For each problem:
115+
116+
- [ ] Run `find_next_problem.py` to get next problem
117+
- [ ] Analyze old JSON structure
118+
- [ ] Create new JSON with all required variables
119+
- [ ] Run `make p-gen PROBLEM=<name>`
120+
- [ ] Run `make p-lint PROBLEM=<name>` and fix issues:
121+
- If linting fails, fix the JSON template
122+
- Re-run `make p-gen PROBLEM=<name> FORCE=1` to regenerate
123+
- Re-run `make p-lint PROBLEM=<name>` to verify
124+
- Iterate until linting passes to ensure reproducibility
125+
- [ ] Implement solution from `leetcode_old/` repository
126+
- [ ] Run `make p-test PROBLEM=<name>` to verify tests pass
127+
- [ ] Commit changes
128+
129+
## Common Patterns
130+
131+
**Basic Problem:**
132+
133+
```json
134+
{
135+
"problem_name": "two_sum",
136+
"solution_class_name": "Solution",
137+
"helpers_run_name": "two_sum",
138+
"helpers_run_signature": "(solution_class: type, nums: list[int], target: int)",
139+
"helpers_run_body": "return solution_class().two_sum(nums, target)",
140+
"solution_methods": [
141+
{
142+
"name": "two_sum",
143+
"signature": "(self, nums: list[int], target: int) -> list[int]",
144+
"body": "# TODO: Implement\nreturn []"
145+
}
146+
]
147+
}
148+
```
149+
150+
**Tree Problem:**
151+
152+
```json
153+
{
154+
"helpers_imports": "from leetcode_py import TreeNode",
155+
"helpers_content": "def create_tree(root_list: list[int | None]) -> TreeNode[int] | None:\n return TreeNode[int].from_list(root_list)",
156+
"solution_imports": "from leetcode_py import TreeNode"
157+
}
158+
```
159+
160+
**Design Problem:**
161+
162+
```json
163+
{
164+
"solution_class_name": "LRUCache",
165+
"solution_methods": [
166+
{
167+
"name": "__init__",
168+
"signature": "(self, capacity: int)",
169+
"body": "# TODO: Initialize"
170+
},
171+
{
172+
"name": "get",
173+
"signature": "(self, key: int) -> int",
174+
"body": "# TODO: Implement\nreturn -1"
175+
}
176+
]
177+
}
178+
```
179+
180+
## Progress Tracking
181+
182+
Track progress by running the script periodically:
183+
184+
```bash
185+
# Check remaining problems
186+
ls .templates/leetcode/json_old/ | wc -l # Total old problems
187+
ls .templates/leetcode/json/ | wc -l # Migrated problems
188+
python .amazonq/plans/find_next_problem.py # Next to migrate
189+
```
190+
191+
## Completion
192+
193+
When `find_next_problem.py` returns "All problems updated!", the migration is complete.

.templates/leetcode/cookiecutter.json

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,32 @@
1818
"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.",
1919
"readme_additional": "",
2020

21+
"helpers_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
22+
"helpers_content": "",
23+
"helpers_run_name": "two_sum",
24+
"helpers_run_signature": "(solution_class: type, nums: list[int], target: int)",
25+
"helpers_run_body": " implementation = solution_class()\n return implementation.two_sum(nums, target)",
26+
"helpers_assert_name": "two_sum",
27+
"helpers_assert_signature": "(result: list[int], expected: list[int]) -> bool",
28+
"helpers_assert_body": " assert result == expected\n return True",
29+
2130
"solution_imports": "",
31+
"solution_contents": "",
32+
"solution_class_content": "",
33+
34+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_two_sum, run_two_sum\nfrom .solution import Solution",
35+
"test_content": "",
36+
"test_class_name": "TwoSum",
37+
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
2238
"_solution_methods": {
2339
"list": [
2440
{
2541
"name": "two_sum",
26-
"parameters": "nums: list[int], target: int",
27-
"return_type": "list[int]",
28-
"dummy_return": "[]"
42+
"signature": "(self, nums: list[int], target: int) -> list[int]",
43+
"body": " # TODO: Implement two_sum\n return []"
2944
}
3045
]
3146
},
32-
33-
"test_imports": "import pytest\nfrom loguru import logger\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
34-
"test_class_name": "TwoSum",
3547
"_test_helper_methods": {
3648
"list": [
3749
{
@@ -45,16 +57,16 @@
4557
"list": [
4658
{
4759
"name": "test_two_sum",
60+
"signature": "(self, nums: list[int], target: int, expected: list[int])",
4861
"parametrize": "nums, target, expected",
49-
"parametrize_typed": "nums: list[int], target: int, expected: list[int]",
5062
"test_cases": "[([2, 7, 11, 15], 9, [0, 1]), ([3, 2, 4], 6, [1, 2])]",
51-
"body": "result = self.solution.two_sum(nums, target)\nassert result == expected"
63+
"body": " result = run_two_sum(Solution, nums, target)\n assert_two_sum(result, expected)"
5264
}
5365
]
5466
},
5567

56-
"playground_imports": "from solution import Solution",
57-
"playground_test_case": "# Example test case\nnums = [2, 7, 11, 15]\ntarget = 9\nexpected = [0, 1]",
58-
"playground_execution": "result = Solution().two_sum(nums, target)\nresult",
59-
"playground_assertion": "assert result == expected"
68+
"playground_imports": "from helpers import run_two_sum, assert_two_sum\nfrom solution import Solution",
69+
"playground_setup": "# Example test case\nnums = [2, 7, 11, 15]\ntarget = 9\nexpected = [0, 1]",
70+
"playground_run": "result = run_two_sum(Solution, nums, target)\nresult",
71+
"playground_assert": "assert_two_sum(result, expected)"
6072
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"problem_name": "accounts_merge",
3+
"solution_class_name": "Solution",
4+
"problem_number": "721",
5+
"problem_title": "Accounts Merge",
6+
"difficulty": "Medium",
7+
"topics": "Array, Hash Table, String, Depth-First Search, Breadth-First Search, Union Find, Sorting",
8+
"tags": ["grind-75"],
9+
"readme_description": "Given a list of `accounts` where each element `accounts[i]` is a list of strings, where the first element `accounts[i][0]` is a name, and the rest of the elements are **emails** representing emails of the account.\n\nNow, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.\n\nAfter merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails **in sorted order**. The accounts themselves can be returned in **any order**.",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: accounts = [[\"John\",\"johnsmith@mail.com\",\"john_newyork@mail.com\"],[\"John\",\"johnsmith@mail.com\",\"john00@mail.com\"],[\"Mary\",\"mary@mail.com\"],[\"John\",\"johnnybravo@mail.com\"]]\nOutput: [[\"John\",\"john00@mail.com\",\"john_newyork@mail.com\",\"johnsmith@mail.com\"],[\"Mary\",\"mary@mail.com\"],[\"John\",\"johnnybravo@mail.com\"]]\n```\n**Explanation:** The first and second John's are the same person as they have the common email \"johnsmith@mail.com\". The third John and Mary are different people as none of their email addresses are used by other accounts."
13+
},
14+
{
15+
"content": "```\nInput: accounts = [[\"Gabe\",\"Gabe0@m.co\",\"Gabe3@m.co\",\"Gabe1@m.co\"],[\"Kevin\",\"Kevin3@m.co\",\"Kevin5@m.co\",\"Kevin0@m.co\"],[\"Ethan\",\"Ethan5@m.co\",\"Ethan4@m.co\",\"Ethan0@m.co\"],[\"Hanzo\",\"Hanzo3@m.co\",\"Hanzo1@m.co\",\"Hanzo0@m.co\"],[\"Fern\",\"Fern5@m.co\",\"Fern1@m.co\",\"Fern0@m.co\"]]\nOutput: [[\"Ethan\",\"Ethan0@m.co\",\"Ethan4@m.co\",\"Ethan5@m.co\"],[\"Gabe\",\"Gabe0@m.co\",\"Gabe1@m.co\",\"Gabe3@m.co\"],[\"Hanzo\",\"Hanzo0@m.co\",\"Hanzo1@m.co\",\"Hanzo3@m.co\"],[\"Kevin\",\"Kevin0@m.co\",\"Kevin3@m.co\",\"Kevin5@m.co\"],[\"Fern\",\"Fern0@m.co\",\"Fern1@m.co\",\"Fern5@m.co\"]]\n```"
16+
}
17+
],
18+
"readme_constraints": "- `1 <= accounts.length <= 1000`\n- `2 <= accounts[i].length <= 10`\n- `1 <= accounts[i][j].length <= 30`\n- `accounts[i][0]` consists of English letters.\n- `accounts[i][j] (for j > 0)` is a valid email.",
19+
"helpers_imports": "",
20+
"helpers_run_name": "accounts_merge",
21+
"helpers_run_signature": "(solution_class: type, accounts: list[list[str]])",
22+
"helpers_run_body": " implementation = solution_class()\n return implementation.accounts_merge(accounts)",
23+
"helpers_assert_name": "accounts_merge",
24+
"helpers_assert_signature": "(result: list[list[str]], expected: list[list[str]]) -> bool",
25+
"helpers_assert_body": " # Sort both result and expected for comparison since order doesn't matter\n result_sorted = [sorted(account) for account in sorted(result)]\n expected_sorted = [sorted(account) for account in sorted(expected)]\n assert result_sorted == expected_sorted\n return True",
26+
"solution_methods": [
27+
{
28+
"name": "accounts_merge",
29+
"signature": "(self, accounts: list[list[str]]) -> list[list[str]]",
30+
"body": " # TODO: Implement accounts_merge\n return []"
31+
}
32+
],
33+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_accounts_merge, run_accounts_merge\nfrom .solution import Solution",
34+
"test_content": "",
35+
"test_class_name": "AccountsMerge",
36+
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
37+
"test_helper_methods": [],
38+
"test_methods": [
39+
{
40+
"name": "test_accounts_merge",
41+
"signature": "(self, accounts: list[list[str]], expected: list[list[str]])",
42+
"parametrize": "accounts, expected",
43+
"test_cases": "[([[\"John\", \"johnsmith@mail.com\", \"john_newyork@mail.com\"], [\"John\", \"johnsmith@mail.com\", \"john00@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]], [[\"John\", \"john00@mail.com\", \"john_newyork@mail.com\", \"johnsmith@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]]), ([[\"Gabe\", \"Gabe0@m.co\", \"Gabe3@m.co\", \"Gabe1@m.co\"], [\"Kevin\", \"Kevin3@m.co\", \"Kevin5@m.co\", \"Kevin0@m.co\"], [\"Ethan\", \"Ethan5@m.co\", \"Ethan4@m.co\", \"Ethan0@m.co\"], [\"Hanzo\", \"Hanzo3@m.co\", \"Hanzo1@m.co\", \"Hanzo0@m.co\"], [\"Fern\", \"Fern5@m.co\", \"Fern1@m.co\", \"Fern0@m.co\"]], [[\"Ethan\", \"Ethan0@m.co\", \"Ethan4@m.co\", \"Ethan5@m.co\"], [\"Gabe\", \"Gabe0@m.co\", \"Gabe1@m.co\", \"Gabe3@m.co\"], [\"Hanzo\", \"Hanzo0@m.co\", \"Hanzo1@m.co\", \"Hanzo3@m.co\"], [\"Kevin\", \"Kevin0@m.co\", \"Kevin3@m.co\", \"Kevin5@m.co\"], [\"Fern\", \"Fern0@m.co\", \"Fern1@m.co\", \"Fern5@m.co\"]])]",
44+
"body": " result = run_accounts_merge(Solution, accounts)\n assert_accounts_merge(result, expected)"
45+
}
46+
],
47+
"playground_imports": "from helpers import run_accounts_merge, assert_accounts_merge\nfrom solution import Solution",
48+
"playground_setup": "# Example test case\naccounts = [[\"John\", \"johnsmith@mail.com\", \"john_newyork@mail.com\"], [\"John\", \"johnsmith@mail.com\", \"john00@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]]\nexpected = [[\"John\", \"john00@mail.com\", \"john_newyork@mail.com\", \"johnsmith@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]]",
49+
"playground_run": "result = run_accounts_merge(Solution, accounts)\nresult",
50+
"playground_assert": "assert_accounts_merge(result, expected)"
51+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"problem_name": "test_simple",
3+
"solution_class_name": "Solution",
4+
"problem_number": "217",
5+
"problem_title": "Contains Duplicate",
6+
"difficulty": "Easy",
7+
"topics": "Array, Hash Table, Sorting",
8+
"tags": ["grind-75"],
9+
"readme_description": "Given an integer array `nums`, return `true` if any value appears **at least twice** in the array, and return `false` if every element is distinct.",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: nums = [1,2,3,1]\nOutput: true\n```\n**Explanation:** The element 1 occurs at the indices 0 and 3."
13+
},
14+
{
15+
"content": "```\nInput: nums = [1,2,3,4]\nOutput: false\n```\n**Explanation:** All elements are distinct."
16+
},
17+
{ "content": "```\nInput: nums = [1,1,1,3,3,4,3,2,4,2]\nOutput: true\n```" }
18+
],
19+
"readme_constraints": "- 1 <= nums.length <= 10^5\n- -10^9 <= nums[i] <= 10^9",
20+
"readme_additional": "",
21+
"solution_imports": "",
22+
"solution_methods": [
23+
{
24+
"name": "contains_duplicate",
25+
"parameters": "nums: list[int]",
26+
"return_type": "bool",
27+
"dummy_return": "False"
28+
}
29+
],
30+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
31+
"test_class_name": "ContainsDuplicate",
32+
"test_helper_methods": [
33+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
34+
],
35+
"test_methods": [
36+
{
37+
"name": "test_contains_duplicate",
38+
"parametrize": "nums, expected",
39+
"parametrize_typed": "nums: list[int], expected: bool",
40+
"test_cases": "[([1, 2, 3, 1], True), ([1, 2, 3, 4], False), ([1, 1, 1, 3, 3, 4, 3, 2, 4, 2], True)]",
41+
"body": "result = self.solution.contains_duplicate(nums)\nassert result == expected"
42+
}
43+
],
44+
"playground_imports": "from solution import Solution",
45+
"playground_test_case": "# Example test case\nnums = [1, 2, 3, 1]\nexpected = True",
46+
"playground_execution": "result = Solution().contains_duplicate(nums)\nresult",
47+
"playground_assertion": "assert result == expected"
48+
}

0 commit comments

Comments
 (0)