# 1. 讓腳本可執行: # chmod +x utils/leetcode_helper.sh # 2. 建立新題目: # ./utils/leetcode_helper.sh problem 1 two-sum Easy # ./utils/leetcode_helper.sh problem 3000 maximum-area-rectangle Medium # 3. 建立月度日誌: # ./utils/leetcode_helper.sh log 2025-09 # 4. 更新README: # ./utils/leetcode_helper.sh readme #!/bin/bash set -euo pipefail # 路徑設定 SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) ROOT_DIR=$(cd "$SCRIPT_DIR/.." && pwd) TEMPLATES_DIR="$ROOT_DIR/templates" # kebab-case -> Title Case(two-sum -> Two Sum) to_title_case() { local s="${1//-/ }" echo "$s" | awk '{for(i=1;i<=NF;i++){ $i=toupper(substr($i,1,1)) substr($i,2) } print }' } # 渲染模板到目的檔 # 變數:{{NUMBER}}, {{NUMBER_PAD}}, {{NAME_SLUG}}, {{NAME_TITLE}}, {{DIFFICULTY}}, {{DATE}}, {{YEAR_MONTH}} render_template() { local src="$1"; shift local dest="$1"; shift local number="${1:-}"; shift || true local number_pad="${1:-}"; shift || true local name_slug="${1:-}"; shift || true local name_title="${1:-}"; shift || true local difficulty="${1:-}"; shift || true local today="${1:-}"; shift || true local year_month="${1:-}"; shift || true mkdir -p "$(dirname "$dest")" sed -e "s/{{NUMBER}}/${number}/g" \ -e "s/{{NUMBER_PAD}}/${number_pad}/g" \ -e "s/{{NAME_SLUG}}/${name_slug}/g" \ -e "s/{{NAME_TITLE}}/${name_title}/g" \ -e "s/{{DIFFICULTY}}/${difficulty}/g" \ -e "s/{{DATE}}/${today}/g" \ -e "s/{{YEAR_MONTH}}/${year_month}/g" \ "$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" local number_pad=$(printf "%04d" "$number") local name_title=$(to_title_case "$name_slug") local today=$(date +%Y-%m-%d) local year_month=$(date +%Y-%m) render_template "$TEMPLATES_DIR/problem/test/SolutionTests.cs.tmpl" \ "$problem_dir/test/SolutionTests.cs" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" render_template "$TEMPLATES_DIR/problem/test/TestProject.csproj.tmpl" \ "$problem_dir/test/TestProject.csproj" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" render_template "$TEMPLATES_DIR/problem/test/main_test.go.tmpl" \ "$problem_dir/test/main_test.go" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" render_template "$TEMPLATES_DIR/problem/test/edge_cases.md.tmpl" \ "$problem_dir/test/edge_cases.md" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" # 不再產生每題的 run_tests.sh,請改用 utils/run_tests.sh } # 建立新題目 create_problem() { local number="$1"; local name_slug="$2"; local difficulty="$3" if [[ -z "${number:-}" || -z "${name_slug:-}" || -z "${difficulty:-}" ]]; then echo "使用方法: $0 problem <題號> <題目名稱-kebab> <難度>"; exit 1 fi local number_pad=$(printf "%04d" "$number") local folder_name="${number_pad}-${name_slug}" local problem_dir="$ROOT_DIR/problems/${folder_name}" local name_title=$(to_title_case "$name_slug") local today=$(date +%Y-%m-%d) local year_month=$(date +%Y-%m) mkdir -p "$problem_dir/csharp" "$problem_dir/go" "$problem_dir/test" # README render_template "$TEMPLATES_DIR/problem/README.md.tmpl" \ "$problem_dir/README.md" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" # C# render_template "$TEMPLATES_DIR/problem/csharp/Program.cs.tmpl" \ "$problem_dir/csharp/Program.cs" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" render_template "$TEMPLATES_DIR/problem/csharp/csharp.csproj.tmpl" \ "$problem_dir/csharp/csharp.csproj" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" # Go render_template "$TEMPLATES_DIR/problem/go/main.go.tmpl" \ "$problem_dir/go/main.go" \ "$number" "$number_pad" "$name_slug" "$name_title" "$difficulty" "$today" "$year_month" (cd "$problem_dir/go" && go mod init "leetcode-$number" >/dev/null 2>&1 || true) # Tests create_test_files "$problem_dir" "$number" "$name_slug" "$difficulty" echo "✅ 已建立題目: $problem_dir" echo "C#: cd problems/${folder_name}/csharp && dotnet run" echo "Go: cd problems/${folder_name}/go && go run main.go" echo "測試: utils/run_tests.sh ${number_pad}-${name_slug} [all|csharp|go]" } # 更新主 README(目前僅統計列印) update_readme() { echo "🔄 更新主 README 統計..." local total_problems=$(find "$ROOT_DIR/problems" -maxdepth 1 -type d | wc -l) total_problems=$((total_problems - 1)) local completed_problems=$(find "$ROOT_DIR/problems" -name "README.md" -exec grep -l "✅" {} \; | wc -l) echo "📊 題目總數: $total_problems,標記完成: $completed_problems" } # 建立月度日誌(由模板 logs-template.md.tmpl 渲染) create_monthly_log() { local year_month="${1:-}" if [[ -z "$year_month" ]]; then year_month=$(date +%Y-%m); fi local dest="$ROOT_DIR/logs/${year_month}.md" if [[ -f "$dest" ]]; then echo "⚠️ 已存在: $dest"; return 1; fi mkdir -p "$ROOT_DIR/logs" render_template "$TEMPLATES_DIR/logs-template.md.tmpl" "$dest" "" "" "" "" "" "" "$year_month" echo "✅ 已建立月度日誌: $dest" } # 入口 # --help if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then show_help exit 0 fi case "${1:-}" in problem) create_problem "${2:-}" "${3:-}" "${4:-}" ;; readme) update_readme ;; log) create_monthly_log "${2:-}" ;; *) show_help ;; esac