diff --git a/projects/02-command-line-mastery/play.sh b/projects/02-command-line-mastery/play.sh index 56322eb..830e034 100644 --- a/projects/02-command-line-mastery/play.sh +++ b/projects/02-command-line-mastery/play.sh @@ -9,6 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' RED='\033[0;31m' +CYAN='\033[0;36m' +BOLD='\033[1m' NC='\033[0m' # Error handling @@ -19,6 +21,35 @@ set -o pipefail # Exit on pipe failure # Trap errors and provide error message trap 'echo -e "${RED}Error: Command failed at line $LINENO${NC}" >&2' ERR +# Function to show spinner while executing command +show_spinner() { + local pid=$1 + local delay=0.1 + local spinstr='|/-\' + while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do + local temp=${spinstr#?} + printf " [%c] " "$spinstr" + local spinstr=$temp${spinstr%"$temp"} + sleep $delay + printf "\b\b\b\b\b\b" + done + printf " \b\b\b\b" +} + +# Function to print banner +print_banner() { + clear + cat << "EOF" + _____ _ +| |___ _____ _____ ___ ___ _____ ___ | | +| --| . | | | .'| | | . ||_| +|_____|___|_|_|_|_|_|_|__,|_|_|_|_|_| | + |___| +EOF + echo -e "${BOLD}Command Line Mastery Challenge${NC}" + echo "==============================" +} + # Function to check if command exists check_command() { if ! command -v "$1" &>/dev/null; then @@ -134,9 +165,93 @@ validate_challenge_id() { fi } +# Function to handle commands with improved feedback +handle_command() { + local cmd="$1" + shift # Remove first argument, leaving remaining args + + case "$cmd" in + "verify"|"verify-challenge"|"v") + echo -e "${CYAN}Verifying your solution...${NC}" + "$CHALLENGE_DIR/.tracking/verify-challenge.sh" "$@" + ;; + "hint"|"show-hint"|"h") + echo -e "${CYAN}Getting hint...${NC}" + "$CHALLENGE_DIR/.tracking/show-hint.sh" "$@" + ;; + "status"|"show-status"|"s") + echo -e "${CYAN}Showing status...${NC}" + "$CHALLENGE_DIR/.tracking/show-status.sh" "$@" + ;; + "progress"|"check-progress"|"p") + echo -e "${CYAN}Checking progress...${NC}" + "$CHALLENGE_DIR/.tracking/check-progress.sh" "$@" + ;; + "clear"|"cls"|"c") + clear + print_challenge_header "$CURRENT_CHALLENGE_ID" + ;; + "reset"|"r") + echo -e "${YELLOW}Resetting current challenge...${NC}" + setup_challenge_env "$CURRENT_CHALLENGE_ID" + ;; + "exit"|"quit"|"q") + return 1 + ;; + "help"|"?") + print_help_menu + ;; + "") + # Empty command, just continue + ;; + *) + echo -e "${RED}Unknown command: $cmd${NC}" + echo -e "Type ${YELLOW}help${NC} or ${YELLOW}?${NC} for available commands" + ;; + esac + return 0 +} + +# Function to print help menu +print_help_menu() { + echo -e "\n${BOLD}Available Commands:${NC}" + echo -e " ${YELLOW}verify, v${NC} - Check your solution" + echo -e " ${YELLOW}hint, h${NC} - Get a hint" + echo -e " ${YELLOW}status, s${NC} - Show challenge status" + echo -e " ${YELLOW}progress, p${NC} - Check overall progress" + echo -e " ${YELLOW}clear, c${NC} - Clear screen" + echo -e " ${YELLOW}reset, r${NC} - Reset current challenge" + echo -e " ${YELLOW}help, ?${NC} - Show this help message" + echo -e " ${YELLOW}exit, q${NC} - Exit the challenge" + echo -e "\n${BOLD}Tips:${NC}" + echo -e "- Commands can be abbreviated (e.g., 'v' for verify)" + echo -e "- Use TAB for command completion" + echo -e "- Use arrow keys for command history" +} + +# Function to print challenge header +print_challenge_header() { + local challenge_id=$1 + echo -e "${GREEN}🎮 Challenge $challenge_id${NC}" + echo -e "${BLUE}$(get_challenge_description "$challenge_id")${NC}" + echo -e "${YELLOW}Type 'help' or '?' for available commands${NC}" +} + +# Function to setup challenge environment +setup_challenge_env() { + local challenge_id=$1 + if ! bash "$CHALLENGE_DIR/.tracking/level1-setup.sh" > /dev/null 2>&1; then + echo -e "${RED}Error: Failed to setup challenge environment${NC}" + echo -e "${YELLOW}Try running: $0 clean && $0 setup${NC}" + return 1 + fi + return 0 +} + # Function to start a challenge start_challenge() { local challenge_id=$1 + export CURRENT_CHALLENGE_ID=$challenge_id # Validate challenge ID if ! validate_challenge_id "$challenge_id"; then @@ -144,24 +259,17 @@ start_challenge() { fi # Source required scripts with error checking - local required_scripts=(common.sh challenges.sh) - for script in "${required_scripts[@]}"; do - if ! source "$CHALLENGE_DIR/.tracking/$script" 2>/dev/null; then - echo -e "${RED}Error: Required script not found: $script${NC}" - echo -e "${YELLOW}Running setup first...${NC}" - setup_environment - if ! source "$CHALLENGE_DIR/.tracking/$script" 2>/dev/null; then - echo -e "${RED}Error: Failed to source $script even after setup${NC}" - return 1 - fi - fi - done + if ! source_required_scripts; then + return 1 + fi - clear - echo -e "${GREEN}🎮 Command Line Mastery Challenge${NC}" - echo "==============================" + print_banner + + # Setup challenge environment + echo -e "${BLUE}Setting up challenge environment...${NC}" + setup_challenge_env "$challenge_id" & + show_spinner $! - # Setup challenge environment with error checking if ! get_challenge_details "$challenge_id" 2>/dev/null; then echo -e "${RED}Error: Challenge $challenge_id not found${NC}" echo -e "${YELLOW}Available challenges:${NC}" @@ -169,29 +277,36 @@ start_challenge() { return 1 fi - if ! bash "$CHALLENGE_DIR/.tracking/level1-setup.sh" 2>/dev/null; then - echo -e "${RED}Error: Failed to setup challenge environment${NC}" - echo -e "${YELLOW}Try running: $0 clean && $0 setup${NC}" - return 1 - fi - - # Record challenge start with error checking + # Record challenge start local start_time=$(date +%s) - if ! echo "$challenge_id,$start_time" > "$CHALLENGE_DIR/.tracking/current_challenge" 2>/dev/null; then - echo -e "${RED}Error: Failed to record challenge start${NC}" - return 1 - fi + echo "$challenge_id,$start_time" > "$CHALLENGE_DIR/.tracking/current_challenge" - # Create command aliases - alias verify="$CHALLENGE_DIR/.tracking/verify-challenge.sh" - alias hint="$CHALLENGE_DIR/.tracking/show-hint.sh" - alias status="$CHALLENGE_DIR/.tracking/track-progress.sh" + print_challenge_header "$challenge_id" - echo -e "\n${GREEN}Challenge ready!${NC}" - echo -e "Commands available:" - echo -e " ${YELLOW}verify${NC} - Check your solution" - echo -e " ${YELLOW}hint${NC} - Get a hint" - echo -e " ${YELLOW}status${NC} - Show progress" + # Enable command history and tab completion + bind 'set completion-ignore-case on' 2>/dev/null || true + bind 'set show-all-if-ambiguous on' 2>/dev/null || true + + # Start command loop with improved prompt + local cmd_count=0 + while true; do + cmd_count=$((cmd_count + 1)) + echo -ne "\n${GREEN}challenge${BLUE}[$challenge_id]${GREEN}>${NC} " + read -e -r cmd args # -e enables readline for command history + history -s "$cmd $args" # Add command to history + if ! handle_command "$cmd" $args; then + break + fi + done + + # Record challenge end + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + echo "$challenge_id,$duration" >> "$CHALLENGE_DIR/.tracking/data/time.csv" + + echo -e "\n${GREEN}Challenge session ended!${NC}" + echo -e "Time spent: $(printf '%dm %ds' $((duration/60)) $((duration%60)))" + echo -e "Commands used: $cmd_count" } # Function to show help diff --git a/projects/02-command-line-mastery/setup/challenges.sh b/projects/02-command-line-mastery/setup/challenges.sh index 072b025..990dfe8 100644 --- a/projects/02-command-line-mastery/setup/challenges.sh +++ b/projects/02-command-line-mastery/setup/challenges.sh @@ -9,6 +9,18 @@ declare -A CHALLENGE_CATEGORIES=( ["text"]="Text Processing" ["proc"]="Process Management" ["net"]="Network Operations" + ["pipe"]="Pipelines & Redirection" + ["shell"]="Shell Scripting" + ["git"]="Git Operations" +) + +# Challenge difficulty descriptions +declare -A DIFFICULTY_DESCRIPTIONS=( + [1]="Beginner - Basic command usage" + [2]="Intermediate - Combined commands" + [3]="Advanced - Complex operations" + [4]="Expert - System administration" + [5]="Master - Advanced scripting" ) # Challenge definitions with metadata @@ -181,19 +193,40 @@ verify_result() { local end_time=$(date +%s) if [[ "$actual" == "$expected" ]]; then - log_message "SUCCESS" "Challenge $challenge_id: $message" - update_challenge_status "$challenge_id" "1" "$end_time" + log_message "SUCCESS" "Challenge $challenge_id completed successfully!" + echo -e "\n${GREEN}🎉 Congratulations! Challenge completed!${NC}" + echo -e "${BLUE}Summary:${NC}" + echo -e "- Time taken: $(format_duration $((end_time - start_time)))" + echo -e "- Commands used: $(get_command_count "$challenge_id")" - # Calculate bonus points + # Calculate and award points local duration=$((end_time - start_time)) local command_count=$(get_command_count "$challenge_id") local bonus_points=$(calculate_bonus_points "$duration" "$command_count") + local total_points=$((100 + bonus_points)) + + echo -e "\n${YELLOW}Points earned:${NC}" + echo -e "- Base points: 100" + echo -e "- Time bonus: $time_bonus" + echo -e "- Efficiency bonus: $efficiency_bonus" + echo -e "- Total points: $total_points" + + # Update progress + update_challenge_status "$challenge_id" "1" "$end_time" + award_points "$challenge_id" "$total_points" + + # Show next challenge suggestion + suggest_next_challenge "$challenge_id" - award_points "$challenge_id" "$((100 + bonus_points))" return 0 else - log_message "ERROR" "Challenge $challenge_id: Completed $actual/$expected checks" - update_challenge_status "$challenge_id" "0" "$end_time" + log_message "ERROR" "Challenge incomplete: $actual/$expected requirements met" + echo -e "\n${RED}Challenge not completed yet.${NC}" + echo -e "Progress: $actual/$expected requirements met" + echo -e "\n${YELLOW}Tips:${NC}" + echo -e "- Use 'hint' command for guidance" + echo -e "- Check your work carefully" + echo -e "- Try breaking down the task into smaller steps" return 1 fi } @@ -263,4 +296,120 @@ list_challenges() { echo "Task: $task" fi done +} + +# Function to add new challenge +add_challenge() { + local id=$1 + local type=$2 + local name=$3 + local difficulty=$4 + local skills=$5 + local task=$6 + local hints=$7 + local requirements=$8 + local criteria=$9 + + # Validate challenge ID format + if [[ ! $id =~ ^[1-9]\.[1-5]$ ]]; then + log_message "ERROR" "Invalid challenge ID format. Must be level.number (e.g., 1.1)" + return 1 + fi + + # Validate challenge type + if [[ -z "${CHALLENGE_CATEGORIES[$type]}" ]]; then + log_message "ERROR" "Invalid challenge type. Available types: ${!CHALLENGE_CATEGORIES[*]}" + return 1 + fi + + # Validate difficulty + if ((difficulty < 1 || difficulty > 5)); then + log_message "ERROR" "Invalid difficulty. Must be between 1 and 5" + return 1 + fi + + # Add challenge to CHALLENGES array + CHALLENGES[$id]="${type}|${name}|${difficulty}|${skills}|${task}|${hints}|${requirements}|${criteria}" + log_message "SUCCESS" "Added challenge $id: $name" + return 0 +} + +# Function to list available challenges by category +list_challenges_by_category() { + local category=${1:-all} + + echo -e "\n${BOLD}Available Challenges:${NC}" + echo "====================" + + if [[ "$category" == "all" ]]; then + for cat in "${!CHALLENGE_CATEGORIES[@]}"; do + echo -e "\n${BLUE}${CHALLENGE_CATEGORIES[$cat]}:${NC}" + for id in "${!CHALLENGES[@]}"; do + IFS='|' read -r type name difficulty skills task hints requirements criteria <<< "${CHALLENGES[$id]}" + if [[ ${type%%_*} == "$cat" ]]; then + echo -e "${YELLOW}$id${NC} ($(printf '★%.0s' $(seq 1 "$difficulty"))) - $name" + echo -e " ${CYAN}Skills:${NC} $skills" + echo -e " ${CYAN}Task:${NC} $task" + fi + done + done + else + if [[ -z "${CHALLENGE_CATEGORIES[$category]}" ]]; then + log_message "ERROR" "Invalid category. Available categories: ${!CHALLENGE_CATEGORIES[*]}" + return 1 + fi + + echo -e "\n${BLUE}${CHALLENGE_CATEGORIES[$category]}:${NC}" + for id in "${!CHALLENGES[@]}"; do + IFS='|' read -r type name difficulty skills task hints requirements criteria <<< "${CHALLENGES[$id]}" + if [[ ${type%%_*} == "$category" ]]; then + echo -e "${YELLOW}$id${NC} ($(printf '★%.0s' $(seq 1 "$difficulty"))) - $name" + echo -e " ${CYAN}Skills:${NC} $skills" + echo -e " ${CYAN}Task:${NC} $task" + fi + done + fi +} + +# Function to suggest next challenge +suggest_next_challenge() { + local current_id=$1 + local level=${current_id%.*} + local number=${current_id#*.} + + echo -e "\n${BLUE}What's next?${NC}" + + # Suggest next challenge in same level + local next_number=$((number + 1)) + local next_id="${level}.${next_number}" + + if [[ -n "${CHALLENGES[$next_id]:-}" ]]; then + IFS='|' read -r type name difficulty skills task hints requirements criteria <<< "${CHALLENGES[$next_id]}" + echo -e "→ Try challenge ${YELLOW}$next_id${NC}: $name" + else + # Suggest first challenge of next level + local next_level=$((level + 1)) + local next_id="${next_level}.1" + if [[ -n "${CHALLENGES[$next_id]:-}" ]]; then + IFS='|' read -r type name difficulty skills task hints requirements criteria <<< "${CHALLENGES[$next_id]}" + echo -e "→ Ready for next level? Try ${YELLOW}$next_id${NC}: $name" + fi + fi +} + +# Function to format duration +format_duration() { + local seconds=$1 + local minutes=$((seconds / 60)) + local hours=$((minutes / 60)) + minutes=$((minutes % 60)) + seconds=$((seconds % 60)) + + if ((hours > 0)); then + printf "%dh %dm %ds" $hours $minutes $seconds + elif ((minutes > 0)); then + printf "%dm %ds" $minutes $seconds + else + printf "%ds" $seconds + fi } \ No newline at end of file