Friday, December 26, 2025

The Need for an Intelligent Running Coach



Motivation                                    


In the realm of physical fitness, achieving specific goals like running a 5-kilometer race or a marathon often requires a structured and progressive training regimen. However, generic training plans, widely available online or in books, frequently fall short of addressing the unique needs and circumstances of individual users. These one-size-fits-all approaches often neglect critical factors such as a person's current fitness level, age, pre-existing health conditions, potential physical handicaps, and even their recent training history. A plan designed for a seasoned athlete will be detrimental to a complete beginner, just as a plan for a young, healthy individual might be inappropriate for an older person with joint issues.

This is where an intelligent AI agent can revolutionize personal fitness. By leveraging artificial intelligence, we can create a sophisticated system capable of generating highly personalized running training plans. This agent would not only adapt to a user's individual profile but also dynamically adjust the plan based on progress, feedback, and even real-time environmental factors. The goal is to provide a virtual coach that offers tailored guidance, ensures safety, and maximizes the user's potential to achieve their running aspirations.


Core Components of the AI Running Coach


Building such an AI agent involves integrating several interconnected modules, each responsible for a specific aspect of the personalization and planning process. These modules collectively form a robust system that can understand a user's needs and craft an optimal training path.


A.  User Profile Module


The foundation of any personalized system is a comprehensive understanding of the user. The User Profile Module is responsible for collecting, storing, and managing all relevant information about the individual runner. This data forms the basis upon which all subsequent decisions regarding the training plan are made.

Data collection encompasses a wide array of details. This includes basic demographic information such as age, which is crucial for determining appropriate training intensity and recovery periods. Fitness level is a paramount factor, often assessed through self-reported activity levels, recent performance metrics (e.g., best 1-mile time), or even simple initial fitness tests like a brisk walk test. Information regarding health conditions, such as cardiovascular issues, diabetes, or respiratory problems, must be carefully recorded to ensure the training plan is safe and does not exacerbate existing conditions. Similarly, any physical handicaps, like knee injuries, back pain, or mobility limitations, need to be understood so that exercises can be modified or avoided entirely. The user's training history is also vital; knowing whether they are starting from scratch or have recently engaged in running or other forms of exercise significantly influences the initial intensity and progression of the plan. Finally, the user's specific training goal, such as running a 5-kilometer race, completing a 10-kilometer run, or simply improving general endurance, dictates the overall structure and focus of the plan.

Data representation within this module is critical for efficient processing. A structured approach, often using a class or data structure, allows for easy access and manipulation of user attributes.

Here is a Python code example illustrating a `UserProfile` class. This class encapsulates various attributes that define a runner's profile, providing a clear and organized way to store this essential information. Each attribute is initialized with a default value or a placeholder, indicating the type of data expected.


class UserProfile:

    """

    Represents a comprehensive profile of a user for personalized running plan generation.


    Attributes:

        user_id (str): A unique identifier for the user.

        age (int): The user's age in years.

        gender (str): The user's gender ('Male', 'Female', 'Other').

        weight_kg (float): The user's weight in kilograms.

        height_cm (float): The user's height in centimeters.

        fitness_level (str): A qualitative assessment of the user's current fitness

                             ('Beginner', 'Intermediate', 'Advanced').

        training_goal (str): The primary objective of the training plan

                             ('Run 5K', 'Run 10K', 'Marathon', 'General Endurance').

        recent_training_history (str): Describes recent exercise activity

                                       ('Sedentary', 'Light Activity', 'Regular Runner').

        health_issues (list): A list of strings detailing any relevant health conditions

                              (e.g., ['Asthma', 'Knee Pain']).

        handicaps (list): A list of strings detailing any physical handicaps

                          (e.g., ['Right Ankle Sprain History']).

        max_heart_rate (int): User's estimated or measured maximum heart rate.

        resting_heart_rate (int): User's measured resting heart rate.

        vo2_max (float): User's estimated or measured VO2 max, if available.

        preferred_running_days (list): A list of strings for preferred training days

                                      (e.g., ['Monday', 'Wednesday', 'Friday']).

        available_time_per_session_minutes (int): Average minutes available for each training session.

    """


    def __init__(self, user_id, age, gender, weight_kg, height_cm, fitness_level,

                 training_goal, recent_training_history, health_issues=None,

                 handicaps=None, max_heart_rate=None, resting_heart_rate=None,

                 vo2_max=None, preferred_running_days=None,

                 available_time_per_session_minutes=60):

        """

        Initializes a new UserProfile instance.


        Args:

            user_id (str): Unique identifier for the user.

            age (int): User's age in years.

            gender (str): User's gender.

            weight_kg (float): User's weight in kilograms.

            height_cm (float): User's height in centimeters.

            fitness_level (str): User's current fitness level.

            training_goal (str): User's primary training objective.

            recent_training_history (str): Description of recent exercise activity.

            health_issues (list, optional): List of health conditions. Defaults to an empty list.

            handicaps (list, optional): List of physical handicaps. Defaults to an empty list.

            max_heart_rate (int, optional): Maximum heart rate. Defaults to None.

            resting_heart_rate (int, optional): Resting heart rate. Defaults to None.

            vo2_max (float, optional): VO2 max value. Defaults to None.

            preferred_running_days (list, optional): Preferred training days. Defaults to an empty list.

            available_time_per_session_minutes (int, optional): Time available per session. Defaults to 60.

        """

        self.user_id = user_id

        self.age = age

        self.gender = gender

        self.weight_kg = weight_kg

        self.height_cm = height_cm

        self.fitness_level = fitness_level

        self.training_goal = training_goal

        self.recent_training_history = recent_training_history

        self.health_issues = health_issues if health_issues is not None else []

        self.handicaps = handicaps if handicaps is not None else []

        self.max_heart_rate = max_heart_rate

        self.resting_heart_rate = resting_heart_rate

        self.vo2_max = vo2_max

        self.preferred_running_days = preferred_running_days if preferred_running_days is not None else []

        self.available_time_per_session_minutes = available_time_per_session_minutes


    def display_profile(self):

        """Prints the user's profile information."""

        print(f"--- User Profile for {self.user_id} ---")

        print(f"Age: {self.age} years")

        print(f"Gender: {self.gender}")

        print(f"Weight: {self.weight_kg} kg")

        print(f"Height: {self.height_cm} cm")

        print(f"Fitness Level: {self.fitness_level}")

        print(f"Training Goal: {self.training_goal}")

        print(f"Recent Training History: {self.recent_training_history}")

        print(f"Health Issues: {', '.join(self.health_issues) if self.health_issues else 'None'}")

        print(f"Handicaps: {', '.join(self.handicaps) if self.handicaps else 'None'}")

        print(f"Max Heart Rate: {self.max_heart_rate if self.max_heart_rate else 'N/A'}")

        print(f"Resting Heart Rate: {self.resting_heart_rate if self.resting_heart_rate else 'N/A'}")

        print(f"VO2 Max: {self.vo2_max if self.vo2_max else 'N/A'}")

        print(f"Preferred Running Days: {', '.join(self.preferred_running_days) if self.preferred_running_days else 'Any'}")

        print(f"Available Time per Session: {self.available_time_per_session_minutes} minutes")

        print("------------------------------------")


B. Training Plan Generation Engine


The Training Plan Generation Engine is the brain of the AI agent, responsible for taking the detailed user profile and translating it into a concrete, week-by-week training schedule. This engine must incorporate principles of exercise science, progressive overload, and injury prevention, all while adapting to the user's specific constraints and goals.

The algorithm overview for plan generation typically starts with a rule-based system. For instance, a beginner aiming for a 5-kilometer run will follow a different progression than an intermediate runner targeting a 10-kilometer race. The engine first identifies the appropriate template or baseline plan based on the user's goal and initial fitness level. Then, it fine-tunes this template by considering other factors.

Factors to consider are numerous and interconnected. Intensity refers to how hard the user should be working during a run, often measured by pace, heart rate zones, or perceived exertion. Volume is the total amount of running, typically measured in kilometers or miles per week. Frequency indicates how many times per week the user should run. Progression is the gradual increase in volume, intensity, or duration over time, which is crucial for adaptation and improvement without leading to overtraining or injury. The engine must ensure that these factors are balanced and increase safely over the duration of the plan. For example, a beginner's plan might start with a run-walk approach, gradually increasing the running segments, while an advanced plan might focus on speed work and long runs.

Adaptation is a key feature of an intelligent agent. The plan should not be static. If a user consistently reports high levels of fatigue, experiences pain, or misses sessions, the plan needs to be adjusted. Conversely, if a user progresses faster than expected, the plan can be accelerated slightly. This dynamic adjustment ensures the plan remains challenging but achievable, preventing both boredom and burnout.

Here is a Python code example for a `TrainingPlanGenerator` class. This class outlines the structure for generating a training plan, taking a `UserProfile` as input. It includes methods for creating different types of running workouts and combining them into a weekly schedule. The `generate_plan` method serves as the orchestrator, applying rules and logic based on the user's profile to construct a personalized plan.


import datetime


class TrainingPlanGenerator:

    """

    Generates a personalized running training plan based on a user's profile.

    """


    def __init__(self, user_profile):

        """

        Initializes the TrainingPlanGenerator with a UserProfile.


        Args:

            user_profile (UserProfile): An instance of the UserProfile class.

        """

        self.profile = user_profile

        self.plan = []  # Stores the generated weekly plan


    def _calculate_base_intensity(self):

        """

        Calculates a base intensity level based on age and fitness.

        This is a simplified example; real-world would use more complex formulas.

        Returns:

            float: A multiplier for intensity (e.g., 0.6 for beginner, 0.8 for advanced).

        """

        base_intensity = 0.6  # Default for beginner

        if self.profile.fitness_level == 'Intermediate':

            base_intensity = 0.75

        elif self.profile.fitness_level == 'Advanced':

            base_intensity = 0.9

        

        # Adjust slightly for age (older might need slightly lower intensity initially)

        if self.profile.age > 50:

            base_intensity *= 0.95

        elif self.profile.age < 25:

            base_intensity *= 1.05

        

        return base_intensity


    def _generate_warm_up(self):

        """Generates a standard warm-up routine."""

        return "5-10 minutes brisk walking or light jogging."


    def _generate_cool_down(self):

        """Generates a standard cool-down routine."""

        return "5-10 minutes walking, followed by stretching."


    def _generate_stretching_routine(self, focus_area=None):

        """

        Generates a stretching routine, optionally focusing on specific areas.

        Args:

            focus_area (str, optional): Specific muscle group to focus on (e.g., 'hamstrings').

        Returns:

            str: Description of the stretching routine.

        """

        stretches = [

            "Quad Stretch (standing or kneeling)",

            "Hamstring Stretch (seated or standing toe touch)",

            "Calf Stretch (gastrocnemius and soleus)",

            "Hip Flexor Stretch (kneeling lunge)",

            "Glute Stretch (figure-four stretch)",

            "IT Band Stretch (cross-legged standing stretch)"

        ]

        if focus_area:

            # Simple logic to prioritize stretches for a focus area

            filtered_stretches = [s for s in stretches if focus_area.lower() in s.lower()]

            if filtered_stretches:

                return f"Focus on {focus_area} stretches: {', '.join(filtered_stretches)}. Also include general stretches."

        return f"General post-run stretching: {', '.join(stretches)}."


    def _create_running_workout(self, week_num, workout_type, duration_minutes, intensity_level, distance_km=None):

        """

        Creates a single running workout description.


        Args:

            week_num (int): The current week number of the plan.

            workout_type (str): Type of run (e.g., 'Easy Run', 'Intervals', 'Long Run').

            duration_minutes (int): Expected duration of the workout in minutes.

            intensity_level (float): A multiplier for intensity, derived from base_intensity.

            distance_km (float, optional): Target distance in kilometers.


        Returns:

            dict: A dictionary describing the workout.

        """

        workout = {

            "week": week_num,

            "type": workout_type,

            "duration_minutes": duration_minutes,

            "intensity_level": f"{int(intensity_level * 100)}% effort",

            "warm_up": self._generate_warm_up(),

            "cool_down": self._generate_cool_down(),

            "stretching": self._generate_stretching_routine()

        }

        if distance_km:

            workout["distance_km"] = distance_km


        # Adjust intensity/duration based on health issues or handicaps

        if any(issue in self.profile.health_issues for issue in ['Knee Pain', 'Ankle Injury']):

            workout["notes"] = "Consider low-impact alternatives or reduced intensity due to joint concerns."

            workout["intensity_level"] = f"{int(intensity_level * 80)}% effort" # Reduce intensity

            workout["duration_minutes"] = int(duration_minutes * 0.8) # Reduce duration

        elif 'Asthma' in self.profile.health_issues:

            workout["notes"] = "Ensure inhaler is accessible. Monitor breathing carefully."


        return workout


    def generate_plan(self, num_weeks=12):

        """

        Generates a full training plan for the specified number of weeks.


        Args:

            num_weeks (int): The total number of weeks for the training plan.

        """

        self.plan = []

        base_intensity = self._calculate_base_intensity()


        for week in range(1, num_weeks + 1):

            weekly_workouts = []

            

            # Determine weekly volume and intensity progression

            # This is a simplified linear progression for demonstration

            # Real-world would use more sophisticated models (e.g., 10% rule, periodization)

            progression_factor = 1 + (week - 1) * 0.05 # 5% increase per week

            

            # Adjust progression based on recent training history

            if self.profile.recent_training_history == 'Sedentary':

                progression_factor = 1 + (week - 1) * 0.03 # Slower progression

                if week == 1: # First week for sedentary users might be mostly walking

                    weekly_workouts.append(self._create_running_workout(

                        week, "Walk/Jog", 30, base_intensity * 0.5, distance_km=2.0))

                    weekly_workouts.append(self._create_running_workout(

                        week, "Walk/Jog", 35, base_intensity * 0.5, distance_km=2.5))

                    weekly_workouts.append(self._create_running_workout(

                        week, "Cross-Training (e.g., cycling/swimming)", 45, base_intensity * 0.6))

                    self.plan.append(weekly_workouts)

                    continue # Skip to next week if first week is special


            # Define typical workouts for a week

            # Day 1: Easy Run / Base Building

            easy_run_duration = int(40 * progression_factor)

            easy_run_distance = round(5.0 * progression_factor, 1)

            weekly_workouts.append(self._create_running_workout(

                week, "Easy Run", easy_run_duration, base_intensity * 0.8, distance_km=easy_run_distance))


            # Day 2: Speed Work / Tempo Run (Introduced later for beginners)

            if week > 3 or self.profile.fitness_level in ['Intermediate', 'Advanced']:

                speed_duration = int(30 * progression_factor)

                weekly_workouts.append(self._create_running_workout(

                    week, "Intervals/Tempo Run", speed_duration, base_intensity * 1.1, distance_km=None))

            else:

                # For beginners, more easy runs or cross-training

                weekly_workouts.append(self._create_running_workout(

                    week, "Easy Run (shorter)", int(easy_run_duration * 0.7), base_intensity * 0.7, distance_km=round(easy_run_distance * 0.7, 1)))


            # Day 3: Long Run (Key for endurance)

            long_run_duration = int(60 * progression_factor)

            long_run_distance = round(8.0 * progression_factor, 1)

            weekly_workouts.append(self._create_running_workout(

                week, "Long Run", long_run_duration, base_intensity * 0.7, distance_km=long_run_distance))


            # Add cross-training or rest days based on preferred days/availability

            if len(self.profile.preferred_running_days) < 3:

                # If user prefers fewer running days, suggest cross-training or rest

                weekly_workouts.append({

                    "week": week,

                    "type": "Cross-Training (e.g., cycling, swimming)",

                    "duration_minutes": int(45 * progression_factor),

                    "intensity_level": f"{int(base_intensity * 0.6 * 100)}% effort",

                    "notes": "Low-impact activity to build aerobic fitness and aid recovery."

                })

            else:

                weekly_workouts.append({

                    "week": week,

                    "type": "Rest Day or Active Recovery (light walk)",

                    "duration_minutes": 0,

                    "intensity_level": "N/A",

                    "notes": "Crucial for recovery and preventing overtraining."

                })


            self.plan.append(weekly_workouts)


        # Final adjustments for specific goals (e.g., taper for 5K)

        if self.profile.training_goal == 'Run 5K' and num_weeks >= 2:

            # Tapering for the last week before a 5K race

            taper_week = self.plan[-1]

            for workout in taper_week:

                if "duration_minutes" in workout:

                    workout["duration_minutes"] = int(workout["duration_minutes"] * 0.5) # Halve duration

                if "distance_km" in workout:

                    workout["distance_km"] = round(workout["distance_km"] * 0.5, 1) # Halve distance

                workout["notes"] = workout.get("notes", "") + " (Taper week: reduce volume, maintain intensity for short bursts)."

            # Add a specific race day entry

            self.plan.append([{

                "week": num_weeks + 1,

                "type": "Race Day: 5K Event",

                "duration_minutes": None,

                "intensity_level": "Max Effort",

                "warm_up": self._generate_warm_up(),

                "cool_down": self._generate_cool_down(),

                "stretching": self._generate_stretching_routine(),

                "notes": "Focus on hydration and nutrition. Enjoy your race!"

            }])



    def display_plan(self):

        """Prints the generated training plan in a readable format."""

        print(f"\n--- Training Plan for {self.profile.user_id} (Goal: {self.profile.training_goal}) ---")

        for i, week_workouts in enumerate(self.plan):

            print(f"\nWeek {i+1}:")

            for workout in week_workouts:

                print(f"  - Type: {workout['type']}")

                if workout.get('duration_minutes') is not None:

                    print(f"    Duration: {workout['duration_minutes']} minutes")

                if workout.get('distance_km') is not None:

                    print(f"    Distance: {workout['distance_km']} km")

                print(f"    Intensity: {workout['intensity_level']}")

                if workout.get('warm_up'):

                    print(f"    Warm-up: {workout['warm_up']}")

                if workout.get('cool_down'):

                    print(f"    Cool-down: {workout['cool_down']}")

                if workout.get('stretching'):

                    print(f"    Stretching: {workout['stretching']}")

                if workout.get('notes'):

                    print(f"    Notes: {workout['notes']}")

        print("--------------------------------------------------")


C. Exercise Database and Selector


A comprehensive training plan extends beyond just running. To promote overall fitness, prevent injuries, and enhance performance, it is crucial to incorporate supplementary exercises. The Exercise Database and Selector module manages a repository of various exercises and intelligently selects the most appropriate ones for the user.

The database should categorize exercises into several types. Running workouts are the core, including easy runs for building aerobic base, interval training for speed and VO2 max improvement, tempo runs for lactate threshold enhancement, and long runs for endurance. Alongside these, stretching exercises are vital for maintaining flexibility and preventing common running-related injuries. These should target key muscle groups used in running, such as the quadriceps, hamstrings, calves, hip flexors, and glutes. Examples include standing quad stretches, seated hamstring stretches, calf raises, hip flexor lunges, and figure-four glute stretches. Additionally, the database might include strength training exercises, which, while optional, are highly beneficial for runners. These could involve bodyweight exercises like squats, lunges, planks, and glute bridges, or exercises with weights for more advanced users.

The selector function within this module uses the user's profile to filter and recommend exercises. For instance, if a user reports knee pain, the selector might prioritize low-impact stretches and strength exercises that support knee stability, while temporarily avoiding high-impact plyometrics. If the user's goal is a marathon, the selector will emphasize longer duration runs and endurance-focused strength work.

Here is a Python code example for an `ExerciseDatabase` class and an `ExerciseSelector` function. The `ExerciseDatabase` stores a collection of different types of exercises with their descriptions and benefits. The `ExerciseSelector` function then intelligently picks relevant exercises based on a given user profile and the type of exercise required.


class ExerciseDatabase:

    """

    Manages a database of various exercises relevant for runners.

    """


    def __init__(self):

        """Initializes the ExerciseDatabase with predefined exercises."""

        self.running_workouts = {

            "Easy Run": "Steady, conversational pace run to build aerobic base. Focus on duration, not speed.",

            "Tempo Run": "Run at a comfortably hard pace, where you can speak in short sentences. Improves lactate threshold.",

            "Intervals": "Short bursts of high-intensity running followed by recovery periods. Improves speed and VO2 max.",

            "Long Run": "Extended duration run at an easy pace to build endurance and mental toughness.",

            "Fartlek": "Unstructured speed play, varying pace and intensity during a continuous run.",

            "Hill Repeats": "Running uphill at high effort, then jogging or walking down for recovery. Builds leg strength and power."

        }

        self.stretching_exercises = {

            "Quad Stretch": "Targets the quadriceps muscles. Stand and pull one heel towards your glutes.",

            "Hamstring Stretch": "Targets the hamstrings. Sit with one leg extended, reach for your toes.",

            "Calf Stretch (Gastrocnemius)": "Targets the upper calf. Lean against a wall with one leg back, heel down.",

            "Calf Stretch (Soleus)": "Targets the lower calf. Same as above, but bend the back knee.",

            "Hip Flexor Stretch": "Targets the hip flexors. Kneeling lunge position, push hips forward.",

            "Glute Stretch (Figure-Four)": "Targets the glutes. Lie on your back, cross one ankle over the opposite knee.",

            "IT Band Stretch": "Targets the IT band. Cross one leg behind the other, lean away from the front leg.",

            "Dynamic Leg Swings": "Warm-up stretch for hips and hamstrings. Swing legs forward-back and side-to-side."

        }

        self.strength_exercises = {

            "Bodyweight Squats": "Strengthens quads, hamstrings, glutes. Stand shoulder-width apart, lower hips as if sitting.",

            "Lunges": "Strengthens quads, hamstrings, glutes. Step forward with one leg, lower hips until both knees are bent at 90 degrees.",

            "Plank": "Strengthens core. Hold a push-up position on forearms.",

            "Glute Bridges": "Strengthens glutes and hamstrings. Lie on back, knees bent, lift hips off the ground.",

            "Calf Raises": "Strengthens calves. Stand on toes, then lower.",

            "Single-Leg Deadlift": "Improves balance and strengthens hamstrings/glutes. Hinge at hips, extend one leg back.",

            "Side Plank": "Strengthens obliques and core stability. Hold a plank position on one forearm, body in a straight line."

        }

        self.recovery_exercises = {

            "Foam Rolling (Quads)": "Self-massage for quads.",

            "Foam Rolling (Hamstrings)": "Self-massage for hamstrings.",

            "Foam Rolling (Calves)": "Self-massage for calves.",

            "Foam Rolling (IT Band)": "Self-massage for IT band.",

            "Yoga/Pilates (Runner-focused)": "Improves flexibility, core strength, and body awareness."

        }


    def get_exercise_description(self, exercise_type, exercise_name):

        """

        Retrieves the description for a specific exercise.


        Args:

            exercise_type (str): The category of the exercise (e.g., 'running_workouts').

            exercise_name (str): The name of the exercise.


        Returns:

            str: The description of the exercise, or an error message if not found.

        """

        if exercise_type == 'running_workouts' and exercise_name in self.running_workouts:

            return self.running_workouts[exercise_name]

        elif exercise_type == 'stretching_exercises' and exercise_name in self.stretching_exercises:

            return self.stretching_exercises[exercise_name]

        elif exercise_type == 'strength_exercises' and exercise_name in self.strength_exercises:

            return self.strength_exercises[exercise_name]

        elif exercise_type == 'recovery_exercises' and exercise_name in self.recovery_exercises:

            return self.recovery_exercises[exercise_name]

        return f"Exercise '{exercise_name}' not found in '{exercise_type}'."


def select_exercises(user_profile, exercise_db, exercise_category, num_exercises=3, focus_areas=None):

    """

    Selects a list of exercises based on user profile and category.


    Args:

        user_profile (UserProfile): The user's profile.

        exercise_db (ExerciseDatabase): An instance of the ExerciseDatabase.

        exercise_category (str): The category of exercises to select from

                                 ('stretching_exercises', 'strength_exercises', 'recovery_exercises').

        num_exercises (int): The maximum number of exercises to select.

        focus_areas (list, optional): Specific areas to prioritize for selection (e.g., ['knees', 'hamstrings']).


    Returns:

        list: A list of selected exercise names.

    """

    available_exercises = []

    if exercise_category == 'stretching_exercises':

        available_exercises = list(exercise_db.stretching_exercises.keys())

    elif exercise_category == 'strength_exercises':

        available_exercises = list(exercise_db.strength_exercises.keys())

    elif exercise_category == 'recovery_exercises':

        available_exercises = list(exercise_db.recovery_exercises.keys())

    else:

        return [] # Invalid category


    selected = []

    

    # Prioritize exercises based on health issues/handicaps if focus_areas are not provided

    # or if specific focus areas are provided

    prioritized_exercises = []

    if focus_areas:

        for area in focus_areas:

            # Simple keyword matching for demonstration

            for ex in available_exercises:

                if area.lower() in ex.lower() or any(area.lower() in desc.lower() for desc in [exercise_db.get_exercise_description(exercise_category, ex)]):

                    if ex not in prioritized_exercises:

                        prioritized_exercises.append(ex)

    

    # Add exercises based on general health issues

    for issue in user_profile.health_issues + user_profile.handicaps:

        if 'knee' in issue.lower() or 'patella' in issue.lower():

            for ex in ['Bodyweight Squats', 'Lunges', 'Glute Bridges', 'IT Band Stretch', 'Quad Stretch']:

                if ex in available_exercises and ex not in prioritized_exercises:

                    prioritized_exercises.append(ex)

        if 'back' in issue.lower() or 'spine' in issue.lower():

            for ex in ['Plank', 'Side Plank', 'Glute Bridges', 'Yoga/Pilates (Runner-focused)']:

                if ex in available_exercises and ex not in prioritized_exercises:

                    prioritized_exercises.append(ex)

        if 'hamstring' in issue.lower():

            for ex in ['Hamstring Stretch', 'Single-Leg Deadlift', 'Foam Rolling (Hamstrings)']:

                if ex in available_exercises and ex not in prioritized_exercises:

                    prioritized_exercises.append(ex)

        # Add more mappings for other issues


    # Combine prioritized with general exercises, ensuring uniqueness

    selected_candidates = prioritized_exercises + [ex for ex in available_exercises if ex not in prioritized_exercises]

    

    # Select up to num_exercises

    for ex in selected_candidates:

        if len(selected) < num_exercises:

            selected.append(ex)

        else:

            break

            

    return selected


D. Health and Safety Monitor


The Health and Safety Monitor is a critical module designed to ensure the user's well-being throughout the training process. Running, while beneficial, carries risks of injury or overtraining if not managed carefully. This module acts as a safeguard, continuously assessing the user's physical state and adjusting the training plan accordingly.

Integration of health data is paramount. This can come from various sources, including wearable devices (smartwatches, fitness trackers) that provide metrics such as heart rate, sleep quality, and activity levels. Crucially, self-reported pain, fatigue, and mood are equally important, as these subjective measures often provide early indicators of potential issues that objective data might miss. The user should have an easy way to log these feelings.

Risk assessment for injuries involves analyzing patterns in the collected data. For example, a sudden increase in training volume combined with persistent knee pain and poor sleep quality would trigger a high-risk alert. The monitor can employ rule-based logic (e.g., "if pain score > X and mileage increased by > Y%, then flag risk") or more sophisticated machine learning models trained on historical injury data.

Modification of plans based on health is the ultimate output of this module. If a high risk is detected, the monitor should recommend specific adjustments. This might include reducing the intensity or duration of upcoming runs, suggesting a complete rest day, recommending cross-training activities (like swimming or cycling) that are less impactful, or even advising a consultation with a medical professional. The goal is to prevent minor discomforts from escalating into serious injuries that could derail the entire training plan.

Here is a Python code example for a `HealthMonitor` function. This function simulates the process of checking a user's health status and providing recommendations. It takes a `UserProfile` and recent health feedback as input and suggests modifications to the training plan based on predefined rules.


class HealthMonitor:

    """

    Monitors user health and provides recommendations for training plan adjustments.

    """


    def __init__(self, user_profile):

        """

        Initializes the HealthMonitor with a UserProfile.


        Args:

            user_profile (UserProfile): An instance of the UserProfile class.

        """

        self.profile = user_profile

        self.injury_risk_threshold = 7 # On a scale of 1-10, 10 being highest risk


    def assess_health_feedback(self, reported_pain_level, fatigue_level, sleep_quality_score, recent_performance_trend='stable'):

        """

        Assesses user's self-reported health feedback to determine potential risks.


        Args:

            reported_pain_level (int): User's reported pain level (0-10, 0=none, 10=severe).

            fatigue_level (int): User's reported fatigue level (0-10, 0=rested, 10=exhausted).

            sleep_quality_score (int): User's reported sleep quality (0-10, 0=poor, 10=excellent).

            recent_performance_trend (str): Trend in recent performance ('improving', 'stable', 'declining').


        Returns:

            dict: A dictionary containing risk assessment and recommendations.

        """

        risk_score = 0

        recommendations = []


        # --- Rule-based risk assessment ---


        # Pain assessment

        if reported_pain_level > 5:

            risk_score += (reported_pain_level - 5) * 2 # Higher pain, higher risk

            recommendations.append(f"Significant pain reported ({reported_pain_level}/10). Consider rest or medical consultation.")

            if reported_pain_level > 7:

                recommendations.append("Strongly recommend stopping current activity and seeking professional advice.")

        elif reported_pain_level > 0:

            risk_score += 1

            recommendations.append(f"Minor pain reported ({reported_pain_level}/10). Monitor closely, consider active recovery.")


        # Fatigue assessment

        if fatigue_level > 6:

            risk_score += (fatigue_level - 6) * 1.5

            recommendations.append(f"High fatigue reported ({fatigue_level}/10). Prioritize rest and reduce training volume.")

        elif fatigue_level > 4:

            risk_score += 1

            recommendations.append(f"Moderate fatigue reported ({fatigue_level}/10). Ensure adequate recovery.")


        # Sleep quality assessment

        if sleep_quality_score < 4:

            risk_score += (4 - sleep_quality_score) * 2

            recommendations.append(f"Poor sleep quality reported ({sleep_quality_score}/10). Focus on sleep hygiene.")

        elif sleep_quality_score < 6:

            risk_score += 1

            recommendations.append(f"Suboptimal sleep quality reported ({sleep_quality_score}/10). Aim for more consistent sleep.")


        # Performance trend assessment

        if recent_performance_trend == 'declining':

            risk_score += 3

            recommendations.append("Recent performance decline detected. Could indicate overtraining or insufficient recovery.")

        elif recent_performance_trend == 'improving' and (reported_pain_level > 0 or fatigue_level > 5):

            recommendations.append("Performance improving but with pain/fatigue. Be careful not to push too hard too fast.")


        # Health issues from profile

        if any(issue in self.profile.health_issues for issue in ['Knee Pain', 'Ankle Injury', 'Back Pain']):

            if reported_pain_level > 3: # If pre-existing issue is flaring up

                risk_score += 2

                recommendations.append(f"Pre-existing condition '{self.profile.health_issues[0]}' may be aggravated. Adjust training.")

        if 'Asthma' in self.profile.health_issues and reported_pain_level > 0: # Simplified check

            recommendations.append("Monitor respiratory symptoms closely during exercise.")


        # --- Determine overall status ---

        overall_status = "Green: All good!"

        if risk_score >= self.injury_risk_threshold:

            overall_status = "Red: High Risk! Immediate action recommended."

        elif risk_score >= self.injury_risk_threshold / 2:

            overall_status = "Yellow: Moderate Risk. Caution advised."


        return {

            "risk_score": risk_score,

            "overall_status": overall_status,

            "recommendations": recommendations if recommendations else ["No specific recommendations at this time. Keep up the good work!"]

        }


    def provide_plan_adjustment(self, health_assessment_result):

        """

        Provides concrete plan adjustment suggestions based on health assessment.


        Args:

            health_assessment_result (dict): The output from assess_health_feedback.


        Returns:

            list: A list of suggested adjustments for the training plan.

        """

        adjustments = []

        status = health_assessment_result['overall_status']

        risk_score = health_assessment_result['risk_score']


        if "Red" in status:

            adjustments.append("IMMEDIATE ACTION: Consider a complete rest day or consult a medical professional.")

            adjustments.append("Reduce all planned running volume by at least 50% for the next 3-5 days.")

            adjustments.append("Focus on gentle active recovery (e.g., walking, stretching) if pain allows.")

        elif "Yellow" in status:

            adjustments.append("CAUTION: Reduce intensity and/or duration of upcoming runs by 20-30%.")

            adjustments.append("Prioritize recovery: ensure adequate sleep, nutrition, and hydration.")

            adjustments.append("Incorporate more stretching and foam rolling.")

            adjustments.append("Consider swapping a running day for cross-training (e.g., swimming, cycling) to reduce impact.")

        else:

            adjustments.append("Continue with the planned training. Maintain good recovery practices.")


        # Add specific adjustments based on recommendations

        for rec in health_assessment_result['recommendations']:

            if "reduce training volume" in rec.lower() and "Yellow" not in status and "Red" not in status:

                adjustments.append("Consider a slight reduction in volume for the next session.")

            if "monitor breathing" in rec.lower():

                adjustments.append("Pay extra attention to breathing and symptoms during exercise.")

        

        # Remove duplicates

        return list(set(adjustments))


E. Route Planning Module


For many runners, the environment in which they train plays a significant role in their motivation and safety. The Route Planning Module aims to enhance the user experience by suggesting suitable running routes in their local area. This personalized touch can make training more engaging and convenient.

Leveraging GPS or address information is the starting point. When the AI agent can determine the user's current GPS position or a specified address, it can then interact with mapping services (conceptually, as direct API calls are outside this scope) to identify nearby paths, parks, or trails. The system would need access to geographical data, including road networks, pedestrian paths, and elevation data.

Route characteristics are key to providing useful suggestions. Each proposed training route should clearly define a start point, a destination (which could be the same as the start for a loop), and a series of intermediate points that define the path. Beyond just geographical coordinates, the module should consider other attributes such as the total distance, estimated elevation gain (which impacts difficulty), the type of surface (e.g., asphalt, trail, gravel), and safety considerations (e.g., well-lit areas for evening runs, areas with less traffic). For instance, a beginner might be recommended a flat, paved loop in a local park, while an advanced trail runner might prefer a challenging route with significant elevation changes on varied terrain.

Integration with mapping services would involve sending queries (e.g., "find a 5km loop near [address] with minimal elevation") and parsing the results to extract route details. While I cannot directly call external mapping APIs, the conceptual framework involves defining how such data would be structured and presented to the user.

Here is a Python code example for a `RoutePlanner` class. This class focuses on defining the data structure for a running route and includes a conceptual method for suggesting routes. Since direct interaction with external mapping APIs is not possible, the `suggest_routes` method will simulate route generation based on simplified criteria and pre-defined route templates.


class RoutePlanner:

    """

    Manages and suggests running routes based on user's location and preferences.

    """


    def __init__(self):

        """Initializes the RoutePlanner with a collection of conceptual routes."""

        self.available_routes = [

            {

                "id": "R001",

                "name": "Park Loop",

                "description": "A flat, scenic loop through a local park, ideal for easy runs.",

                "start_location": "Park Entrance (Lat: 34.0522, Lon: -118.2437)",

                "destination_location": "Park Entrance (Lat: 34.0522, Lon: -118.2437)",

                "intermediate_points": ["Gazebo (Lat: 34.0530, Lon: -118.2420)", "Lake View (Lat: 34.0510, Lon: -118.2450)"],

                "distance_km": 5.0,

                "elevation_gain_m": 10,

                "surface_type": "Paved",

                "safety_notes": "Well-lit, popular area, suitable for all times.",

                "location_tags": ["urban", "park", "flat"]

            },

            {

                "id": "R002",

                "name": "River Trail Out-and-Back",

                "description": "A gentle trail along the river, good for longer, steady runs.",

                "start_location": "Riverfront Parking (Lat: 34.0600, Lon: -118.2500)",

                "destination_location": "Bridge Viewpoint (Lat: 34.0700, Lon: -118.2600)",

                "intermediate_points": ["Old Mill (Lat: 34.0650, Lon: -118.2550)"],

                "distance_km": 8.0,

                "elevation_gain_m": 50,

                "surface_type": "Gravel/Dirt",

                "safety_notes": "Less lit at night, bring headlamp. Moderate traffic.",

                "location_tags": ["suburban", "trail", "gentle hills"]

            },

            {

                "id": "R003",

                "name": "Hill Challenge Loop",

                "description": "A challenging route with significant climbs, great for hill training.",

                "start_location": "Mountain Base Parking (Lat: 34.0800, Lon: -118.2800)",

                "destination_location": "Mountain Base Parking (Lat: 34.0800, Lon: -118.2800)",

                "intermediate_points": ["Summit Viewpoint (Lat: 34.0850, Lon: -118.2850)", "Forest Path (Lat: 34.0780, Lon: -118.2750)"],

                "distance_km": 10.0,

                "elevation_gain_m": 300,

                "surface_type": "Trail/Uneven",

                "safety_notes": "Remote, carry water and phone. Not recommended for beginners.",

                "location_tags": ["mountain", "trail", "steep hills"]

            }

        ]


    def suggest_routes(self, user_profile, preferred_distance_km=None, max_elevation_gain_m=None,

                       preferred_surface_type=None, current_location_lat_lon=None):

        """

        Suggests suitable running routes based on user preferences and (conceptual) location.


        Args:

            user_profile (UserProfile): The user's profile.

            preferred_distance_km (float, optional): Desired route distance in kilometers.

            max_elevation_gain_m (int, optional): Maximum acceptable elevation gain in meters.

            preferred_surface_type (str, optional): Desired surface type ('Paved', 'Gravel/Dirt', 'Trail/Uneven').

            current_location_lat_lon (tuple, optional): User's current (latitude, longitude) for proximity.

                                                        (Conceptual for this example).


        Returns:

            list: A list of dictionaries, each representing a suggested route.

        """

        suggested = []

        

        # In a real system, current_location_lat_lon would be used to filter

        # routes geographically. For this example, we'll simulate filtering.

        

        for route in self.available_routes:

            match = True


            # Filter by distance

            if preferred_distance_km is not None:

                # Allow a small tolerance (e.g., +/- 20%)

                if not (preferred_distance_km * 0.8 <= route['distance_km'] <= preferred_distance_km * 1.2):

                    match = False

            

            # Filter by elevation gain

            if max_elevation_gain_m is not None:

                if route['elevation_gain_m'] > max_elevation_gain_m:

                    match = False


            # Filter by surface type

            if preferred_surface_type is not None:

                if route['surface_type'].lower() != preferred_surface_type.lower():

                    match = False

            

            # Filter by fitness level (simple rule)

            if user_profile.fitness_level == 'Beginner' and route['elevation_gain_m'] > 100:

                match = False # Beginners generally shouldn't start with very hilly routes

            if user_profile.fitness_level == 'Beginner' and "Trail/Uneven" in route['surface_type']:

                match = False # Beginners should avoid highly technical trails initially


            if match:

                suggested.append(route)

        

        return suggested


    def display_route(self, route):

        """

        Prints the details of a single route.


        Args:

            route (dict): A dictionary representing a route.

        """

        print(f"--- Route: {route['name']} (ID: {route['id']}) ---")

        print(f"  Description: {route['description']}")

        print(f"  Start: {route['start_location']}")

        print(f"  Destination: {route['destination_location']}")

        print(f"  Intermediate Points: {', '.join(route['intermediate_points'])}")

        print(f"  Distance: {route['distance_km']} km")

        print(f"  Elevation Gain: {route['elevation_gain_m']} m")

        print(f"  Surface Type: {route['surface_type']}")

        print(f"  Safety Notes: {route['safety_notes']}")

        print("------------------------------------------")


AI/ML Techniques Employed


The development of a sophisticated AI running coach benefits from a combination of artificial intelligence and machine learning techniques. These methods allow the agent to not only follow predefined rules but also to learn, adapt, and make intelligent decisions based on data.


A. Rule-Based Systems


Rule-based systems form the initial layer of intelligence, especially for plan generation. These systems operate on a set of "if-then" statements derived from expert knowledge in exercise physiology and coaching. For example, a rule might state: "IF user_fitness_level is 'Beginner' AND user_training_goal is 'Run 5K' THEN initial_weekly_volume is '10km' AND include_run_walk_intervals." These rules are explicit and provide a transparent way to generate a foundational training plan. They are particularly effective for handling well-defined scenarios and ensuring adherence to established training principles. As demonstrated in the `TrainingPlanGenerator`, initial plan structures and progressions are often governed by such rules, which are then refined by other modules.


B. Machine Learning for Adaptation


While rule-based systems are excellent for initial plan generation, machine learning (ML) techniques provide the crucial ability for the AI agent to learn from experience and adapt dynamically.

Reinforcement Learning (conceptual) is a powerful paradigm where an agent learns to make decisions by performing actions in an environment and receiving rewards or penalties. In the context of a running coach, the "environment" is the user's training journey, and "actions" are the modifications made to the training plan (e.g., increasing mileage, suggesting rest). A "reward" could be the user successfully completing a workout with positive feedback, improved performance, or achieving a goal without injury. A "penalty" would be reported pain, missed workouts, or injury. Over time, the reinforcement learning agent could learn optimal strategies for progression, tapering, and recovery that maximize user success and minimize adverse outcomes. This would allow the system to discover subtle patterns and interactions that might be too complex to encode in explicit rules.

Predictive modeling (conceptual) can be used for various aspects, such as predicting the risk of injury or forecasting optimal recovery times. By analyzing historical data from many users (anonymized and aggregated, of course), including training load, sleep patterns, heart rate variability, and self-reported pain, an ML model could learn to predict when a user is approaching an overtraining state or is at higher risk for a specific injury. For instance, a model might identify that a sudden 20% increase in weekly mileage combined with two consecutive nights of poor sleep significantly increases the probability of a hamstring strain. This predictive capability allows the agent to issue proactive warnings and suggest preventative adjustments before an issue arises.


C. Natural Language Processing (NLP)


Natural Language Processing (NLP) enables the AI agent to understand and interact with users using human language. This is vital for a user-friendly coaching experience.

For user input, NLP can process free-form text from users describing their goals ("I want to run a marathon next year"), feedback ("My knees are feeling a bit sore after that run"), or questions ("What kind of stretches should I do for my calves?"). This allows for a more natural and less structured interaction than filling out forms. The NLP component would extract key entities (e.g., "marathon," "knees," "sore," "calves") and intents (e.g., "set goal," "report pain," "ask for exercise").

For output, NLP helps in generating clear, empathetic, and actionable coaching advice. Instead of just presenting raw data or generic instructions, the agent can formulate responses like: "It sounds like your knees are experiencing some soreness. I recommend reducing your mileage by 20% for your next two runs and focusing on these specific quad and hamstring stretches." This makes the AI coach feel more like a human expert, improving user engagement and adherence to the plan.


System Architecture


The AI running coach operates through a modular architecture, where each component performs a specialized function and interacts with others to achieve the overall goal of personalized training.


[

   +---------------------+        +---------------------+

   |                     |        |                     |

   |   User Interface    |        |   External Data     |

   | (Web/Mobile App)    |        | (Wearables, GPS)    |

   |                     |        |                     |

   +----------+----------+        +----------+----------+

              |                              |

              | User Input (Goals, Feedback) | Data Stream (HR, Sleep, Location)

              |                              |

              v                              v

   +----------+------------------------------------------+

   |                                                     |

   |               AI Running Coach Core System          |

   |                                                     |

   |   +---------------------+   +---------------------+ |

   |   |                     |   |                     | |

   |   |  A. User Profile    |<->|  B. Training Plan   | |

   |   |     Module          |   |  Generation Engine  | |

   |   | (Data Storage,      |   | (Rules, ML Logic)   | |

   |   |   Management)       |   |                     | |

   |   +----------^----------+   +----------^----------+ |

   |              |                        |             |

   |              | User Data              | Plan Details|

   |              |                        |             |

   |   +----------v----------+   +----------v----------+ |

   |   |                     |   |                     | |

   |   |  C. Exercise        |<->|  D. Health & Safety | |

   |   |     Database &      |   |     Monitor         | |

   |   |     Selector        |   | (Risk Assessment,   | |

   |   | (Running, Stretching,|   |   Feedback Analysis)| |

   |   |   Strength)         |   |                     | |

   |   +----------^----------+   +----------^----------+ |

   |              |                        |             |

   |              | Exercise Suggestions   | Health Alerts/Adjustments

   |              |                        |             |

   |   +----------v----------+             |             |

   |   |                     |             |             |

   |   |  E. Route Planning  |-------------+             |

   |   |     Module          |                           |

   |   | (Location, Routes)  |                           |

   |   +---------------------+                           |

   |                                                     |

   +-----------------------------------------------------+

              |

              | Personalized Plan, Advice, Route Suggestions

              v

   +---------------------+

   |                     |

   |   User Output       |

   | (Display Plan,      |

   |  Notifications)     |

   +---------------------+

]


Figure 1: High-Level Architecture of the AI Running Coach


The data flow begins with the User Interface, where the user provides initial input such as their goals, age, and health conditions. This information is fed into the User Profile Module, which stores and manages this foundational data. Concurrently, external data streams from wearables or GPS devices provide real-time metrics like heart rate, sleep quality, and location.

The Training Plan Generation Engine then retrieves the user's profile and, using a combination of rule-based logic and potentially machine learning models, crafts an initial training plan. This plan specifies the type, duration, and intensity of runs for each week.

As the plan is executed, the Health and Safety Monitor continuously receives feedback from the user (e.g., reported pain, fatigue) and data from external sources. It assesses these inputs against the user's profile and the current training load to identify potential risks or areas for adjustment. If issues are detected, the monitor communicates these to the Training Plan Generation Engine, which then modifies upcoming workouts to ensure safety and promote recovery.

The Exercise Database and Selector works in conjunction with the Training Plan Generation Engine and the Health and Safety Monitor. When a workout requires specific stretches or strength exercises, or if the health monitor suggests targeted recovery, this module provides appropriate recommendations based on the user's profile and any identified issues.

Finally, the Route Planning Module, when provided with the user's location, suggests suitable running routes that align with the day's planned workout (e.g., a 5km easy run on a flat, paved surface). This module also considers the user's fitness level and any handicaps when filtering routes.

All these components work in a continuous feedback loop, ensuring that the training plan remains dynamic, safe, and optimally tailored to the user's evolving needs. The User Interface then presents the personalized plan, health notifications, and route suggestions back to the user in an understandable format.


A Running Example: Building a 5km Plan for a Beginner


Let us walk through a practical example of how our AI running coach would operate for a new user. Imagine Sarah, a 30-year-old woman who has been sedentary for the past few years and has decided to train for her first 5-kilometer race. She occasionally experiences mild knee discomfort if she overexerts herself.


1.  User Input and Profile Creation:

Sarah downloads the AI running coach application and enters her details:

    *   Age: 30

    *   Gender: Female

    *   Weight: 65 kg

    *   Height: 165 cm

    *   Fitness Level: Beginner

    *   Training Goal: Run 5K

    *   Recent Training History: Sedentary

    *   Health Issues: ['Mild Knee Discomfort']

    *   Handicaps: []

    *   Preferred Running Days: ['Tuesday', 'Thursday', 'Saturday']

    *   Available Time per Session: 45 minutes


    The User Profile Module stores this information.


2.  Initial Plan Generation:

The Training Plan Generation Engine receives Sarah's profile. Based on her 'Beginner' fitness level, 'Run 5K' goal, and 'Sedentary' history, it initiates a conservative 12-week plan, starting with a run-walk program. The engine's rules dictate a slow progression to prevent injury, especially considering her 'Mild Knee Discomfort'.


    For Week 1, the plan might look like this:

    *   Tuesday: Walk/Jog - 25 minutes (e.g., 1 min jog, 4 min walk x 5), easy intensity.

    *   Thursday: Walk/Jog - 25 minutes (e.g., 1 min jog, 4 min walk x 5), easy intensity.

    *   Saturday: Walk/Jog - 30 minutes (e.g., 2 min jog, 3 min walk x 6), easy intensity.

    Each session includes a warm-up, cool-down, and a stretching routine.


3.  Exercise Selection:

For the stretching routine, the Exercise Database and Selector, noting Sarah's 'Mild Knee Discomfort', prioritizes stretches and gentle strengthening exercises that support knee health. It might suggest:

    *   Quad Stretch

    *   Hamstring Stretch

    *   Glute Bridges (for hip and glute strength, supporting knees)

    *   IT Band Stretch


4.  Route Suggestion:

Before her Tuesday run, Sarah checks the app. She enters her current address (or the app uses her GPS). The Route Planning Module, knowing she needs a 25-minute easy run on a paved surface, suggests a 2.5 km loop in a nearby park, highlighting its flat terrain and good lighting.


5.  Health Monitoring and Adaptation:

    After her first Saturday run, Sarah reports a pain level of 3/10 in her right knee and a fatigue level of 5/10. Her sleep quality was 7/10.

    The Health and Safety Monitor processes this feedback:

    *   Pain (3/10) is moderate, combined with her pre-existing 'Mild Knee Discomfort'.

    *   Fatigue (5/10) is also moderate.

    *   Sleep quality is good, which is a positive sign.

    The monitor assesses this as 'Yellow: Moderate Risk'.


It then provides recommendations: "CAUTION: Reduce intensity and/or duration of upcoming runs by 20% for the next few sessions. Prioritize recovery: ensure adequate sleep, nutrition, and hydration. Incorporate more stretching and foam rolling, specifically targeting quads and hamstrings to support knee stability."

    The Training Plan Generation Engine receives these adjustments. For Week 2, instead of increasing the run duration as originally planned, it might keep the duration similar to Week 1 or even slightly reduce it, focusing on maintaining the run-walk intervals at an easier pace. It might also explicitly add a foam rolling session to her schedule.

This iterative process of planning, executing, monitoring, and adapting ensures that Sarah's training plan is always optimized for her current condition, helping her progress safely and effectively towards her 5-kilometer goal.


Full Running Code Example


The following is a complete, runnable Python script combining all the classes and functions discussed, demonstrating how they interact to generate a personalized running plan for a user.


import datetime


# --- UserProfile Class ---

class UserProfile:

    """

    Represents a comprehensive profile of a user for personalized running plan generation.


    Attributes:

        user_id (str): A unique identifier for the user.

        age (int): The user's age in years.

        gender (str): The user's gender ('Male', 'Female', 'Other').

        weight_kg (float): The user's weight in kilograms.

        height_cm (float): The user's height in centimeters.

        fitness_level (str): A qualitative assessment of the user's current fitness

                             ('Beginner', 'Intermediate', 'Advanced').

        training_goal (str): The primary objective of the training plan

                             ('Run 5K', 'Run 10K', 'Marathon', 'General Endurance').

        recent_training_history (str): Describes recent exercise activity

                                       ('Sedentary', 'Light Activity', 'Regular Runner').

        health_issues (list): A list of strings detailing any relevant health conditions

                              (e.g., ['Asthma', 'Knee Pain']).

        handicaps (list): A list of strings detailing any physical handicaps

                          (e.g., ['Right Ankle Sprain History']).

        max_heart_rate (int): User's estimated or measured maximum heart rate.

        resting_heart_rate (int): User's measured resting heart rate.

        vo2_max (float): User's estimated or measured VO2 max, if available.

        preferred_running_days (list): A list of strings for preferred training days

                                      (e.g., ['Monday', 'Wednesday', 'Friday']).

        available_time_per_session_minutes (int): Average minutes available for each training session.

    """


    def __init__(self, user_id, age, gender, weight_kg, height_cm, fitness_level,

                 training_goal, recent_training_history, health_issues=None,

                 handicaps=None, max_heart_rate=None, resting_heart_rate=None,

                 vo2_max=None, preferred_running_days=None,

                 available_time_per_session_minutes=60):

        """

        Initializes a new UserProfile instance.


        Args:

            user_id (str): Unique identifier for the user.

            age (int): User's age in years.

            gender (str): User's gender.

            weight_kg (float): User's weight in kilograms.

            height_cm (float): User's height in centimeters.

            fitness_level (str): User's current fitness level.

            training_goal (str): User's primary training objective.

            recent_training_history (str): Description of recent exercise activity.

            health_issues (list, optional): List of health conditions. Defaults to an empty list.

            handicaps (list, optional): List of physical handicaps. Defaults to an empty list.

            max_heart_rate (int, optional): Maximum heart rate. Defaults to None.

            resting_heart_rate (int, optional): Resting heart rate. Defaults to None.

            vo2_max (float, optional): VO2 max value. Defaults to None.

            preferred_running_days (list, optional): Preferred training days. Defaults to an empty list.

            available_time_per_session_minutes (int, optional): Time available per session. Defaults to 60.

        """

        self.user_id = user_id

        self.age = age

        self.gender = gender

        self.weight_kg = weight_kg

        self.height_cm = height_cm

        self.fitness_level = fitness_level

        self.training_goal = training_goal

        self.recent_training_history = recent_training_history

        self.health_issues = health_issues if health_issues is not None else []

        self.handicaps = handicaps if handicaps is not None else []

        self.max_heart_rate = max_heart_rate

        self.resting_heart_rate = resting_heart_rate

        self.vo2_max = vo2_max

        self.preferred_running_days = preferred_running_days if preferred_running_days is not None else []

        self.available_time_per_session_minutes = available_time_per_session_minutes


    def display_profile(self):

        """Prints the user's profile information."""

        print(f"--- User Profile for {self.user_id} ---")

        print(f"Age: {self.age} years")

        print(f"Gender: {self.gender}")

        print(f"Weight: {self.weight_kg} kg")

        print(f"Height: {self.height_cm} cm")

        print(f"Fitness Level: {self.fitness_level}")

        print(f"Training Goal: {self.training_goal}")

        print(f"Recent Training History: {self.recent_training_history}")

        print(f"Health Issues: {', '.join(self.health_issues) if self.health_issues else 'None'}")

        print(f"Handicaps: {', '.join(self.handicaps) if self.handicaps else 'None'}")

        print(f"Max Heart Rate: {self.max_heart_rate if self.max_heart_rate else 'N/A'}")

        print(f"Resting Heart Rate: {self.resting_heart_rate if self.resting_heart_rate else 'N/A'}")

        print(f"VO2 Max: {self.vo2_max if self.vo2_max else 'N/A'}")

        print(f"Preferred Running Days: {', '.join(self.preferred_running_days) if self.preferred_running_days else 'Any'}")

        print(f"Available Time per Session: {self.available_time_per_session_minutes} minutes")

        print("------------------------------------")


# --- ExerciseDatabase Class and select_exercises function ---

class ExerciseDatabase:

    """

    Manages a database of various exercises relevant for runners.

    """


    def __init__(self):

        """Initializes the ExerciseDatabase with predefined exercises."""

        self.running_workouts = {

            "Easy Run": "Steady, conversational pace run to build aerobic base. Focus on duration, not speed.",

            "Tempo Run": "Run at a comfortably hard pace, where you can speak in short sentences. Improves lactate threshold.",

            "Intervals": "Short bursts of high-intensity running followed by recovery periods. Improves speed and VO2 max.",

            "Long Run": "Extended duration run at an easy pace to build endurance and mental toughness.",

            "Fartlek": "Unstructured speed play, varying pace and intensity during a continuous run.",

            "Hill Repeats": "Running uphill at high effort, then jogging or walking down for recovery. Builds leg strength and power."

        }

        self.stretching_exercises = {

            "Quad Stretch": "Targets the quadriceps muscles. Stand and pull one heel towards your glutes.",

            "Hamstring Stretch": "Targets the hamstrings. Sit with one leg extended, reach for your toes.",

            "Calf Stretch (Gastrocnemius)": "Targets the upper calf. Lean against a wall with one leg back, heel down.",

            "Calf Stretch (Soleus)": "Targets the lower calf. Same as above, but bend the back knee.",

            "Hip Flexor Stretch": "Targets the hip flexors. Kneeling lunge position, push hips forward.",

            "Glute Stretch (Figure-Four)": "Targets the glutes. Lie on your back, cross one ankle over the opposite knee.",

            "IT Band Stretch": "Targets the IT band. Cross one leg behind the other, lean away from the front leg.",

            "Dynamic Leg Swings": "Warm-up stretch for hips and hamstrings. Swing legs forward-back and side-to-side."

        }

        self.strength_exercises = {

            "Bodyweight Squats": "Strengthens quads, hamstrings, glutes. Stand shoulder-width apart, lower hips as if sitting.",

            "Lunges": "Strengthens quads, hamstrings, glutes. Step forward with one leg, lower hips until both knees are bent at 90 degrees.",

            "Plank": "Strengthens core. Hold a push-up position on forearms.",

            "Glute Bridges": "Strengthens glutes and hamstrings. Lie on back, knees bent, lift hips off the ground.",

            "Calf Raises": "Strengthens calves. Stand on toes, then lower.",

            "Single-Leg Deadlift": "Improves balance and strengthens hamstrings/glutes. Hinge at hips, extend one leg back.",

            "Side Plank": "Strengthens obliques and core stability. Hold a plank position on one forearm, body in a straight line."

        }

        self.recovery_exercises = {

            "Foam Rolling (Quads)": "Self-massage for quads.",

            "Foam Rolling (Hamstrings)": "Self-massage for hamstrings.",

            "Foam Rolling (Calves)": "Self-massage for calves.",

            "Foam Rolling (IT Band)": "Self-massage for IT band.",

            "Yoga/Pilates (Runner-focused)": "Improves flexibility, core strength, and body awareness."

        }


    def get_exercise_description(self, exercise_type, exercise_name):

        """

        Retrieves the description for a specific exercise.


        Args:

            exercise_type (str): The category of the exercise (e.g., 'running_workouts').

            exercise_name (str): The name of the exercise.


        Returns:

            str: The description of the exercise, or an error message if not found.

        """

        if exercise_type == 'running_workouts' and exercise_name in self.running_workouts:

            return self.running_workouts[exercise_name]

        elif exercise_type == 'stretching_exercises' and exercise_name in self.stretching_exercises:

            return self.stretching_exercises[exercise_name]

        elif exercise_type == 'strength_exercises' and exercise_name in self.strength_exercises:

            return self.strength_exercises[exercise_name]

        elif exercise_type == 'recovery_exercises' and exercise_name in self.recovery_exercises:

            return self.recovery_exercises[exercise_name]

        return f"Exercise '{exercise_name}' not found in '{exercise_type}'."


def select_exercises(user_profile, exercise_db, exercise_category, num_exercises=3, focus_areas=None):

    """

    Selects a list of exercises based on user profile and category.


    Args:

        user_profile (UserProfile): The user's profile.

        exercise_db (ExerciseDatabase): An instance of the ExerciseDatabase.

        exercise_category (str): The category of exercises to select from

                                 ('stretching_exercises', 'strength_exercises', 'recovery_exercises').

        num_exercises (int): The maximum number of exercises to select.

        focus_areas (list, optional): Specific areas to prioritize for selection (e.g., ['knees', 'hamstrings']).


    Returns:

        list: A list of selected exercise names.

    """

    available_exercises = []

    if exercise_category == 'stretching_exercises':

        available_exercises = list(exercise_db.stretching_exercises.keys())

    elif exercise_category == 'strength_exercises':

        available_exercises = list(exercise_db.strength_exercises.keys())

    elif exercise_category == 'recovery_exercises':

        available_exercises = list(exercise_db.recovery_exercises.keys())

    else:

        return [] # Invalid category


    selected = []

    

    # Prioritize exercises based on health issues/handicaps if focus_areas are not provided

    # or if specific focus areas are provided

    prioritized_exercises = []

    if focus_areas:

        for area in focus_areas:

            # Simple keyword matching for demonstration

            for ex in available_exercises:

                if area.lower() in ex.lower() or any(area.lower() in desc.lower() for desc in [exercise_db.get_exercise_description(exercise_category, ex)]):

                    if ex not in prioritized_exercises:

                        prioritized_exercises.append(ex)

    

    # Add exercises based on general health issues from user profile

    for issue in user_profile.health_issues + user_profile.handicaps:

        if 'knee' in issue.lower() or 'patella' in issue.lower():

            for ex in ['Bodyweight Squats', 'Lunges', 'Glute Bridges', 'IT Band Stretch', 'Quad Stretch']:

                if ex in available_exercises and ex not in prioritized_exercises:

                    prioritized_exercises.append(ex)

        if 'back' in issue.lower() or 'spine' in issue.lower():

            for ex in ['Plank', 'Side Plank', 'Glute Bridges', 'Yoga/Pilates (Runner-focused)']:

                if ex in available_exercises and ex not in prioritized_exercises:

                    prioritized_exercises.append(ex)

        if 'hamstring' in issue.lower():

            for ex in ['Hamstring Stretch', 'Single-Leg Deadlift', 'Foam Rolling (Hamstrings)']:

                if ex in available_exercises and ex not in prioritized_exercises:

                    prioritized_exercises.append(ex)

        # Add more mappings for other issues as needed


    # Combine prioritized with general exercises, ensuring uniqueness

    selected_candidates = prioritized_exercises + [ex for ex in available_exercises if ex not in prioritized_exercises]

    

    # Select up to num_exercises

    for ex in selected_candidates:

        if len(selected) < num_exercises:

            selected.append(ex)

        else:

            break

            

    return selected


# --- TrainingPlanGenerator Class ---

class TrainingPlanGenerator:

    """

    Generates a personalized running training plan based on a user's profile.

    """


    def __init__(self, user_profile, exercise_db):

        """

        Initializes the TrainingPlanGenerator with a UserProfile and ExerciseDatabase.


        Args:

            user_profile (UserProfile): An instance of the UserProfile class.

            exercise_db (ExerciseDatabase): An instance of the ExerciseDatabase class.

        """

        self.profile = user_profile

        self.exercise_db = exercise_db

        self.plan = []  # Stores the generated weekly plan


    def _calculate_base_intensity(self):

        """

        Calculates a base intensity level based on age and fitness.

        This is a simplified example; real-world would use more complex formulas.

        Returns:

            float: A multiplier for intensity (e.g., 0.6 for beginner, 0.8 for advanced).

        """

        base_intensity = 0.6  # Default for beginner

        if self.profile.fitness_level == 'Intermediate':

            base_intensity = 0.75

        elif self.profile.fitness_level == 'Advanced':

            base_intensity = 0.9

        

        # Adjust slightly for age (older might need slightly lower intensity initially)

        if self.profile.age > 50:

            base_intensity *= 0.95

        elif self.profile.age < 25:

            base_intensity *= 1.05

        

        return base_intensity


    def _generate_warm_up(self):

        """Generates a standard warm-up routine."""

        return "5-10 minutes brisk walking or light jogging."


    def _generate_cool_down(self):

        """Generates a standard cool-down routine."""

        return "5-10 minutes walking, followed by stretching."


    def _generate_stretching_routine(self, focus_areas=None):

        """

        Generates a stretching routine, optionally focusing on specific areas.

        Args:

            focus_areas (list, optional): Specific muscle groups to focus on (e.g., ['hamstrings']).

        Returns:

            str: Description of the stretching routine.

        """

        selected_stretches = select_exercises(self.profile, self.exercise_db, 'stretching_exercises', num_exercises=4, focus_areas=focus_areas)

        if selected_stretches:

            return f"Post-run stretching: {', '.join(selected_stretches)}."

        return "General post-run stretching (Quad Stretch, Hamstring Stretch, Calf Stretch)."


    def _create_running_workout(self, week_num, workout_type, duration_minutes, intensity_level, distance_km=None, notes=""):

        """

        Creates a single running workout description.


        Args:

            week_num (int): The current week number of the plan.

            workout_type (str): Type of run (e.g., 'Easy Run', 'Intervals', 'Long Run').

            duration_minutes (int): Expected duration of the workout in minutes.

            intensity_level (float): A multiplier for intensity, derived from base_intensity.

            distance_km (float, optional): Target distance in kilometers.

            notes (str, optional): Additional notes for the workout.


        Returns:

            dict: A dictionary describing the workout.

        """

        workout = {

            "week": week_num,

            "type": workout_type,

            "duration_minutes": duration_minutes,

            "intensity_level": f"{int(intensity_level * 100)}% effort",

            "warm_up": self._generate_warm_up(),

            "cool_down": self._generate_cool_down(),

            "stretching": self._generate_stretching_routine(),

            "notes": notes

        }

        if distance_km:

            workout["distance_km"] = distance_km


        # Adjust intensity/duration based on health issues or handicaps

        # This is a general adjustment, more specific ones can come from HealthMonitor

        if any(issue in self.profile.health_issues for issue in ['Knee Pain', 'Ankle Injury']):

            if "Reduce intensity" not in workout["notes"]: # Avoid duplicate notes

                workout["notes"] += " Consider low-impact alternatives or reduced intensity due to joint concerns."

            workout["intensity_level"] = f"{int(intensity_level * 0.8 * 100)}% effort" # Reduce intensity

            workout["duration_minutes"] = int(duration_minutes * 0.8) # Reduce duration

        elif 'Asthma' in self.profile.health_issues:

            if "Monitor breathing" not in workout["notes"]:

                workout["notes"] += " Ensure inhaler is accessible. Monitor breathing carefully."


        return workout


    def generate_plan(self, num_weeks=12):

        """

        Generates a full training plan for the specified number of weeks.

        The plan includes running workouts, cross-training, and rest days,

        progressing based on the user's profile.


        Args:

            num_weeks (int): The total number of weeks for the training plan.

        """

        self.plan = []

        base_intensity = self._calculate_base_intensity()


        for week in range(1, num_weeks + 1):

            weekly_workouts = []

            

            # Determine weekly volume and intensity progression

            # This is a simplified linear progression for demonstration

            # Real-world would use more sophisticated models (e.g., 10% rule, periodization)

            progression_factor = 1 + (week - 1) * 0.05 # 5% increase per week

            

            # Adjust progression based on recent training history

            if self.profile.recent_training_history == 'Sedentary':

                progression_factor = 1 + (week - 1) * 0.03 # Slower progression

                if week == 1: # First week for sedentary users might be mostly walking

                    weekly_workouts.append(self._create_running_workout(

                        week, "Walk/Jog", 30, base_intensity * 0.5, distance_km=2.0,

                        notes="Focus on comfortable pace, mostly walking with short jog intervals."))

                    weekly_workouts.append(self._create_running_workout(

                        week, "Walk/Jog", 35, base_intensity * 0.5, distance_km=2.5,

                        notes="Gradually increase jog duration if comfortable."))

                    weekly_workouts.append(self._create_running_workout(

                        week, "Cross-Training (e.g., cycling/swimming)", 45, base_intensity * 0.6,

                        notes="Low-impact activity to build aerobic fitness without stressing joints."))

                    self.plan.append(weekly_workouts)

                    continue # Skip to next week if first week is special


            # Define typical workouts for a week

            # Day 1: Easy Run / Base Building

            easy_run_duration = int(40 * progression_factor)

            easy_run_distance = round(5.0 * progression_factor, 1)

            weekly_workouts.append(self._create_running_workout(

                week, "Easy Run", easy_run_duration, base_intensity * 0.8, distance_km=easy_run_distance))


            # Day 2: Speed Work / Tempo Run (Introduced later for beginners)

            if week > 3 or self.profile.fitness_level in ['Intermediate', 'Advanced']:

                speed_duration = int(30 * progression_factor)

                weekly_workouts.append(self._create_running_workout(

                    week, "Intervals/Tempo Run", speed_duration, base_intensity * 1.1, distance_km=None,

                    notes="Include short bursts of faster running or sustained 'comfortably hard' pace."))

            else:

                # For beginners, more easy runs or cross-training

                weekly_workouts.append(self._create_running_workout(

                    week, "Easy Run (shorter)", int(easy_run_duration * 0.7), base_intensity * 0.7,

                    distance_km=round(easy_run_distance * 0.7, 1),

                    notes="Focus on building consistent running time at an easy pace."))


            # Day 3: Long Run (Key for endurance)

            long_run_duration = int(60 * progression_factor)

            long_run_distance = round(8.0 * progression_factor, 1)

            weekly_workouts.append(self._create_running_workout(

                week, "Long Run", long_run_duration, base_intensity * 0.7, distance_km=long_run_distance,

                notes="Maintain a conversational pace throughout. Hydrate well before and after."))


            # Add cross-training or rest days based on preferred days/availability

            # This logic ensures at least one recovery/cross-training day

            if len(self.profile.preferred_running_days) < 3: # If user prefers fewer running days, suggest cross-training

                weekly_workouts.append({

                    "week": week,

                    "type": "Cross-Training (e.g., cycling, swimming)",

                    "duration_minutes": int(45 * progression_factor),

                    "intensity_level": f"{int(base_intensity * 0.6 * 100)}% effort",

                    "notes": "Low-impact activity to build aerobic fitness and aid recovery. Also include strength exercises."

                })

            else: # If user has enough running days, ensure a dedicated rest day

                weekly_workouts.append({

                    "week": week,

                    "type": "Rest Day or Active Recovery (light walk)",

                    "duration_minutes": 0,

                    "intensity_level": "N/A",

                    "notes": "Crucial for recovery and preventing overtraining. Focus on sleep and nutrition."

                })


            self.plan.append(weekly_workouts)


        # Final adjustments for specific goals (e.g., taper for 5K)

        if self.profile.training_goal == 'Run 5K' and num_weeks >= 2:

            # Tapering for the last week before a 5K race

            taper_week_index = num_weeks - 1

            if taper_week_index < len(self.plan): # Ensure index is valid

                taper_week = self.plan[taper_week_index]

                for workout in taper_week:

                    if "duration_minutes" in workout and workout["duration_minutes"] > 0:

                        workout["duration_minutes"] = int(workout["duration_minutes"] * 0.5) # Halve duration

                    if "distance_km" in workout and workout["distance_km"] > 0:

                        workout["distance_km"] = round(workout["distance_km"] * 0.5, 1) # Halve distance

                    workout["notes"] = workout.get("notes", "") + " (Taper week: reduce volume, maintain intensity for short bursts. Focus on rest)."

            # Add a specific race day entry

            self.plan.append([{

                "week": num_weeks + 1,

                "type": "Race Day: 5K Event",

                "duration_minutes": None, # Duration is race dependent

                "intensity_level": "Max Effort",

                "warm_up": self._generate_warm_up(),

                "cool_down": self._generate_cool_down(),

                "stretching": self._generate_stretching_routine(),

                "notes": "Focus on hydration and nutrition. Get good sleep. Enjoy your race!"

            }])



    def display_plan(self):

        """Prints the generated training plan in a readable format."""

        print(f"\n--- Training Plan for {self.profile.user_id} (Goal: {self.profile.training_goal}) ---")

        for i, week_workouts in enumerate(self.plan):

            print(f"\nWeek {i+1}:")

            for workout in week_workouts:

                print(f"  - Type: {workout['type']}")

                if workout.get('duration_minutes') is not None:

                    print(f"    Duration: {workout['duration_minutes']} minutes")

                if workout.get('distance_km') is not None:

                    print(f"    Distance: {workout['distance_km']} km")

                print(f"    Intensity: {workout['intensity_level']}")

                if workout.get('warm_up'):

                    print(f"    Warm-up: {workout['warm_up']}")

                if workout.get('cool_down'):

                    print(f"    Cool-down: {workout['cool_down']}")

                if workout.get('stretching'):

                    print(f"    Stretching: {workout['stretching']}")

                if workout.get('notes'):

                    print(f"    Notes: {workout['notes']}")

        print("--------------------------------------------------")


# --- HealthMonitor Class ---

class HealthMonitor:

    """

    Monitors user health and provides recommendations for training plan adjustments.

    """


    def __init__(self, user_profile):

        """

        Initializes the HealthMonitor with a UserProfile.


        Args:

            user_profile (UserProfile): An instance of the UserProfile class.

        """

        self.profile = user_profile

        self.injury_risk_threshold = 7 # On a scale of 1-10, 10 being highest risk


    def assess_health_feedback(self, reported_pain_level, fatigue_level, sleep_quality_score, recent_performance_trend='stable'):

        """

        Assesses user's self-reported health feedback to determine potential risks.


        Args:

            reported_pain_level (int): User's reported pain level (0-10, 0=none, 10=severe).

            fatigue_level (int): User's reported fatigue level (0-10, 0=rested, 10=exhausted).

            sleep_quality_score (int): User's reported sleep quality (0-10, 0=poor, 10=excellent).

            recent_performance_trend (str): Trend in recent performance ('improving', 'stable', 'declining').


        Returns:

            dict: A dictionary containing risk assessment and recommendations.

        """

        risk_score = 0

        recommendations = []


        # --- Rule-based risk assessment ---


        # Pain assessment

        if reported_pain_level > 5:

            risk_score += (reported_pain_level - 5) * 2 # Higher pain, higher risk

            recommendations.append(f"Significant pain reported ({reported_pain_level}/10). Consider rest or medical consultation.")

            if reported_pain_level > 7:

                recommendations.append("Strongly recommend stopping current activity and seeking professional advice.")

        elif reported_pain_level > 0:

            risk_score += 1

            recommendations.append(f"Minor pain reported ({reported_pain_level}/10). Monitor closely, consider active recovery.")


        # Fatigue assessment

        if fatigue_level > 6:

            risk_score += (fatigue_level - 6) * 1.5

            recommendations.append(f"High fatigue reported ({fatigue_level}/10). Prioritize rest and reduce training volume.")

        elif fatigue_level > 4:

            risk_score += 1

            recommendations.append(f"Moderate fatigue reported ({fatigue_level}/10). Ensure adequate recovery.")


        # Sleep quality assessment

        if sleep_quality_score < 4:

            risk_score += (4 - sleep_quality_score) * 2

            recommendations.append(f"Poor sleep quality reported ({sleep_quality_score}/10). Focus on sleep hygiene.")

        elif sleep_quality_score < 6:

            risk_score += 1

            recommendations.append(f"Suboptimal sleep quality reported ({sleep_quality_score}/10). Aim for more consistent sleep.")


        # Performance trend assessment

        if recent_performance_trend == 'declining':

            risk_score += 3

            recommendations.append("Recent performance decline detected. Could indicate overtraining or insufficient recovery.")

        elif recent_performance_trend == 'improving' and (reported_pain_level > 0 or fatigue_level > 5):

            recommendations.append("Performance improving but with pain/fatigue. Be careful not to push too hard too fast.")


        # Health issues from profile

        if any(issue in self.profile.health_issues for issue in ['Knee Discomfort', 'Ankle Injury', 'Back Pain']):

            if reported_pain_level > 3: # If pre-existing issue is flaring up

                risk_score += 2

                recommendations.append(f"Pre-existing condition '{self.profile.health_issues[0]}' may be aggravated. Adjust training.")

        if 'Asthma' in self.profile.health_issues and reported_pain_level > 0: # Simplified check

            recommendations.append("Monitor respiratory symptoms closely during exercise.")


        # --- Determine overall status ---

        overall_status = "Green: All good!"

        if risk_score >= self.injury_risk_threshold:

            overall_status = "Red: High Risk! Immediate action recommended."

        elif risk_score >= self.injury_risk_threshold / 2:

            overall_status = "Yellow: Moderate Risk. Caution advised."


        return {

            "risk_score": risk_score,

            "overall_status": overall_status,

            "recommendations": recommendations if recommendations else ["No specific recommendations at this time. Keep up the good work!"]

        }


    def provide_plan_adjustment(self, health_assessment_result):

        """

        Provides concrete plan adjustment suggestions based on health assessment.


        Args:

            health_assessment_result (dict): The output from assess_health_feedback.


        Returns:

            list: A list of suggested adjustments for the training plan.

        """

        adjustments = []

        status = health_assessment_result['overall_status']

        risk_score = health_assessment_result['risk_score']


        if "Red" in status:

            adjustments.append("IMMEDIATE ACTION: Consider a complete rest day or consult a medical professional.")

            adjustments.append("Reduce all planned running volume by at least 50% for the next 3-5 days.")

            adjustments.append("Focus on gentle active recovery (e.g., walking, stretching) if pain allows.")

        elif "Yellow" in status:

            adjustments.append("CAUTION: Reduce intensity and/or duration of upcoming runs by 20-30%.")

            adjustments.append("Prioritize recovery: ensure adequate sleep, nutrition, and hydration.")

            adjustments.append("Incorporate more stretching and foam rolling.")

            adjustments.append("Consider swapping a running day for cross-training (e.g., swimming, cycling) to reduce impact.")

        else:

            adjustments.append("Continue with the planned training. Maintain good recovery practices.")


        # Add specific adjustments based on recommendations

        for rec in health_assessment_result['recommendations']:

            if "reduce training volume" in rec.lower() and "Yellow" not in status and "Red" not in status:

                adjustments.append("Consider a slight reduction in volume for the next session.")

            if "monitor breathing" in rec.lower():

                adjustments.append("Pay extra attention to breathing and symptoms during exercise.")

        

        # Remove duplicates

        return list(set(adjustments))


# --- RoutePlanner Class ---

class RoutePlanner:

    """

    Manages and suggests running routes based on user's location and preferences.

    """


    def __init__(self):

        """Initializes the RoutePlanner with a collection of conceptual routes."""

        self.available_routes = [

            {

                "id": "R001",

                "name": "Park Loop",

                "description": "A flat, scenic loop through a local park, ideal for easy runs.",

                "start_location": "Park Entrance (Lat: 34.0522, Lon: -118.2437)",

                "destination_location": "Park Entrance (Lat: 34.0522, Lon: -118.2437)",

                "intermediate_points": ["Gazebo (Lat: 34.0530, Lon: -118.2420)", "Lake View (Lat: 34.0510, Lon: -118.2450)"],

                "distance_km": 5.0,

                "elevation_gain_m": 10,

                "surface_type": "Paved",

                "safety_notes": "Well-lit, popular area, suitable for all times.",

                "location_tags": ["urban", "park", "flat"]

            },

            {

                "id": "R002",

                "name": "River Trail Out-and-Back",

                "description": "A gentle trail along the river, good for longer, steady runs.",

                "start_location": "Riverfront Parking (Lat: 34.0600, Lon: -118.2500)",

                "destination_location": "Bridge Viewpoint (Lat: 34.0700, Lon: -118.2600)",

                "intermediate_points": ["Old Mill (Lat: 34.0650, Lon: -118.2550)"],

                "distance_km": 8.0,

                "elevation_gain_m": 50,

                "surface_type": "Gravel/Dirt",

                "safety_notes": "Less lit at night, bring headlamp. Moderate traffic.",

                "location_tags": ["suburban", "trail", "gentle hills"]

            },

            {

                "id": "R003",

                "name": "Hill Challenge Loop",

                "description": "A challenging route with significant climbs, great for hill training.",

                "start_location": "Mountain Base Parking (Lat: 34.0800, Lon: -118.2800)",

                "destination_location": "Mountain Base Parking (Lat: 34.0800, Lon: -118.2800)",

                "intermediate_points": ["Summit Viewpoint (Lat: 34.0850, Lon: -118.2850)", "Forest Path (Lat: 34.0780, Lon: -118.2750)"],

                "distance_km": 10.0,

                "elevation_gain_m": 300,

                "surface_type": "Trail/Uneven",

                "safety_notes": "Remote, carry water and phone. Not recommended for beginners.",

                "location_tags": ["mountain", "trail", "steep hills"]

            }

        ]


    def suggest_routes(self, user_profile, preferred_distance_km=None, max_elevation_gain_m=None,

                       preferred_surface_type=None, current_location_lat_lon=None):

        """

        Suggests suitable running routes based on user preferences and (conceptual) location.


        Args:

            user_profile (UserProfile): The user's profile.

            preferred_distance_km (float, optional): Desired route distance in kilometers.

            max_elevation_gain_m (int, optional): Maximum acceptable elevation gain in meters.

            preferred_surface_type (str, optional): Desired surface type ('Paved', 'Gravel/Dirt', 'Trail/Uneven').

            current_location_lat_lon (tuple, optional): User's current (latitude, longitude) for proximity.

                                                        (Conceptual for this example).


        Returns:

            list: A list of dictionaries, each representing a suggested route.

        """

        suggested = []

        

        # In a real system, current_location_lat_lon would be used to filter

        # routes geographically. For this example, we'll simulate filtering.

        

        for route in self.available_routes:

            match = True


            # Filter by distance

            if preferred_distance_km is not None:

                # Allow a small tolerance (e.g., +/- 20%)

                if not (preferred_distance_km * 0.8 <= route['distance_km'] <= preferred_distance_km * 1.2):

                    match = False

            

            # Filter by elevation gain

            if max_elevation_gain_m is not None:

                if route['elevation_gain_m'] > max_elevation_gain_m:

                    match = False


            # Filter by surface type

            if preferred_surface_type is not None:

                if route['surface_type'].lower() != preferred_surface_type.lower():

                    match = False

            

            # Filter by fitness level (simple rule)

            if user_profile.fitness_level == 'Beginner' and route['elevation_gain_m'] > 100:

                match = False # Beginners generally shouldn't start with very hilly routes

            if user_profile.fitness_level == 'Beginner' and "Trail/Uneven" in route['surface_type']:

                match = False # Beginners should avoid highly technical trails initially


            if match:

                suggested.append(route)

        

        return suggested


    def display_route(self, route):

        """

        Prints the details of a single route.


        Args:

            route (dict): A dictionary representing a route.

        """

        print(f"--- Route: {route['name']} (ID: {route['id']}) ---")

        print(f"  Description: {route['description']}")

        print(f"  Start: {route['start_location']}")

        print(f"  Destination: {route['destination_location']}")

        print(f"  Intermediate Points: {', '.join(route['intermediate_points'])}")

        print(f"  Distance: {route['distance_km']} km")

        print(f"  Elevation Gain: {route['elevation_gain_m']} m")

        print(f"  Surface Type: {route['surface_type']}")

        print(f"  Safety Notes: {route['safety_notes']}")

        print("------------------------------------------")



# --- Main execution flow for the running example ---

if __name__ == "__main__":

    print("--- Initializing AI Running Coach ---")


    # 1. Create User Profile (Sarah's example)

    sarah_profile = UserProfile(

        user_id="Sarah_Runner_001",

        age=30,

        gender="Female",

        weight_kg=65.0,

        height_cm=165.0,

        fitness_level="Beginner",

        training_goal="Run 5K",

        recent_training_history="Sedentary",

        health_issues=["Mild Knee Discomfort"],

        handicaps=[],

        preferred_running_days=["Tuesday", "Thursday", "Saturday"],

        available_time_per_session_minutes=45

    )

    sarah_profile.display_profile()


    # Initialize Exercise Database and Training Plan Generator

    exercise_db = ExerciseDatabase()

    plan_generator = TrainingPlanGenerator(sarah_profile, exercise_db)


    # 2. Generate Initial Training Plan for 12 weeks

    print("\n--- Generating Initial 12-Week Training Plan ---")

    plan_generator.generate_plan(num_weeks=12)

    plan_generator.display_plan()


    # 3. Simulate a workout and get health feedback (e.g., after Week 1, Day 3)

    print("\n--- Simulating Health Feedback after Week 1 ---")

    health_monitor = HealthMonitor(sarah_profile)

    reported_pain_level = 3  # Mild knee discomfort

    fatigue_level = 5    # Moderate fatigue

    sleep_quality_score = 7 # Good sleep

    recent_performance_trend = 'stable'


    health_assessment = health_monitor.assess_health_feedback(

        reported_pain_level, fatigue_level, sleep_quality_score, recent_performance_trend

    )

    print(f"Health Assessment Status: {health_assessment['overall_status']}")

    print("Recommendations:")

    for rec in health_assessment['recommendations']:

        print(f"- {rec}")

    

    plan_adjustments = health_monitor.provide_plan_adjustment(health_assessment)

    print("\nSuggested Plan Adjustments:")

    for adj in plan_adjustments:

        print(f"- {adj}")


    # 4. (Conceptual) Adjust plan based on feedback.

    # In a real system, the plan_generator would receive these adjustments and regenerate/modify

    # the plan for subsequent weeks. For this example, we'll show a manual conceptual adjustment.

    print("\n--- Conceptual Plan Adjustment for Week 2 based on feedback ---")

    print("The AI agent would now modify Week 2's plan to reduce intensity/duration and add specific recovery exercises.")

    # Example modification: for Week 2, reduce the duration of runs by 20%

    if len(plan_generator.plan) > 1: # Ensure Week 2 exists

        for workout in plan_generator.plan[1]: # Access Week 2 workouts

            if "duration_minutes" in workout and workout["duration_minutes"] is not None:

                original_duration = workout["duration_minutes"]

                workout["duration_minutes"] = int(original_duration * 0.8)

                workout["notes"] = workout.get("notes", "") + " (Adjusted due to knee discomfort and fatigue)."

            if "stretching" in workout:

                 # Add specific recovery exercises based on feedback

                focused_stretches = select_exercises(sarah_profile, exercise_db, 'stretching_exercises', num_exercises=2, focus_areas=['knee', 'hamstring'])

                workout["stretching"] = f"Focused stretching: {', '.join(focused_stretches)}. " + workout["stretching"]

            if "Cross-Training" in workout["type"]:

                strength_exercises = select_exercises(sarah_profile, exercise_db, 'strength_exercises', num_exercises=2, focus_areas=['glutes', 'core'])

                workout["notes"] += f" Also include strength exercises: {', '.join(strength_exercises)}."


    print("\n--- Displaying Adjusted Plan for Week 2 (and subsequent weeks) ---")

    plan_generator.display_plan()



    # 5. Suggest a Route for an upcoming run (e.g., Week 2, Day 1)

    print("\n--- Suggesting a Route for Week 2, Day 1 ---")

    route_planner = RoutePlanner()

    

    # Assuming Week 2, Day 1 is an Easy Run, say 4km (adjusted from 5km * 0.8)

    # and Sarah's conceptual location is near the "Park Loop"

    suggested_routes = route_planner.suggest_routes(

        user_profile=sarah_profile,

        preferred_distance_km=4.0, # Target distance for the adjusted run

        max_elevation_gain_m=50,

        preferred_surface_type="Paved",

        current_location_lat_lon=(34.0550, -118.2440) # Near Park Loop

    )


    if suggested_routes:

        print("Found the following suitable routes:")

        for route in suggested_routes:

            route_planner.display_route(route)

    else:

        print("No suitable routes found based on current criteria.")


    print("\n--- AI Running Coach process complete ---")




References


  1. https://peakrunner.ai/
  2. https://aiendurance.com/
  3. https://www.runningmates.ai/
  4. https://athletica.ai/
  5. https://trainasone.com/





No comments: