diff --git a/README.md b/README.md index 14c6766..58fbf3b 100755 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ chmod +x utils/leetcode_helper.sh # 範例 ./utils/leetcode_helper.sh problem 1 two-sum Easy ./utils/leetcode_helper.sh problem 3000 maximum-area-rectangle Medium +# 顯示說明: +./utils/leetcode_helper.sh --help ``` 建立完成後的結構(示意): @@ -62,8 +64,7 @@ problems/NNNN-name/ ├── SolutionTests.cs # xUnit 測試 ├── TestProject.csproj ├── main_test.go # Go testing - ├── edge_cases.md - └── run_tests.sh # 一鍵執行 C# 與 Go 測試 + └── edge_cases.md ``` - 建立月度日誌(依模板生成 `logs/YYYY-MM.md`) @@ -97,8 +98,16 @@ go run main.go - 執行該題的測試(同時跑 C# 與 Go): ``` -cd problems/NNNN-name/test -./run_tests.sh +./utils/run_tests.sh NNNN-name [all|csharp|go] +# 也可只給題號: +./utils/run_tests.sh 3025 +# 僅跑 C# 或 Go: +./utils/run_tests.sh 3025 csharp +./utils/run_tests.sh 3025 go +# 顯示說明: +./utils/run_tests.sh --help +# 列出目前可用題目: +./utils/run_tests.sh --list ``` 或分別執行: @@ -116,7 +125,7 @@ go test -v - problem:根據輸入的題號、名稱與難度,建立一個完整題目骨架 - 產生題目 `README.md`(含題目資訊、思路、測試與筆記段落) - 建立 `csharp/` 與 `go/` 目錄及基本程式碼 - - 建立 `test/` 目錄,內含 xUnit、Go 測試模板與 `run_tests.sh` + - 建立 `test/` 目錄,內含 xUnit、Go 測試模板(統一用 `utils/run_tests.sh` 執行) - 初始化 Go module(`go.mod`) - readme:掃描 `problems/` 題目數量,輸出簡單統計到終端(可再擴充寫回本檔) - log:依 `templates/logs-template.md.tmpl` 生成月誌雛形,方便每日記錄與月度回顧 @@ -234,6 +243,28 @@ func TestFindClosest(t *testing.T) { - 月誌:`templates/logs-template.md.tmpl` 不需修改腳本本身即可客製化模板。 +## 工具清單 + +- `utils/leetcode_helper.sh`: 管理題目與月誌 + - 功能: + - `problem <題號> <題目名稱-kebab> <難度>`:建立新題目(產生 `README.md`、`csharp/`、`go/`、`test/` 骨架) + - `log [YYYY-MM]`:建立月度學習日誌(預設本月) + - `readme`:顯示題目數量統計 + - `-h|--help`:顯示說明 + - 範例: + - `./utils/leetcode_helper.sh problem 3025 find-the-number-of-ways-to-place-people-i Medium` + - `./utils/leetcode_helper.sh log 2025-09` + - `./utils/leetcode_helper.sh --help` + +- `utils/run_tests.sh`: 執行指定題目的測試 + - 語法:`./utils/run_tests.sh [all|csharp|go]` + - 可為:題號(如 `3025`)/ 目錄名(如 `3025-find-…`)/ 完整路徑(如 `problems/3025-…`) + - 參數:`--help` 顯示說明、`--list` 列出現有題目 + - 範例: + - `./utils/run_tests.sh 3025`(同時跑 C# 與 Go) + - `./utils/run_tests.sh 3025 csharp`(只跑 C#) + - `./utils/run_tests.sh --list` + ## 未來可擴充方向 - 主 README 自動彙整各題狀態(難度、語言、連結、完成度) diff --git a/problems/0042-answer-to-everything/README.md b/problems/0042-answer-to-everything/README.md deleted file mode 100644 index f8180c0..0000000 --- a/problems/0042-answer-to-everything/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# [42] Answer To Everything - -## 題目資訊 -- **難度**: Easy -- **標籤**: -- **題目連結**: https://leetcode.com/problems/answer-to-everything/ -- **練習日期**: 2025-09-12 -- **目標複雜度**: 時間 O(?)、空間 O(?) - -## 題目描述 -> 在這裡貼上題目的完整描述(或重點) - -## 先備條件與限制 -- 輸入限制:n ∈ [?, ?]、值域 ∈ [?, ?] -- 回傳/輸出格式:... -- 其他:是否允許排序/就地修改 - -## 解題思路 - -### 初步分析 -- 類型:雙指針 / 滑動視窗 / 排序 / DP / 貪心 / 圖論 ... -- 關鍵觀察: -- 複雜度目標理由: - -### 解法比較 -1. 解法A(基準/暴力): - - 思路: - - 正確性: - - 複雜度:O(?) / O(?) -2. 解法B(優化): - - 思路: - - 正確性: - - 複雜度:O(?) / O(?) - -### 乾跑(Dry Run) -- 範例:... - -## 實作細節與 API 設計 - -### C# 方法簽名(示意) -```csharp -public class Solution { - // TODO: 根據題意調整簽名 - public int Solve(int[] nums) { - return 0; - } -} -``` - -### Go 方法簽名(示意) -```go -func solve(nums []int) int { - return 0 -} -``` - -### 常見陷阱 -- 邊界:空/單一/極值/全相等 -- 去重:排序後跳重複、集合 -- 溢位:使用 64-bit - -## 測試案例 - -### 範例輸入輸出 -``` -Input: ... -Output: ... -Explanation: ... -``` - -### 邊界清單 -- [ ] 空陣列/空字串 -- [ ] 單一元素 / 全相同 -- [ ] 含負數/0/大數 -- [ ] 去重 -- [ ] 大資料壓力 - -## 複雜度分析 -- 最壞:時間 O(?)、空間 O(?) - -## 相關題目 / Follow-up -- - -## 學習筆記 -- 今天學到: -- 卡住與修正: -- 待優化: - ---- -**總結**:這題的核心在於 ______,適合練習 ______。 - diff --git a/problems/0042-answer-to-everything/csharp/Program.cs b/problems/0042-answer-to-everything/csharp/Program.cs deleted file mode 100644 index c5e5c74..0000000 --- a/problems/0042-answer-to-everything/csharp/Program.cs +++ /dev/null @@ -1,24 +0,0 @@ -// LeetCode 42: Answer To Everything -// 難度: Easy -// 日期: 2025-09-12 - -using System; -using System.Collections.Generic; -using System.Linq; - -public class Solution { - // TODO: 根據題意調整簽名 - public int Solve(int[] nums) { - // 實作解法 - return 0; - } -} - -public class Program { - public static void Main() { - var s = new Solution(); - // TODO: 可加入簡單輸入/輸出測試 - Console.WriteLine("Hello LeetCode 42!"); - } -} - diff --git a/problems/0042-answer-to-everything/csharp/csharp.csproj b/problems/0042-answer-to-everything/csharp/csharp.csproj deleted file mode 100644 index 9dbab49..0000000 --- a/problems/0042-answer-to-everything/csharp/csharp.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - Exe - net8.0 - enable - enable - - - diff --git a/problems/0042-answer-to-everything/go/main.go b/problems/0042-answer-to-everything/go/main.go deleted file mode 100644 index 697cedc..0000000 --- a/problems/0042-answer-to-everything/go/main.go +++ /dev/null @@ -1,18 +0,0 @@ -// LeetCode 42: Answer To Everything -// 難度: Easy -// 日期: 2025-09-12 - -package main - -import "fmt" - -// TODO: 根據題意調整簽名 -func solve(nums []int) int { - return 0 -} - -func main() { - fmt.Printf("Hello LeetCode 42!\n") - // TODO: 可加入簡單測試 -} - diff --git a/problems/0042-answer-to-everything/test/SolutionTests.cs b/problems/0042-answer-to-everything/test/SolutionTests.cs deleted file mode 100644 index c0cfc74..0000000 --- a/problems/0042-answer-to-everything/test/SolutionTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -// LeetCode 42 單元測試(xUnit) - -using Xunit; - -public class SolutionTests { - private readonly Solution _s = new Solution(); - - [Fact] - public void Example1() { - // TODO: Arrange - // var input = new int[] { }; - // var expected = 0; - // Act - // var got = _s.Solve(input); - // Assert.Equal(expected, got); - Assert.True(true); - } - - [Fact] - public void EdgeCases() { - Assert.True(true); - } -} - diff --git a/problems/0042-answer-to-everything/test/TestProject.csproj b/problems/0042-answer-to-everything/test/TestProject.csproj deleted file mode 100644 index e2d8b41..0000000 --- a/problems/0042-answer-to-everything/test/TestProject.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - net8.0 - enable - false - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - diff --git a/problems/0042-answer-to-everything/test/edge_cases.md b/problems/0042-answer-to-everything/test/edge_cases.md deleted file mode 100644 index 4b1a927..0000000 --- a/problems/0042-answer-to-everything/test/edge_cases.md +++ /dev/null @@ -1,20 +0,0 @@ -# 邊界情況清單(42 Answer To Everything) - -## 需要測試的邊界 -- [ ] 空輸入 / 單一元素 -- [ ] 重複元素 / 全相同 -- [ ] 極值(最小/最大) -- [ ] 含負數 / 0 / 大數 -- [ ] 大資料量(接近上限) - -## 額外案例 -### 案例 1 -- 輸入: -- 預期: -- 說明: - -### 案例 2 -- 輸入: -- 預期: -- 說明: - diff --git a/problems/0042-answer-to-everything/test/main_test.go b/problems/0042-answer-to-everything/test/main_test.go deleted file mode 100644 index 8780fa7..0000000 --- a/problems/0042-answer-to-everything/test/main_test.go +++ /dev/null @@ -1,12 +0,0 @@ -// LeetCode 42 單元測試(Go testing) -package main - -import "testing" - -func TestExample(t *testing.T) { - // TODO: input := []int{} - // got := solve(input) - // want := 0 - // if got != want { t.Fatalf("want %v got %v", want, got) } -} - diff --git a/problems/0042-answer-to-everything/test/run_tests.sh b/problems/0042-answer-to-everything/test/run_tests.sh deleted file mode 100755 index 4dac6e1..0000000 --- a/problems/0042-answer-to-everything/test/run_tests.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -echo "🧪 執行測試: 0042-answer-to-everything" -echo "==========================" - -pushd "$(dirname "$0")" >/dev/null - -echo "📋 C# 測試結果:" -if (cd ../csharp && dotnet build >/dev/null 2>&1); then - if dotnet test --logger "console;verbosity=minimal" >/dev/null 2>&1; then - echo "✅ C# 測試通過" - else - echo "❌ C# 測試失敗" - fi -else - echo "❌ C# 編譯失敗" -fi - -echo "" -echo "📋 Go 測試結果:" -if (cd ../go && go build >/dev/null 2>&1); then - if go test -v >/dev/null 2>&1; then - echo "✅ Go 測試通過" - else - echo "❌ Go 測試失敗(顯示詳細)" - go test -v - fi -else - echo "❌ Go 編譯失敗" -fi - -popd >/dev/null -echo "" -echo "📊 測試完成!" - diff --git a/problems/1792-maximum-average-pass-ratio/test/run_tests.sh b/problems/1792-maximum-average-pass-ratio/test/run_tests.sh deleted file mode 100755 index 1137923..0000000 --- a/problems/1792-maximum-average-pass-ratio/test/run_tests.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -echo "🧪 執行 LeetCode 題目測試" -echo "==========================" - -# 執行 C# 測試 -echo "📋 C# 測試結果:" -cd ../csharp -if dotnet build > /dev/null 2>&1; then - cd ../test - if dotnet test --logger "console;verbosity=minimal" 2>/dev/null; then - echo "✅ C# 測試通過" - else - echo "❌ C# 測試失敗" - fi -else - echo "❌ C# 編譯失敗" -fi - -echo "" -echo "📊 測試完成!" diff --git a/templates/problem/test/run_tests.sh.tmpl b/templates/problem/test/run_tests.sh.tmpl deleted file mode 100644 index cdd6c2f..0000000 --- a/templates/problem/test/run_tests.sh.tmpl +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -echo "🧪 執行測試: {{NUMBER_PAD}}-{{NAME_SLUG}}" -echo "==========================" - -pushd "$(dirname "$0")" >/dev/null - -echo "📋 C# 測試結果:" -if (cd ../csharp && dotnet build >/dev/null 2>&1); then - if dotnet test --logger "console;verbosity=minimal" >/dev/null 2>&1; then - echo "✅ C# 測試通過" - else - echo "❌ C# 測試失敗" - fi -else - echo "❌ C# 編譯失敗" -fi - -echo "" -echo "📋 Go 測試結果:" -if (cd ../go && go build >/dev/null 2>&1); then - if go test -v >/dev/null 2>&1; then - echo "✅ Go 測試通過" - else - echo "❌ Go 測試失敗(顯示詳細)" - go test -v - fi -else - echo "❌ Go 編譯失敗" -fi - -popd >/dev/null -echo "" -echo "📊 測試完成!" - diff --git a/utils/leetcode_helper.sh b/utils/leetcode_helper.sh index 2d5eeef..99e6e4e 100755 --- a/utils/leetcode_helper.sh +++ b/utils/leetcode_helper.sh @@ -47,6 +47,34 @@ render_template() { "$src" > "$dest" } +# 顯示說明 +show_help() { + cat << EOF +LeetCode Practice Helper + +用法: + $0 problem <題號> <題目名稱-kebab> <難度> + $0 log [YYYY-MM] + $0 readme + $0 -h | --help + +說明: + problem 建立新題目(會建立 C#/Go 骨架、測試模板與 README) + log 建立月度學習日誌(預設為本月,或指定 YYYY-MM) + readme 顯示題目數量統計(可擴充為寫回 README) + +範例: + $0 problem 1 two-sum Easy + $0 problem 3025 find-the-number-of-ways-to-place-people-i Medium + $0 log 2025-09 + $0 readme + +測試: + 建議使用 utils/run_tests.sh 執行,例: + utils/run_tests.sh 3025 [all|csharp|go] +EOF +} + # 建立測試檔案(來自 templates/problem/test/*) create_test_files() { local problem_dir="$1"; local number="$2"; local name_slug="$3"; local difficulty="$4" @@ -71,10 +99,7 @@ create_test_files() { "$problem_dir/test/edge_cases.md" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" - render_template "$TEMPLATES_DIR/problem/test/run_tests.sh.tmpl" \ - "$problem_dir/test/run_tests.sh" \ - "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" - chmod +x "$problem_dir/test/run_tests.sh" + # 不再產生每題的 run_tests.sh,請改用 utils/run_tests.sh } # 建立新題目 @@ -118,7 +143,7 @@ create_problem() { echo "✅ 已建立題目: $problem_dir" echo "C#: cd problems/${folder_name}/csharp && dotnet run" echo "Go: cd problems/${folder_name}/go && go run main.go" - echo "測試: cd problems/${folder_name}/test && ./run_tests.sh" + echo "測試: utils/run_tests.sh ${number_pad}-${name_slug} [all|csharp|go]" } # 更新主 README(目前僅統計列印) @@ -143,6 +168,13 @@ create_monthly_log() { } # 入口 + +# --help +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + show_help + exit 0 +fi + case "${1:-}" in problem) create_problem "${2:-}" "${3:-}" "${4:-}" ;; @@ -151,10 +183,6 @@ case "${1:-}" in log) create_monthly_log "${2:-}" ;; *) - echo "LeetCode Practice Helper" - echo "使用:" - echo " $0 problem <題號> <題目名稱-kebab> <難度>" - echo " $0 readme" - echo " $0 log [YYYY-MM]" + show_help ;; esac diff --git a/utils/run_tests.sh b/utils/run_tests.sh new file mode 100755 index 0000000..db00466 --- /dev/null +++ b/utils/run_tests.sh @@ -0,0 +1,125 @@ +#!/bin/bash +set -euo pipefail + +# Usage: +# utils/run_tests.sh [all|csharp|go] +# Examples: +# utils/run_tests.sh 3025 +# utils/run_tests.sh 3025 csharp +# utils/run_tests.sh problems/3025-find-the-number-of-ways-to-place-people-i go +# utils/run_tests.sh 3516-find-closest-person all +# Help: +# utils/run_tests.sh -h | --help +# List: +# utils/run_tests.sh --list + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +ROOT_DIR=$(cd "$SCRIPT_DIR/.." && pwd) + +show_help() { + cat << EOF +Run tests for a problem folder. + +Usage: + $0 [all|csharp|go] + $0 --list + +Where can be: + - A number (e.g., 3025) + - A 4-digit + slug folder name (e.g., 3025-find-the-number-of-ways-to-place-people-i) + - A relative path under problems/ (e.g., problems/3025-find-the-number-of-ways-to-place-people-i) + +Examples: + $0 3025 # run both C# and Go tests + $0 3025 csharp # only C# + $0 3516 go # only Go + $0 3516-find-closest-person all + $0 problems/3516-find-closest-person all + $0 --list # list available problems +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + show_help + exit 0 +fi + +if [[ "${1:-}" == "--list" ]]; then + echo "Available problems (from problems/):" + # List directories matching 4-digit prefix + find "$ROOT_DIR/problems" -maxdepth 1 -mindepth 1 -type d -printf "%f\n" \ + | sort \ + | awk 'BEGIN{printf("%-8s %s\n","Number","Slug"); printf("%-8s %s\n","------","----");} + {num=substr($0,1,4); slug=substr($0,6); printf("%-8s %s\n", num, $0)}' + exit 0 +fi + +problem_arg="${1:-}" +lang="${2:-all}" + +if [[ -z "$problem_arg" ]]; then + show_help >&2 + exit 1 +fi + +resolve_problem_dir() { + local arg="$1" + if [[ -d "$ROOT_DIR/$arg" ]]; then + echo "$ROOT_DIR/$arg" + return 0 + fi + if [[ "$arg" =~ ^[0-9]+$ ]]; then + local num=$(printf "%04d" "$arg") + local match + match=$(find "$ROOT_DIR/problems" -maxdepth 1 -type d -name "${num}-*" | head -n 1 || true) + if [[ -n "$match" ]]; then + echo "$match"; return 0 + fi + fi + # try exact name under problems + if [[ -d "$ROOT_DIR/problems/$arg" ]]; then + echo "$ROOT_DIR/problems/$arg"; return 0 + fi + echo "Cannot resolve problem directory from '$arg'" >&2 + exit 1 +} + +PROB_DIR=$(resolve_problem_dir "$problem_arg") +TEST_DIR="$PROB_DIR/test" +CS_DIR="$PROB_DIR/csharp" +GO_DIR="$PROB_DIR/go" + +run_csharp() { + if [[ ! -d "$CS_DIR" || ! -d "$TEST_DIR" ]]; then + echo "[C#] Skipped (missing csharp or test folder)"; return 0 + fi + echo "📋 C# 測試結果:" + if (cd "$CS_DIR" && dotnet build >/dev/null 2>&1); then + (cd "$TEST_DIR" && dotnet test --logger "console;verbosity=minimal") || { echo "❌ C# 測試失敗"; return 1; } + echo "✅ C# 測試通過" + else + echo "❌ C# 編譯失敗"; return 1 + fi +} + +run_go() { + if [[ ! -d "$GO_DIR" || ! -d "$TEST_DIR" ]]; then + echo "[Go] Skipped (missing go or test folder)"; return 0 + fi + echo "📋 Go 測試結果:" + if (cd "$GO_DIR" && go build >/dev/null 2>&1); then + (cd "$TEST_DIR" && go test -v) || { echo "❌ Go 測試失敗"; return 1; } + echo "✅ Go 測試通過" + else + echo "❌ Go 編譯失敗"; return 1 + fi +} + +case "$lang" in + all) run_csharp; run_go ;; + csharp) run_csharp ;; + go) run_go ;; + *) echo "Unknown lang '$lang' (use all|csharp|go)" >&2; exit 1 ;; +esac + +echo "📊 測試完成: $PROB_DIR"