Files
linux-sysadmin-journey/projects/02-command-line-mastery/setup/challenges.sh
T

415 lines
15 KiB
Bash
Raw Normal View History

2025-02-09 02:01:58 -06:00
#!/bin/bash
source "$(dirname "$0")/common.sh"
# Challenge categories and levels
declare -A CHALLENGE_CATEGORIES=(
["file"]="File System Operations"
["perm"]="Permissions & Security"
["text"]="Text Processing"
["proc"]="Process Management"
["net"]="Network Operations"
2025-02-09 03:25:43 -06:00
["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"
2025-02-09 02:01:58 -06:00
)
2025-02-09 02:33:45 -06:00
# Challenge definitions with metadata
2025-02-09 02:01:58 -06:00
# Format: type|name|difficulty|skills|task|hints|setup_requirements|verification_criteria
declare -A CHALLENGES=(
["1.1"]="file_search|Find Recent Files|1|file_ops,search|Find all .conf files modified in the last 24 hours and save the list to recent_configs.txt|Try using find with -mtime|none|output_file=recent_configs.txt;file_count=10"
["1.2"]="file_count|File Statistics|1|file_ops,search|Generate a report showing count of files by extension|Consider using find and awk|none|report_exists=true;extension_count=5"
["1.3"]="file_organize|Smart File Organizer|2|file_ops|Create directories for different file types and sort files accordingly|Look into file command|none|dir_structure=complete;files_sorted=true"
["1.4"]="file_cleanup|System Cleanup|2|file_ops,search|Find and list all empty files and zero-byte files|Combine find with size tests|none|empty_files_found=true;report_generated=true"
["1.5"]="file_dedup|Duplicate Hunter|3|file_ops,search|Find and handle duplicate files using md5sum|Consider using sort and uniq|none|duplicates_identified=true;report_accurate=true"
# Level 2: Permissions
["2.1"]="perm_basic|Permission Fundamentals|1|permissions|Set up basic file permissions following security best practices|Remember user-group-others order|users=app_user1|permissions_correct=true;ownership_correct=true"
["2.2"]="perm_acl|ACL Master|2|permissions|Implement complex ACL rules for multiple users|Look into setfacl usage|users=app_user1,app_user2|acl_rules_correct=true"
["2.3"]="perm_special|Special Permission Bits|2|permissions|Configure SUID/SGID permissions correctly|Understanding special bits|none|special_bits_correct=true"
["2.4"]="perm_recursive|Directory Tree Fix|3|permissions|Fix permissions recursively while maintaining security|Consider find with exec|none|tree_permissions_correct=true"
["2.5"]="perm_audit|Security Auditor|3|permissions|Perform security audit and generate detailed report|Use stat and lsattr|none|audit_complete=true;report_detailed=true"
# Level 3: Text Processing
["3.1"]="text_search|Log Detective|1|text_proc,search|Find and analyze specific patterns in log files|grep with context|none|patterns_found=true;count_correct=true"
["3.2"]="text_extract|Data Extraction Pro|2|text_proc|Extract and format specific fields from log files|awk is your friend|none|fields_extracted=true;format_correct=true"
["3.3"]="text_transform|Format Converter|2|text_proc|Transform data between different formats|sed for substitution|none|transformation_correct=true"
["3.4"]="text_report|Report Generator|3|text_proc|Create a detailed system report from various logs|Combine multiple tools|none|report_complete=true;format_valid=true"
["3.5"]="text_analyze|Pattern Analyzer|3|text_proc,search|Analyze complex error patterns and generate statistics|Use grep with perl regex|none|analysis_complete=true;stats_accurate=true"
)
# Challenge verification functions
verify_challenge() {
local challenge_id=$1
local start_time=$2
IFS='|' read -r type name difficulty skills task hints requirements criteria <<< "${CHALLENGES[$challenge_id]}"
# Check prerequisites
if ! check_requirements "$requirements"; then
log_message "ERROR" "Challenge prerequisites not met"
return 1
2025-02-09 02:36:28 -06:00
fi
2025-02-09 02:01:58 -06:00
# Record attempt
record_attempt "$challenge_id" "$start_time"
# Run specific verification
local category=${type%%_*}
case "$category" in
"file") verify_file_challenge "$challenge_id" "$criteria" ;;
"perm") verify_permission_challenge "$challenge_id" "$criteria" ;;
"text") verify_text_challenge "$challenge_id" "$criteria" ;;
*) log_message "ERROR" "Unknown challenge category: $category" ;;
esac
}
# Check challenge prerequisites
check_requirements() {
local requirements=$1
[[ "$requirements" == "none" ]] && return 0
IFS=';' read -ra REQS <<< "$requirements"
for req in "${REQS[@]}"; do
IFS='=' read -r key value <<< "$req"
case "$key" in
"users")
for user in ${value//,/ }; do
if ! id "$user" &>/dev/null; then
log_message "ERROR" "Required user $user not found"
return 1
fi
done
;;
# Add more requirement checks as needed
esac
done
return 0
}
# Verify file-related challenges
verify_file_challenge() {
local challenge_id=$1
local criteria=$2
local result=0
IFS=';' read -ra CHECKS <<< "$criteria"
for check in "${CHECKS[@]}"; do
IFS='=' read -r key value <<< "$check"
case "$key" in
"output_file")
[[ -f "$value" ]] && ((result++))
;;
"file_count")
[[ "$(find "$CHALLENGE_DIR/files" -type f | wc -l)" -eq "$value" ]] && ((result++))
;;
# Add more specific checks
esac
done
verify_result "$challenge_id" "$result" "${#CHECKS[@]}" "File challenge verification"
}
# Verify permission-related challenges
verify_permission_challenge() {
local challenge_id=$1
local criteria=$2
local result=0
IFS=';' read -ra CHECKS <<< "$criteria"
for check in "${CHECKS[@]}"; do
IFS='=' read -r key value <<< "$check"
case "$key" in
"users")
for user in ${value//,/ }; do
if ! id "$user" &>/dev/null; then
log_message "ERROR" "Required user $user not found"
return 1
fi
done
;;
# Add more permission checks as needed
esac
done
verify_result "$challenge_id" "$result" "${#CHECKS[@]}" "Permission challenge verification"
}
# Verify text-related challenges
verify_text_challenge() {
local challenge_id=$1
local criteria=$2
local result=0
IFS=';' read -ra CHECKS <<< "$criteria"
for check in "${CHECKS[@]}"; do
IFS='=' read -r key value <<< "$check"
case "$key" in
"patterns_found")
[[ "$(grep -c "$value" "$CHALLENGE_DIR/files/logs/"*.log)" -ge 1 ]] && ((result++))
;;
"count_correct")
[[ "$(grep -c "$value" "$CHALLENGE_DIR/files/logs/"*.log)" -eq "$value" ]] && ((result++))
;;
# Add more text checks as needed
esac
done
verify_result "$challenge_id" "$result" "${#CHECKS[@]}" "Text challenge verification"
}
# Record challenge attempt
record_attempt() {
local challenge_id=$1
local start_time=$2
local duration=$(($(date +%s) - start_time))
local command_count=$(grep -c "^$start_time" "$COMMAND_LOG")
echo "$challenge_id,$(get_attempt_number "$challenge_id"),$start_time,$command_count,$duration,0" >> "$ATTEMPT_FILE"
}
# Get attempt number for challenge
get_attempt_number() {
local challenge_id=$1
local attempts=$(grep -c "^$challenge_id," "$ATTEMPT_FILE")
echo $((attempts + 1))
}
# Enhanced result verification with detailed feedback
verify_result() {
local challenge_id=$1
local actual=$2
local expected=$3
local message=$4
local end_time=$(date +%s)
if [[ "$actual" == "$expected" ]]; then
2025-02-09 03:25:43 -06:00
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")"
2025-02-09 02:01:58 -06:00
2025-02-09 03:25:43 -06:00
# Calculate and award points
2025-02-09 02:01:58 -06:00
local duration=$((end_time - start_time))
local command_count=$(get_command_count "$challenge_id")
local bonus_points=$(calculate_bonus_points "$duration" "$command_count")
2025-02-09 03:25:43 -06:00
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"
2025-02-09 02:01:58 -06:00
return 0
else
2025-02-09 03:25:43 -06:00
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"
2025-02-09 02:01:58 -06:00
return 1
fi
}
# Calculate bonus points based on time and efficiency
calculate_bonus_points() {
local duration=$1
local command_count=$2
local time_bonus=0
local efficiency_bonus=0
# Time bonus
if [[ "$duration" -lt 60 ]]; then
time_bonus=50
elif [[ "$duration" -lt 120 ]]; then
time_bonus=25
fi
# Efficiency bonus
if [[ "$command_count" -le 3 ]]; then
efficiency_bonus=50
elif [[ "$command_count" -le 5 ]]; then
efficiency_bonus=25
fi
echo $((time_bonus + efficiency_bonus))
}
2025-02-09 02:33:45 -06:00
# Get challenge details
2025-02-09 02:01:58 -06:00
get_challenge_details() {
local challenge_id=$1
2025-02-09 02:33:45 -06:00
if [[ -n "${CHALLENGES[$challenge_id]:-}" ]]; then
IFS='|' read -r type name difficulty skills task hints requirements criteria <<< "${CHALLENGES[$challenge_id]}"
echo -e "${BLUE}Challenge $challenge_id:${NC} $name"
echo -e "${BLUE}Difficulty:${NC} $(printf '★%.0s' $(seq 1 "$difficulty"))"
echo -e "${BLUE}Skills:${NC} $skills"
echo -e "${BLUE}Task:${NC} $task"
return 0
fi
return 1
2025-02-09 02:01:58 -06:00
}
# Update challenge status
update_challenge_status() {
local challenge_id=$1
local success=$2
local end_time=$3
# Update attempt status
sed -i "$ s/,0$/,$success/" "$ATTEMPT_FILE"
# Record completion if successful
if [[ "$success" == "1" ]]; then
echo "$challenge_id,$end_time,completed,${CHALLENGES[$challenge_id]}" >> "$PROGRESS_FILE"
fi
}
# List available challenges
list_challenges() {
2025-02-09 02:33:45 -06:00
echo -e "\n${BLUE}Available Challenges:${NC}"
2025-02-09 02:01:58 -06:00
for challenge_id in "${!CHALLENGES[@]}"; do
if ! is_challenge_completed "$challenge_id"; then
2025-02-09 02:33:45 -06:00
IFS='|' read -r type name difficulty skills task _ <<< "${CHALLENGES[$challenge_id]}"
2025-02-09 02:01:58 -06:00
echo -e "\n${YELLOW}Challenge $challenge_id${NC} ($difficulty★)"
echo "Type: $type"
echo "Skills: $skills"
echo "Task: $task"
fi
done
2025-02-09 03:25:43 -06:00
}
# 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
2025-02-09 02:01:58 -06:00
}