diff --git a/.amazonq/rules/problem-creation.md b/.amazonq/rules/problem-creation.md
index 8159741..97e2a21 100644
--- a/.amazonq/rules/problem-creation.md
+++ b/.amazonq/rules/problem-creation.md
@@ -42,15 +42,224 @@ Required fields for `leetcode_py/cli/resources/leetcode/json/problems/{problem_n
- `playground_assertion`: Use single quotes for string literals
- Double quotes in JSON + cookiecutter + Jupyter notebook = triple escaping issues
-**Reference the complete template example:**
-
-See `leetcode_py/cli/resources/leetcode/examples/example.json5` for a comprehensive template with:
-
-- All field definitions and variations
-- Comments explaining each field
-- Examples for different problem types (basic, tree, linked list, design, trie)
-- Proper JSON escaping rules for playground fields
-- Multiple solution class patterns
+**IMPORTANT: Create actual JSON files, not JSON5**
+
+The template below uses JSON5 format with comments for documentation purposes only. When creating the actual `.json` file, you must:
+
+1. **Remove all comments** (lines starting with `//`)
+2. **Use proper JSON syntax** with quoted property names
+3. **Save as `.json` file** (not `.json5`)
+
+**Template with comments (JSON5 format for reference only):**
+
+````json5
+{
+ // ============================================================================
+ // COMPREHENSIVE LEETCODE TEMPLATE EXAMPLE
+ // ============================================================================
+ // This example demonstrates ALL template patterns using valid_anagram as base
+ // with comprehensive comments showing variations for different problem types.
+ //
+ // REFERENCE PROBLEMS (see .templates/leetcode/json/ for complete examples):
+ // 1. valid_anagram - Basic: string parameters, boolean return
+ // 2. invert_binary_tree - Tree: TreeNode imports/parameters
+ // 3. merge_two_sorted_lists - LinkedList: ListNode imports/parameters
+ // 4. lru_cache - Design: custom class, multiple methods, operations
+ // 5. implement_trie_prefix_tree - Trie: DictTree inheritance
+ // ============================================================================
+
+ // === PROBLEM IDENTIFICATION ===
+ problem_name: "valid_anagram", // snake_case: used for directory/file names
+ solution_class_name: "Solution", // "Solution" for basic problems
+ // "LRUCache" for design problems
+ // "Trie(DictTree[str])" for inheritance
+ problem_number: "242", // LeetCode problem number as string
+ problem_title: "Valid Anagram", // Exact title from LeetCode
+ difficulty: "Easy", // Easy, Medium, Hard
+ topics: "Hash Table, String, Sorting", // Comma-separated topics from LeetCode
+ _tags: { list: ["grind-75"] }, // Optional: common problem set tags
+ // Use _tags wrapper for cookiecutter lists
+
+ // === 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: 
+ // - HTML formatting:
,
,
, - , etc.
+ // - Mathematical expressions and special characters
+ readme_description: "Given two strings `s` and `t`, return `true` if `t` is an anagram of `s`, and `false` otherwise.",
+
+ _readme_examples: {
+ // Use _readme_examples wrapper for cookiecutter lists
+ list: [
+ { content: '```\nInput: s = "anagram", t = "nagaram"\nOutput: true\n```' },
+ { content: '```\nInput: s = "rat", t = "car"\nOutput: false\n```' },
+ // For tree problems: Include images
+ // { "content": "\n\n```\nInput: root = [4,2,7,1,3,6,9]\nOutput: [4,7,2,9,6,3,1]\n```" }
+ ],
+ },
+
+ readme_constraints: "- 1 <= s.length, t.length <= 5 * 10^4\n- s and t consist of lowercase English letters.",
+ readme_additional: "**Follow up:** What if the inputs contain Unicode characters? How would you adapt your solution to such a case?",
+
+ // === HELPER FUNCTIONS ===
+ // New template system uses helper functions for cleaner test organization
+ helpers_imports: "", // Empty for basic problems
+ // "from leetcode_py import TreeNode" for tree problems
+ // "from leetcode_py import ListNode" for linked list problems
+ helpers_content: "", // Additional helper content if needed
+ helpers_run_name: "is_anagram", // Function name matching main method
+ helpers_run_signature: "(solution_class: type, s: str, t: str)",
+ // For tree: "(solution_class: type, root_list: list[int | None])"
+ // For linked list: "(solution_class: type, list1_vals: list[int], list2_vals: list[int])"
+ // For design: "(solution_class: type, operations: list[str], inputs: list[list[int]])"
+ helpers_run_body: " implementation = solution_class()\n return implementation.is_anagram(s, t)",
+ // For tree: " root = TreeNode[int].from_list(root_list)\n implementation = solution_class()\n return implementation.invert_tree(root)"
+ // For design: " cache = None\n results: list[int | None] = []\n # ... operation loop ...\n return results, cache"
+ helpers_assert_name: "is_anagram", // Function name matching main method
+ helpers_assert_signature: "(result: bool, expected: bool) -> bool",
+ // For tree: "(result: TreeNode[int] | None, expected_list: list[int | None]) -> bool"
+ // For design: "(result: list[int | None], expected: list[int | None]) -> bool"
+ helpers_assert_body: " assert result == expected\n return True",
+ // For tree: " expected = TreeNode[int].from_list(expected_list)\n assert result == expected\n return True"
+
+ // === SOLUTION TEMPLATE ===
+ solution_imports: "", // Empty for basic problems
+ // "from leetcode_py import TreeNode" for tree problems
+ // "from leetcode_py import ListNode" for linked list problems
+ // "from leetcode_py.data_structures import DictTree, RecursiveDict" for trie problems
+ solution_contents: "", // Additional content before class definition
+ solution_class_content: "", // Content inside class definition (usually empty)
+
+ // === TEST CONFIGURATION ===
+ test_imports: "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_anagram, run_is_anagram\nfrom .solution import Solution",
+ // For design: "from .solution import LRUCache" instead of Solution
+ test_content: "", // Additional test content
+ test_class_name: "ValidAnagram", // PascalCase: TestClassName for pytest class
+ test_class_content: " def setup_method(self):\n self.solution = Solution()",
+ // Empty for design problems: ""
+
+ // === SOLUTION METHODS ===
+ _solution_methods: {
+ // Use _solution_methods wrapper for cookiecutter lists
+ list: [
+ {
+ name: "is_anagram", // snake_case method name
+ signature: "(self, s: str, t: str) -> bool", // Full method signature with type hints
+ // For tree: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
+ // For linked list: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
+ body: " # TODO: Implement is_anagram\n return False",
+ // For design problems with __init__:
+ // { "name": "__init__", "signature": "(self, capacity: int) -> None", "body": " # TODO: Initialize\n pass" }
+ },
+ ],
+ },
+
+ // === TEST HELPER METHODS ===
+ _test_helper_methods: {
+ // Use _test_helper_methods wrapper for cookiecutter lists
+ list: [
+ { name: "setup_method", parameters: "", body: "self.solution = Solution()" },
+ // Empty list for design problems: []
+ ],
+ },
+
+ // === TEST METHODS ===
+ _test_methods: {
+ // Use _test_methods wrapper for cookiecutter lists
+ list: [
+ {
+ name: "test_is_anagram", // test_{method_name}
+ signature: "(self, s: str, t: str, expected: bool)", // Method signature with type hints
+ parametrize: "s, t, expected", // pytest parametrize parameters
+ // For tree: "root_list, expected_list"
+ // For design: "operations, inputs, expected"
+ test_cases: "[('anagram', 'nagaram', True), ('rat', 'car', False), ('listen', 'silent', True), ('hello', 'bello', False), ('', '', True), ('a', 'a', True), ('a', 'b', False), ('ab', 'ba', True), ('abc', 'bca', True), ('abc', 'def', False), ('aab', 'abb', False), ('aabbcc', 'abcabc', True), ('abcd', 'abcde', False), ('race', 'care', True), ('elbow', 'below', True), ('study', 'dusty', True), ('night', 'thing', True), ('stressed', 'desserts', True)]",
+ // For tree: "[([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]), ([2, 1, 3], [2, 3, 1]), ([], [])]"
+ // For design: "[(['LRUCache', 'put', 'get'], [[2], [1, 1], [1]], [None, None, 1])]"
+ body: " result = run_is_anagram(Solution, s, t)\n assert_is_anagram(result, expected)",
+ // For tree: " result = run_invert_tree(Solution, root_list)\n assert_invert_tree(result, expected_list)"
+ // For design: " result, _ = run_lru_cache(LRUCache, operations, inputs)\n assert_lru_cache(result, expected)"
+ },
+ ],
+ },
+
+ // === PLAYGROUND NOTEBOOK ===
+ // CRITICAL: Use single quotes for Python strings to avoid JSON escaping issues with Jupyter notebooks
+ // Double quotes in JSON + cookiecutter + Jupyter notebook = triple escaping issues
+ // ALWAYS use single quotes: s = 'hello', not s = "hello"
+ playground_imports: "from helpers import run_is_anagram, assert_is_anagram\nfrom solution import Solution",
+ // For tree: "from helpers import run_invert_tree, assert_invert_tree\nfrom solution import Solution\nfrom leetcode_py import TreeNode"
+ // For design: "from helpers import run_lru_cache, assert_lru_cache\nfrom solution import LRUCache"
+ playground_setup: "# Example test case\ns = 'anagram'\nt = 'nagaram'\nexpected = True",
+ // For tree: "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nexpected_list: list[int | None] = [4, 7, 2, 9, 6, 3, 1]"
+ // For design: "# Example test case\noperations = ['LRUCache', 'put', 'get']\ninputs = [[2], [1, 1], [1]]\nexpected = [None, None, 1]"
+ playground_run: "result = run_is_anagram(Solution, s, t)\nresult",
+ // For tree: "result = run_invert_tree(Solution, root_list)\nresult"
+ // For design: "result, cache = run_lru_cache(LRUCache, operations, inputs)\nprint(result)\ncache"
+ playground_assert: "assert_is_anagram(result, expected)",
+ // For tree: "assert_invert_tree(result, expected_list)"
+ // For design: "assert_lru_cache(result, expected)"
+
+ // ============================================================================
+ // PROBLEM TYPE VARIATIONS SUMMARY:
+ // ============================================================================
+ //
+ // BASIC PROBLEMS (valid_anagram):
+ // - solution_class_name: "Solution"
+ // - solution_imports: ""
+ // - Simple method signatures: "(self, s: str, t: str) -> bool"
+ // - Basic test cases: string/number parameters
+ // - Playground: single quotes for strings
+ //
+ // TREE PROBLEMS (invert_binary_tree):
+ // - solution_class_name: "Solution"
+ // - solution_imports: "from leetcode_py import TreeNode"
+ // - Tree method signatures: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
+ // - Helper functions use TreeNode.from_list()
+ // - Test cases: list representations of trees
+ // - Playground: TreeNode imports and list conversions
+ //
+ // LINKED LIST PROBLEMS (merge_two_sorted_lists):
+ // - solution_class_name: "Solution"
+ // - solution_imports: "from leetcode_py import ListNode"
+ // - List method signatures: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
+ // - Helper functions use ListNode.from_list()
+ // - Test cases: list representations of linked lists
+ // - Playground: ListNode imports and list conversions
+ //
+ // DESIGN PROBLEMS (lru_cache):
+ // - solution_class_name: "LRUCache" (custom class name)
+ // - Multiple methods including __init__
+ // - Operations-based testing: operations, inputs, expected arrays
+ // - Complex test body with operation loops
+ // - Helper functions return (results, instance) for debugging
+ // - Playground: print results, return instance
+ // - test_class_content: "" (no setup_method)
+ //
+ // INHERITANCE PROBLEMS (implement_trie_prefix_tree):
+ // - solution_class_name: "Trie(DictTree[str])" (with inheritance)
+ // - solution_imports: "from leetcode_py.data_structures import DictTree, RecursiveDict"
+ // - Custom class with inheritance from DictTree
+ // - Operations-based testing like design problems
+ // - Helper functions return (results, instance) for debugging
+ //
+ // MULTIPLE SOLUTIONS (invert_binary_tree, lru_cache):
+ // - Add parametrize for solution classes in test files:
+ // @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
+ // @pytest.mark.parametrize("solution_class", [LRUCache, LRUCacheWithDoublyList])
+ // - Update test method signature to include solution_class parameter
+ // - Import all solution classes in test file
+ // ============================================================================
+}
+````
+
+**IMPORTANT: When creating the actual .json file, convert the above JSON5 to valid JSON by:**
+
+1. **Remove all comments** (lines starting with `//`)
+2. **Keep all quoted property names** (already done above)
+3. **Save as `.json` file** (not `.json5`)
## Naming Conventions
diff --git a/Makefile b/Makefile
index 6fd145a..7145ec5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
PYTHON_VERSION = 3.13
-PROBLEM ?= design_add_and_search_words_data_structure
+PROBLEM ?= valid_sudoku
FORCE ?= 0
COMMA := ,
@@ -100,4 +100,4 @@ gen-all-problems:
@echo "Deleting existing problems..."
@rm -rf leetcode/*/
@echo "Generating all problems..."
- poetry run lcpy gen -t grind-75 -o leetcode $(if $(filter 1,$(FORCE)),--force)
+ poetry run lcpy gen --all -o leetcode $(if $(filter 1,$(FORCE)),--force)
diff --git a/leetcode/pacific_atlantic_water_flow/README.md b/leetcode/pacific_atlantic_water_flow/README.md
new file mode 100644
index 0000000..9d5e5f7
--- /dev/null
+++ b/leetcode/pacific_atlantic_water_flow/README.md
@@ -0,0 +1,59 @@
+# Pacific Atlantic Water Flow
+
+**Difficulty:** Medium
+**Topics:** Array, Depth-First Search, Breadth-First Search, Matrix
+**Tags:** grind
+
+**LeetCode:** [Problem 417](https://leetcode.com/problems/pacific-atlantic-water-flow/description/)
+
+## Problem Description
+
+There is an `m x n` rectangular island that borders both the **Pacific Ocean** and **Atlantic Ocean**. The **Pacific Ocean** touches the island's left and top edges, and the **Atlantic Ocean** touches the island's right and bottom edges.
+
+The island is partitioned into a grid of square cells. You are given an `m x n` integer matrix `heights` where `heights[r][c]` represents the **height above sea level** of the cell at coordinate `(r, c)`.
+
+The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is **less than or equal to** the current cell's height. Water can flow from any cell adjacent to an ocean into the ocean.
+
+Return _a **2D list** of grid coordinates_ `result` _where_ `result[i] = [ri, ci]` _denotes that rain water can flow from cell_ `(ri, ci)` _to **both** the Pacific and Atlantic oceans_.
+
+## Examples
+
+### Example 1:
+
+
+
+```
+Input: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
+Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
+Explanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below:
+[0,4]: [0,4] -> Pacific Ocean
+ [0,4] -> Atlantic Ocean
+[1,3]: [1,3] -> [0,3] -> Pacific Ocean
+ [1,3] -> [1,4] -> Atlantic Ocean
+[1,4]: [1,4] -> [1,3] -> [0,3] -> Pacific Ocean
+ [1,4] -> Atlantic Ocean
+[2,2]: [2,2] -> [1,2] -> [0,2] -> Pacific Ocean
+ [2,2] -> [2,3] -> [2,4] -> Atlantic Ocean
+[3,0]: [3,0] -> Pacific Ocean
+ [3,0] -> [4,0] -> Atlantic Ocean
+[3,1]: [3,1] -> [3,0] -> Pacific Ocean
+ [3,1] -> [4,1] -> Atlantic Ocean
+[4,0]: [4,0] -> Pacific Ocean
+ [4,0] -> Atlantic Ocean
+Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
+```
+
+### Example 2:
+
+```
+Input: heights = [[1]]
+Output: [[0,0]]
+Explanation: The water can flow from the only cell to the Pacific and Atlantic oceans.
+```
+
+## Constraints
+
+- `m == heights.length`
+- `n == heights[r].length`
+- `1 <= m, n <= 200`
+- `0 <= heights[r][c] <= 10^5`
diff --git a/leetcode/pacific_atlantic_water_flow/__init__.py b/leetcode/pacific_atlantic_water_flow/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/pacific_atlantic_water_flow/helpers.py b/leetcode/pacific_atlantic_water_flow/helpers.py
new file mode 100644
index 0000000..7450d9f
--- /dev/null
+++ b/leetcode/pacific_atlantic_water_flow/helpers.py
@@ -0,0 +1,8 @@
+def run_pacific_atlantic(solution_class: type, heights: list[list[int]]):
+ implementation = solution_class()
+ return implementation.pacific_atlantic(heights)
+
+
+def assert_pacific_atlantic(result: list[list[int]], expected: list[list[int]]) -> bool:
+ assert sorted(result) == sorted(expected)
+ return True
diff --git a/leetcode/pacific_atlantic_water_flow/playground.py b/leetcode/pacific_atlantic_water_flow/playground.py
new file mode 100644
index 0000000..07ddfb6
--- /dev/null
+++ b/leetcode/pacific_atlantic_water_flow/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_pacific_atlantic, run_pacific_atlantic
+from solution import Solution
+
+# %%
+# Example test case
+heights = [[1, 2, 2, 3, 5], [3, 2, 3, 4, 4], [2, 4, 5, 3, 1], [6, 7, 1, 4, 5], [5, 1, 1, 2, 4]]
+expected = [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]]
+
+# %%
+result = run_pacific_atlantic(Solution, heights)
+result
+
+# %%
+assert_pacific_atlantic(result, expected)
diff --git a/leetcode/pacific_atlantic_water_flow/solution.py b/leetcode/pacific_atlantic_water_flow/solution.py
new file mode 100644
index 0000000..bfdcfdb
--- /dev/null
+++ b/leetcode/pacific_atlantic_water_flow/solution.py
@@ -0,0 +1,37 @@
+class Solution:
+
+ # Time: O(m * n)
+ # Space: O(m * n)
+ def pacific_atlantic(self, heights: list[list[int]]) -> list[list[int]]:
+ if not heights or not heights[0]:
+ return []
+
+ m, n = len(heights), len(heights[0])
+ pacific: set[tuple[int, int]] = set()
+ atlantic: set[tuple[int, int]] = set()
+
+ def dfs(r: int, c: int, visited: set) -> None:
+ visited.add((r, c))
+ for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
+ nr, nc = r + dr, c + dc
+ if (
+ 0 <= nr < m
+ and 0 <= nc < n
+ and (nr, nc) not in visited
+ and heights[nr][nc] >= heights[r][c]
+ ):
+ dfs(nr, nc, visited)
+
+ # DFS from Pacific borders (top and left)
+ for i in range(m):
+ dfs(i, 0, pacific)
+ for j in range(n):
+ dfs(0, j, pacific)
+
+ # DFS from Atlantic borders (bottom and right)
+ for i in range(m):
+ dfs(i, n - 1, atlantic)
+ for j in range(n):
+ dfs(m - 1, j, atlantic)
+
+ return [[r, c] for r, c in pacific & atlantic]
diff --git a/leetcode/pacific_atlantic_water_flow/test_solution.py b/leetcode/pacific_atlantic_water_flow/test_solution.py
new file mode 100644
index 0000000..92e711f
--- /dev/null
+++ b/leetcode/pacific_atlantic_water_flow/test_solution.py
@@ -0,0 +1,170 @@
+import pytest
+
+from leetcode_py import logged_test
+
+from .helpers import assert_pacific_atlantic, run_pacific_atlantic
+from .solution import Solution
+
+
+class TestPacificAtlanticWaterFlow:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "heights, expected",
+ [
+ (
+ [
+ [1, 2, 2, 3, 5],
+ [3, 2, 3, 4, 4],
+ [2, 4, 5, 3, 1],
+ [6, 7, 1, 4, 5],
+ [5, 1, 1, 2, 4],
+ ],
+ [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]],
+ ),
+ ([[1]], [[0, 0]]),
+ ([[2, 1], [1, 2]], [[0, 0], [0, 1], [1, 0], [1, 1]]),
+ (
+ [[1, 2, 3], [8, 9, 4], [7, 6, 5]],
+ [[0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]],
+ ),
+ (
+ [[3, 3, 3], [3, 1, 3], [0, 2, 4]],
+ [[0, 0], [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1], [2, 2]],
+ ),
+ (
+ [[1, 1], [1, 1], [1, 1]],
+ [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]],
+ ),
+ (
+ [[10, 10, 10], [10, 1, 10], [10, 10, 10]],
+ [[0, 0], [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1], [2, 2]],
+ ),
+ (
+ [[1, 2, 3, 4], [12, 13, 14, 5], [11, 16, 15, 6], [10, 9, 8, 7]],
+ [
+ [0, 3],
+ [1, 0],
+ [1, 1],
+ [1, 2],
+ [1, 3],
+ [2, 0],
+ [2, 1],
+ [2, 2],
+ [2, 3],
+ [3, 0],
+ [3, 1],
+ [3, 2],
+ [3, 3],
+ ],
+ ),
+ (
+ [[5, 4, 3], [4, 3, 2], [3, 2, 1]],
+ [[0, 0], [0, 1], [0, 2], [1, 0], [2, 0]],
+ ),
+ (
+ [[1, 3, 2, 4], [6, 8, 7, 3], [5, 7, 6, 2], [4, 4, 5, 1]],
+ [
+ [0, 3],
+ [1, 0],
+ [1, 1],
+ [1, 2],
+ [2, 0],
+ [2, 1],
+ [2, 2],
+ [3, 0],
+ [3, 1],
+ [3, 2],
+ ],
+ ),
+ (
+ [
+ [4, 2, 7, 3, 4],
+ [2, 1, 1, 3, 2],
+ [1, 1, 2, 2, 1],
+ [2, 1, 2, 2, 1],
+ [2, 1, 1, 1, 1],
+ ],
+ [
+ [0, 0],
+ [0, 1],
+ [0, 2],
+ [0, 3],
+ [0, 4],
+ [1, 0],
+ [1, 1],
+ [1, 2],
+ [1, 3],
+ [1, 4],
+ [2, 0],
+ [2, 1],
+ [2, 2],
+ [2, 3],
+ [2, 4],
+ [3, 0],
+ [3, 1],
+ [3, 2],
+ [3, 3],
+ [3, 4],
+ [4, 0],
+ [4, 1],
+ [4, 2],
+ [4, 3],
+ [4, 4],
+ ],
+ ),
+ (
+ [
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ ],
+ [
+ [0, 0],
+ [0, 1],
+ [0, 2],
+ [0, 3],
+ [0, 4],
+ [0, 5],
+ [0, 6],
+ [0, 7],
+ [0, 8],
+ [0, 9],
+ [0, 10],
+ [0, 11],
+ [0, 12],
+ [0, 13],
+ [0, 14],
+ [0, 15],
+ [0, 16],
+ [0, 17],
+ [0, 18],
+ [0, 19],
+ [1, 0],
+ [1, 1],
+ [1, 2],
+ [1, 3],
+ [1, 4],
+ [1, 5],
+ [1, 6],
+ [1, 7],
+ [1, 8],
+ [1, 9],
+ [1, 10],
+ [1, 11],
+ [1, 12],
+ [1, 13],
+ [1, 14],
+ [1, 15],
+ [1, 16],
+ [1, 17],
+ [1, 18],
+ [1, 19],
+ ],
+ ),
+ ],
+ )
+ def test_pacific_atlantic(self, heights: list[list[int]], expected: list[list[int]]):
+ result = run_pacific_atlantic(Solution, heights)
+ assert_pacific_atlantic(result, expected)
diff --git a/leetcode/valid_sudoku/test_solution.py b/leetcode/valid_sudoku/test_solution.py
index 18054bf..420d93b 100644
--- a/leetcode/valid_sudoku/test_solution.py
+++ b/leetcode/valid_sudoku/test_solution.py
@@ -14,7 +14,6 @@ def setup_method(self):
@pytest.mark.parametrize(
"board, expected",
[
- # Valid sudoku example 1
(
[
["5", "3", ".", ".", "7", ".", ".", ".", "."],
@@ -29,7 +28,6 @@ def setup_method(self):
],
True,
),
- # Invalid sudoku example 2 (duplicate 8 in top-left box)
(
[
["8", "3", ".", ".", "7", ".", ".", ".", "."],
@@ -44,7 +42,6 @@ def setup_method(self):
],
False,
),
- # Empty board (valid)
(
[
[".", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -59,7 +56,6 @@ def setup_method(self):
],
True,
),
- # Duplicate in row (invalid)
(
[
["1", "1", ".", ".", ".", ".", ".", ".", "."],
@@ -74,7 +70,6 @@ def setup_method(self):
],
False,
),
- # Duplicate in column (invalid)
(
[
["1", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -89,7 +84,6 @@ def setup_method(self):
],
False,
),
- # Duplicate in 3x3 box (invalid)
(
[
["1", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -104,7 +98,6 @@ def setup_method(self):
],
False,
),
- # Valid single row filled
(
[
["1", "2", "3", "4", "5", "6", "7", "8", "9"],
@@ -119,7 +112,6 @@ def setup_method(self):
],
True,
),
- # Valid single column filled
(
[
["1", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -134,7 +126,6 @@ def setup_method(self):
],
True,
),
- # Valid 3x3 box filled (top-left)
(
[
["1", "2", "3", ".", ".", ".", ".", ".", "."],
@@ -149,7 +140,6 @@ def setup_method(self):
],
True,
),
- # Duplicate in middle 3x3 box (invalid)
(
[
[".", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -164,7 +154,6 @@ def setup_method(self):
],
False,
),
- # Duplicate in bottom-right 3x3 box (invalid)
(
[
[".", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -179,7 +168,6 @@ def setup_method(self):
],
False,
),
- # Multiple duplicates in same row (invalid)
(
[
["3", "3", "3", ".", ".", ".", ".", ".", "."],
@@ -194,7 +182,6 @@ def setup_method(self):
],
False,
),
- # Duplicate in last row (invalid)
(
[
[".", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -209,7 +196,6 @@ def setup_method(self):
],
False,
),
- # Duplicate in last column (invalid)
(
[
[".", ".", ".", ".", ".", ".", ".", ".", "4"],
@@ -224,7 +210,6 @@ def setup_method(self):
],
False,
),
- # Valid with all digits 1-9 in different positions
(
[
["1", ".", ".", ".", ".", ".", ".", ".", "."],
@@ -239,7 +224,6 @@ def setup_method(self):
],
True,
),
- # Valid board with multiple possible solutions (demonstrates "not necessarily solvable" constraint)
(
[
[".", ".", ".", ".", ".", ".", ".", ".", "."],
diff --git a/leetcode_py/cli/resources/leetcode/examples/example.json5 b/leetcode_py/cli/resources/leetcode/examples/example.json5
deleted file mode 100644
index 57d4159..0000000
--- a/leetcode_py/cli/resources/leetcode/examples/example.json5
+++ /dev/null
@@ -1,200 +0,0 @@
-{
- // ============================================================================
- // COMPREHENSIVE LEETCODE TEMPLATE EXAMPLE
- // ============================================================================
- // This example demonstrates ALL template patterns using valid_anagram as base
- // with comprehensive comments showing variations for different problem types.
- //
- // REFERENCE PROBLEMS (see .templates/leetcode/json/ for complete examples):
- // 1. valid_anagram - Basic: string parameters, boolean return
- // 2. invert_binary_tree - Tree: TreeNode imports/parameters
- // 3. merge_two_sorted_lists - LinkedList: ListNode imports/parameters
- // 4. lru_cache - Design: custom class, multiple methods, operations
- // 5. implement_trie_prefix_tree - Trie: DictTree inheritance
- // ============================================================================
-
- // === PROBLEM IDENTIFICATION ===
- problem_name: "valid_anagram", // snake_case: used for directory/file names
- solution_class_name: "Solution", // "Solution" for basic problems
- // "LRUCache" for design problems
- // "Trie(DictTree[str])" for inheritance
- problem_number: "242", // LeetCode problem number as string
- problem_title: "Valid Anagram", // Exact title from LeetCode
- difficulty: "Easy", // Easy, Medium, Hard
- topics: "Hash Table, String, Sorting", // Comma-separated topics from LeetCode
- _tags: { list: ["grind-75"] }, // Optional: common problem set tags
- // Use _tags wrapper for cookiecutter lists
-
- // === 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: 
- // - HTML formatting:
,
,
, - , etc.
- // - Mathematical expressions and special characters
- readme_description: "Given two strings `s` and `t`, return `true` if `t` is an anagram of `s`, and `false` otherwise.",
-
- _readme_examples: {
- // Use _readme_examples wrapper for cookiecutter lists
- list: [
- { content: '```\nInput: s = "anagram", t = "nagaram"\nOutput: true\n```' },
- { content: '```\nInput: s = "rat", t = "car"\nOutput: false\n```' },
- // For tree problems: Include images
- // { "content": "\n\n```\nInput: root = [4,2,7,1,3,6,9]\nOutput: [4,7,2,9,6,3,1]\n```" }
- ],
- },
-
- readme_constraints: "- 1 <= s.length, t.length <= 5 * 10^4\n- s and t consist of lowercase English letters.",
- readme_additional: "**Follow up:** What if the inputs contain Unicode characters? How would you adapt your solution to such a case?",
-
- // === HELPER FUNCTIONS ===
- // New template system uses helper functions for cleaner test organization
- helpers_imports: "", // Empty for basic problems
- // "from leetcode_py import TreeNode" for tree problems
- // "from leetcode_py import ListNode" for linked list problems
- helpers_content: "", // Additional helper content if needed
- helpers_run_name: "is_anagram", // Function name matching main method
- helpers_run_signature: "(solution_class: type, s: str, t: str)",
- // For tree: "(solution_class: type, root_list: list[int | None])"
- // For linked list: "(solution_class: type, list1_vals: list[int], list2_vals: list[int])"
- // For design: "(solution_class: type, operations: list[str], inputs: list[list[int]])"
- helpers_run_body: " implementation = solution_class()\n return implementation.is_anagram(s, t)",
- // For tree: " root = TreeNode[int].from_list(root_list)\n implementation = solution_class()\n return implementation.invert_tree(root)"
- // For design: " cache = None\n results: list[int | None] = []\n # ... operation loop ...\n return results, cache"
- helpers_assert_name: "is_anagram", // Function name matching main method
- helpers_assert_signature: "(result: bool, expected: bool) -> bool",
- // For tree: "(result: TreeNode[int] | None, expected_list: list[int | None]) -> bool"
- // For design: "(result: list[int | None], expected: list[int | None]) -> bool"
- helpers_assert_body: " assert result == expected\n return True",
- // For tree: " expected = TreeNode[int].from_list(expected_list)\n assert result == expected\n return True"
-
- // === SOLUTION TEMPLATE ===
- solution_imports: "", // Empty for basic problems
- // "from leetcode_py import TreeNode" for tree problems
- // "from leetcode_py import ListNode" for linked list problems
- // "from leetcode_py.data_structures import DictTree, RecursiveDict" for trie problems
- solution_contents: "", // Additional content before class definition
- solution_class_content: "", // Content inside class definition (usually empty)
-
- // === TEST CONFIGURATION ===
- test_imports: "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_is_anagram, run_is_anagram\nfrom .solution import Solution",
- // For design: "from .solution import LRUCache" instead of Solution
- test_content: "", // Additional test content
- test_class_name: "ValidAnagram", // PascalCase: TestClassName for pytest class
- test_class_content: " def setup_method(self):\n self.solution = Solution()",
- // Empty for design problems: ""
-
- // === SOLUTION METHODS ===
- _solution_methods: {
- // Use _solution_methods wrapper for cookiecutter lists
- list: [
- {
- name: "is_anagram", // snake_case method name
- signature: "(self, s: str, t: str) -> bool", // Full method signature with type hints
- // For tree: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
- // For linked list: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
- body: " # TODO: Implement is_anagram\n return False",
- // For design problems with __init__:
- // { "name": "__init__", "signature": "(self, capacity: int) -> None", "body": " # TODO: Initialize\n pass" }
- },
- ],
- },
-
- // === TEST HELPER METHODS ===
- _test_helper_methods: {
- // Use _test_helper_methods wrapper for cookiecutter lists
- list: [
- { name: "setup_method", parameters: "", body: "self.solution = Solution()" },
- // Empty list for design problems: []
- ],
- },
-
- // === TEST METHODS ===
- _test_methods: {
- // Use _test_methods wrapper for cookiecutter lists
- list: [
- {
- name: "test_is_anagram", // test_{method_name}
- signature: "(self, s: str, t: str, expected: bool)", // Method signature with type hints
- parametrize: "s, t, expected", // pytest parametrize parameters
- // For tree: "root_list, expected_list"
- // For design: "operations, inputs, expected"
- test_cases: "[('anagram', 'nagaram', True), ('rat', 'car', False), ('listen', 'silent', True), ('hello', 'bello', False), ('', '', True), ('a', 'a', True), ('a', 'b', False), ('ab', 'ba', True), ('abc', 'bca', True), ('abc', 'def', False), ('aab', 'abb', False), ('aabbcc', 'abcabc', True), ('abcd', 'abcde', False), ('race', 'care', True), ('elbow', 'below', True), ('study', 'dusty', True), ('night', 'thing', True), ('stressed', 'desserts', True)]",
- // For tree: "[([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]), ([2, 1, 3], [2, 3, 1]), ([], [])]"
- // For design: "[(['LRUCache', 'put', 'get'], [[2], [1, 1], [1]], [None, None, 1])]"
- body: " result = run_is_anagram(Solution, s, t)\n assert_is_anagram(result, expected)",
- // For tree: " result = run_invert_tree(Solution, root_list)\n assert_invert_tree(result, expected_list)"
- // For design: " result, _ = run_lru_cache(LRUCache, operations, inputs)\n assert_lru_cache(result, expected)"
- },
- ],
- },
-
- // === PLAYGROUND NOTEBOOK ===
- // CRITICAL: Use single quotes for Python strings to avoid JSON escaping issues with Jupyter notebooks
- // Double quotes in JSON + cookiecutter + Jupyter notebook = triple escaping issues
- // ALWAYS use single quotes: s = 'hello', not s = "hello"
- playground_imports: "from helpers import run_is_anagram, assert_is_anagram\nfrom solution import Solution",
- // For tree: "from helpers import run_invert_tree, assert_invert_tree\nfrom solution import Solution\nfrom leetcode_py import TreeNode"
- // For design: "from helpers import run_lru_cache, assert_lru_cache\nfrom solution import LRUCache"
- playground_setup: "# Example test case\ns = 'anagram'\nt = 'nagaram'\nexpected = True",
- // For tree: "# Example test case\nroot_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\nexpected_list: list[int | None] = [4, 7, 2, 9, 6, 3, 1]"
- // For design: "# Example test case\noperations = ['LRUCache', 'put', 'get']\ninputs = [[2], [1, 1], [1]]\nexpected = [None, None, 1]"
- playground_run: "result = run_is_anagram(Solution, s, t)\nresult",
- // For tree: "result = run_invert_tree(Solution, root_list)\nresult"
- // For design: "result, cache = run_lru_cache(LRUCache, operations, inputs)\nprint(result)\ncache"
- playground_assert: "assert_is_anagram(result, expected)",
- // For tree: "assert_invert_tree(result, expected_list)"
- // For design: "assert_lru_cache(result, expected)"
-
- // ============================================================================
- // PROBLEM TYPE VARIATIONS SUMMARY:
- // ============================================================================
- //
- // BASIC PROBLEMS (valid_anagram):
- // - solution_class_name: "Solution"
- // - solution_imports: ""
- // - Simple method signatures: "(self, s: str, t: str) -> bool"
- // - Basic test cases: string/number parameters
- // - Playground: single quotes for strings
- //
- // TREE PROBLEMS (invert_binary_tree):
- // - solution_class_name: "Solution"
- // - solution_imports: "from leetcode_py import TreeNode"
- // - Tree method signatures: "(self, root: TreeNode[int] | None) -> TreeNode[int] | None"
- // - Helper functions use TreeNode.from_list()
- // - Test cases: list representations of trees
- // - Playground: TreeNode imports and list conversions
- //
- // LINKED LIST PROBLEMS (merge_two_sorted_lists):
- // - solution_class_name: "Solution"
- // - solution_imports: "from leetcode_py import ListNode"
- // - List method signatures: "(self, list1: ListNode[int] | None, list2: ListNode[int] | None) -> ListNode[int] | None"
- // - Helper functions use ListNode.from_list()
- // - Test cases: list representations of linked lists
- // - Playground: ListNode imports and list conversions
- //
- // DESIGN PROBLEMS (lru_cache):
- // - solution_class_name: "LRUCache" (custom class name)
- // - Multiple methods including __init__
- // - Operations-based testing: operations, inputs, expected arrays
- // - Complex test body with operation loops
- // - Helper functions return (results, instance) for debugging
- // - Playground: print results, return instance
- // - test_class_content: "" (no setup_method)
- //
- // INHERITANCE PROBLEMS (implement_trie_prefix_tree):
- // - solution_class_name: "Trie(DictTree[str])" (with inheritance)
- // - solution_imports: "from leetcode_py.data_structures import DictTree, RecursiveDict"
- // - Custom class with inheritance from DictTree
- // - Operations-based testing like design problems
- // - Helper functions return (results, instance) for debugging
- //
- // MULTIPLE SOLUTIONS (invert_binary_tree, lru_cache):
- // - Add parametrize for solution classes in test files:
- // @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
- // @pytest.mark.parametrize("solution_class", [LRUCache, LRUCacheWithDoublyList])
- // - Update test method signature to include solution_class parameter
- // - Import all solution classes in test file
- // ============================================================================
-}
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/pacific_atlantic_water_flow.json b/leetcode_py/cli/resources/leetcode/json/problems/pacific_atlantic_water_flow.json
new file mode 100644
index 0000000..e43798e
--- /dev/null
+++ b/leetcode_py/cli/resources/leetcode/json/problems/pacific_atlantic_water_flow.json
@@ -0,0 +1,74 @@
+{
+ "problem_name": "pacific_atlantic_water_flow",
+ "solution_class_name": "Solution",
+ "problem_number": "417",
+ "problem_title": "Pacific Atlantic Water Flow",
+ "difficulty": "Medium",
+ "topics": "Array, Depth-First Search, Breadth-First Search, Matrix",
+ "_tags": { "list": ["grind"] },
+
+ "readme_description": "There is an `m x n` rectangular island that borders both the **Pacific Ocean** and **Atlantic Ocean**. The **Pacific Ocean** touches the island's left and top edges, and the **Atlantic Ocean** touches the island's right and bottom edges.\n\nThe island is partitioned into a grid of square cells. You are given an `m x n` integer matrix `heights` where `heights[r][c]` represents the **height above sea level** of the cell at coordinate `(r, c)`.\n\nThe island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is **less than or equal to** the current cell's height. Water can flow from any cell adjacent to an ocean into the ocean.\n\nReturn *a **2D list** of grid coordinates* `result` *where* `result[i] = [ri, ci]` *denotes that rain water can flow from cell* `(ri, ci)` *to **both** the Pacific and Atlantic oceans*.",
+
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "\n\n```\nInput: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]\nOutput: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]\nExplanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below:\n[0,4]: [0,4] -> Pacific Ocean \n [0,4] -> Atlantic Ocean\n[1,3]: [1,3] -> [0,3] -> Pacific Ocean \n [1,3] -> [1,4] -> Atlantic Ocean\n[1,4]: [1,4] -> [1,3] -> [0,3] -> Pacific Ocean \n [1,4] -> Atlantic Ocean\n[2,2]: [2,2] -> [1,2] -> [0,2] -> Pacific Ocean \n [2,2] -> [2,3] -> [2,4] -> Atlantic Ocean\n[3,0]: [3,0] -> Pacific Ocean \n [3,0] -> [4,0] -> Atlantic Ocean\n[3,1]: [3,1] -> [3,0] -> Pacific Ocean \n [3,1] -> [4,1] -> Atlantic Ocean\n[4,0]: [4,0] -> Pacific Ocean \n [4,0] -> Atlantic Ocean\nNote that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.\n```"
+ },
+ {
+ "content": "```\nInput: heights = [[1]]\nOutput: [[0,0]]\nExplanation: The water can flow from the only cell to the Pacific and Atlantic oceans.\n```"
+ }
+ ]
+ },
+
+ "readme_constraints": "- `m == heights.length`\n- `n == heights[r].length`\n- `1 <= m, n <= 200`\n- `0 <= heights[r][c] <= 10^5`",
+ "readme_additional": "",
+
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "pacific_atlantic",
+ "helpers_run_signature": "(solution_class: type, heights: list[list[int]])",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.pacific_atlantic(heights)",
+ "helpers_assert_name": "pacific_atlantic",
+ "helpers_assert_signature": "(result: list[list[int]], expected: list[list[int]]) -> bool",
+ "helpers_assert_body": " assert sorted(result) == sorted(expected)\n return True",
+
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+
+ "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_pacific_atlantic, run_pacific_atlantic\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "PacificAtlanticWaterFlow",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "pacific_atlantic",
+ "signature": "(self, heights: list[list[int]]) -> list[list[int]]",
+ "body": " # TODO: Implement pacific_atlantic\n return []"
+ }
+ ]
+ },
+
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_pacific_atlantic",
+ "signature": "(self, heights: list[list[int]], expected: list[list[int]])",
+ "parametrize": "heights, expected",
+ "test_cases": "[([[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]], [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]), ([[1]], [[0,0]]), ([[2,1],[1,2]], [[0,0],[0,1],[1,0],[1,1]]), ([[1,2,3],[8,9,4],[7,6,5]], [[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]), ([[3,3,3],[3,1,3],[0,2,4]], [[0,0],[0,1],[0,2],[1,0],[1,2],[2,0],[2,1],[2,2]]), ([[1,1],[1,1],[1,1]], [[0,0],[0,1],[1,0],[1,1],[2,0],[2,1]]), ([[10,10,10],[10,1,10],[10,10,10]], [[0,0],[0,1],[0,2],[1,0],[1,2],[2,0],[2,1],[2,2]]), ([[1,2,3,4],[12,13,14,5],[11,16,15,6],[10,9,8,7]], [[0,3],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3],[3,0],[3,1],[3,2],[3,3]]), ([[5,4,3],[4,3,2],[3,2,1]], [[0,0],[0,1],[0,2],[1,0],[2,0]]), ([[1,3,2,4],[6,8,7,3],[5,7,6,2],[4,4,5,1]], [[0,3],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]]), ([[4,2,7,3,4],[2,1,1,3,2],[1,1,2,2,1],[2,1,2,2,1],[2,1,1,1,1]], [[0,0],[0,1],[0,2],[0,3],[0,4],[1,0],[1,1],[1,2],[1,3],[1,4],[2,0],[2,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[3,3],[3,4],[4,0],[4,1],[4,2],[4,3],[4,4]]), ([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]], [[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[0,9],[0,10],[0,11],[0,12],[0,13],[0,14],[0,15],[0,16],[0,17],[0,18],[0,19],[1,0],[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[1,7],[1,8],[1,9],[1,10],[1,11],[1,12],[1,13],[1,14],[1,15],[1,16],[1,17],[1,18],[1,19]])]",
+ "body": " result = run_pacific_atlantic(Solution, heights)\n assert_pacific_atlantic(result, expected)"
+ }
+ ]
+ },
+
+ "playground_imports": "from helpers import run_pacific_atlantic, assert_pacific_atlantic\nfrom solution import Solution",
+ "playground_setup": "# Example test case\nheights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]\nexpected = [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]",
+ "playground_run": "result = run_pacific_atlantic(Solution, heights)\nresult",
+ "playground_assert": "assert_pacific_atlantic(result, expected)"
+}
diff --git a/leetcode_py/cli/resources/leetcode/json/problems/valid_sudoku.json b/leetcode_py/cli/resources/leetcode/json/problems/valid_sudoku.json
index 553e94a..f255bcb 100644
--- a/leetcode_py/cli/resources/leetcode/json/problems/valid_sudoku.json
+++ b/leetcode_py/cli/resources/leetcode/json/problems/valid_sudoku.json
@@ -60,7 +60,7 @@
"name": "test_is_valid_sudoku",
"signature": "(self, board: list[list[str]], expected: bool)",
"parametrize": "board, expected",
- "test_cases": "[([['5','3','.','.','7','.','.','.','.'],['6','.','.','1','9','5','.','.','.'],['.','9','8','.','.','.','.','6','.'],['8','.','.','.','6','.','.','.','3'],['4','.','.','8','.','3','.','.','1'],['7','.','.','.','2','.','.','.','6'],['.','6','.','.','.','.','2','8','.'],['.','.','.','4','1','9','.','.','5'],['.','.','.','.','.','.','.','7','9']], True), ([['8','3','.','.','7','.','.','.','.'],['6','.','.','1','9','5','.','.','.'],['.','9','8','.','.','.','.','6','.'],['8','.','.','.','6','.','.','.','3'],['4','.','.','8','.','3','.','.','1'],['7','.','.','.','2','.','.','.','6'],['.','6','.','.','.','.','2','8','.'],['.','.','.','4','1','9','.','.','5'],['.','.','.','.','.','.','.','7','9']], False), ([['.','.','.','.','5','.','.','1','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['9','3','.','.','2','.','4','.','.'],['.','.','7','.','.','.','3','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','3','4','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','5','.','.','.']]], False), ([['.','.','4','.','.','.','6','3','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']]], True), ([['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']]], True), ([['1','2','3','4','5','6','7','8','9'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']]], True), ([['1','1','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']]], False), ([['1','.','.','.','.','.','.','.','.'],['.','1','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']]], False), ([['1','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','1','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']]], False), ([['1','.','.','.','.','.','.','.','2'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['3','.','.','.','.','.','.','.','1']]], True), ([['.','.','.','.','.','.','5','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','5','.','.']]], False), ([['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','1','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','1','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.'],['.','.','.','.','.','.','.','.','.']], False)]",
+ "test_cases": "[([['5', '3', '.', '.', '7', '.', '.', '.', '.'], ['6', '.', '.', '1', '9', '5', '.', '.', '.'], ['.', '9', '8', '.', '.', '.', '.', '6', '.'], ['8', '.', '.', '.', '6', '.', '.', '.', '3'], ['4', '.', '.', '8', '.', '3', '.', '.', '1'], ['7', '.', '.', '.', '2', '.', '.', '.', '6'], ['.', '6', '.', '.', '.', '.', '2', '8', '.'], ['.', '.', '.', '4', '1', '9', '.', '.', '5'], ['.', '.', '.', '.', '8', '.', '.', '7', '9']], True), ([['8', '3', '.', '.', '7', '.', '.', '.', '.'], ['6', '.', '.', '1', '9', '5', '.', '.', '.'], ['.', '9', '8', '.', '.', '.', '.', '6', '.'], ['8', '.', '.', '.', '6', '.', '.', '.', '3'], ['4', '.', '.', '8', '.', '3', '.', '.', '1'], ['7', '.', '.', '.', '2', '.', '.', '.', '6'], ['.', '6', '.', '.', '.', '.', '2', '8', '.'], ['.', '.', '.', '4', '1', '9', '.', '.', '5'], ['.', '.', '.', '.', '8', '.', '.', '7', '9']], False), ([['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], True), ([['1', '1', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], False), ([['1', '.', '.', '.', '.', '.', '.', '.', '.'], ['1', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], False), ([['1', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '1', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], False), ([['1', '2', '3', '4', '5', '6', '7', '8', '9'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], True), ([['1', '.', '.', '.', '.', '.', '.', '.', '.'], ['2', '.', '.', '.', '.', '.', '.', '.', '.'], ['3', '.', '.', '.', '.', '.', '.', '.', '.'], ['4', '.', '.', '.', '.', '.', '.', '.', '.'], ['5', '.', '.', '.', '.', '.', '.', '.', '.'], ['6', '.', '.', '.', '.', '.', '.', '.', '.'], ['7', '.', '.', '.', '.', '.', '.', '.', '.'], ['8', '.', '.', '.', '.', '.', '.', '.', '.'], ['9', '.', '.', '.', '.', '.', '.', '.', '.']], True), ([['1', '2', '3', '.', '.', '.', '.', '.', '.'], ['4', '5', '6', '.', '.', '.', '.', '.', '.'], ['7', '8', '9', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], True), ([['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '5', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '5', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], False), ([['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '7', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '7', '.']], False), ([['3', '3', '3', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], False), ([['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['9', '.', '.', '.', '.', '.', '.', '.', '9']], False), ([['.', '.', '.', '.', '.', '.', '.', '.', '4'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '4']], False), ([['1', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '2', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '3', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '4', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '5', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '6', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '7', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '8', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '9']], True), ([['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '1', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.', '.', '.']], True)]",
"body": " result = run_is_valid_sudoku(Solution, board)\n assert_is_valid_sudoku(result, expected)"
}
]
diff --git a/leetcode_py/cli/resources/leetcode/json/tags.json5 b/leetcode_py/cli/resources/leetcode/json/tags.json5
index e9c6c53..b697722 100644
--- a/leetcode_py/cli/resources/leetcode/json/tags.json5
+++ b/leetcode_py/cli/resources/leetcode/json/tags.json5
@@ -87,6 +87,7 @@
"house_robber",
"maximum_product_subarray",
"next_permutation",
+ "pacific_atlantic_water_flow",
"valid_sudoku",
],
diff --git a/leetcode_py/tools/check_test_cases.py b/leetcode_py/tools/check_test_cases.py
index fbbad79..c6871d3 100644
--- a/leetcode_py/tools/check_test_cases.py
+++ b/leetcode_py/tools/check_test_cases.py
@@ -21,9 +21,15 @@ def count_test_cases_for_problem(json_data: dict[str, Any]) -> int:
for method in test_methods:
test_cases = method.get("test_cases", "")
if test_cases.strip():
- # Parse Python list literal using ast.literal_eval
- cases_list = ast.literal_eval(test_cases)
- total += len(cases_list)
+ try:
+ # Parse Python list literal using ast.literal_eval
+ cases_list = ast.literal_eval(test_cases)
+ total += len(cases_list)
+ except (ValueError, SyntaxError) as e:
+ # Re-raise with more context
+ raise ValueError(
+ f"Failed to parse test_cases in method '{method.get('name', 'unknown')}': {e}"
+ )
return total
@@ -38,6 +44,7 @@ def check_test_cases(
"""Check test case counts in LeetCode problems."""
problems_dir = get_problems_json_path()
all_problems: list[tuple[str, int]] = []
+ errors_found = False
for problem_file in problems_dir.glob("*.json"):
try:
@@ -48,6 +55,12 @@ def check_test_cases(
all_problems.append((problem_file.name, test_count))
except Exception as e:
typer.echo(f"Error reading problem {problem_file.name}: {e}", err=True)
+ errors_found = True
+ # Continue processing other files instead of stopping
+
+ # Don't exit immediately - show results for files that worked
+ if errors_found:
+ typer.echo("\nNote: Some files had errors and were skipped.", err=True)
# Sort by test count
all_problems.sort(key=lambda x: x[1])
@@ -69,8 +82,8 @@ def check_test_cases(
for problem_name, count in filtered_problems:
typer.echo(f"{problem_name}: {count} test cases")
- # Exit with non-zero code if any problems found
- if filtered_problems:
+ # Exit with non-zero code if any problems found or if there were errors
+ if filtered_problems or errors_found:
raise typer.Exit(1)