AI-Driven Personalized Travel Itinerary Planner Based on User Preferences Swift

👤 Sharing: AI
```swift
import Foundation

// MARK: - Data Structures

/// Represents a travel destination with relevant information.
struct Destination: Identifiable, Hashable {
    let id = UUID()
    let name: String
    let description: String
    let latitude: Double
    let longitude: Double
    let category: String // e.g., "Historical", "Nature", "Food"
    let averageCost: Int // In some arbitrary currency unit per day
    let popularity: Int // Scale of 1-10, higher means more popular
    let imageUrl: String // URL of an image representing the destination
    let activities: [Activity]

    static func == (lhs: Destination, rhs: Destination) -> Bool {
        return lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

/// Represents an activity you can do at a destination.
struct Activity: Identifiable {
    let id = UUID()
    let name: String
    let description: String
    let cost: Int // Cost of the activity
    let duration: Int // Duration in minutes
    let category: String // e.g., "Hiking", "Museum Visit", "Restaurant"
}


/// Represents a user's travel preferences.
struct UserPreferences {
    var budget: Int? // Overall budget for the trip
    var duration: Int? // Duration of the trip in days
    var preferredCategories: [String] // e.g., ["Historical", "Food"]
    var preferredActivities: [String] // e.g. ["Museum Visit", "Hiking"]
    var dislikedCategories: [String] // categories to avoid
    var dislikedActivities: [String] // activities to avoid
    var preferredPace: String // "Relaxed", "Moderate", "Fast-paced"
    var mustVisit: [Destination] // Destinations that the user must visit, if possible
}

/// Represents a day in the itinerary.
struct ItineraryDay {
    let dayNumber: Int
    var activities: [Activity]
    var totalCost: Int
}

/// Represents the overall itinerary.
struct Itinerary {
    var days: [ItineraryDay]
    var totalCost: Int {
        days.reduce(0) { $0 + $1.totalCost }
    }
}

// MARK: - Mock Data (Replace with a database or API)

// Some example destinations.  In a real app, this would come from a database or API.
let mockDestinations: [Destination] = [
    Destination(
        name: "Rome, Italy",
        description: "The Eternal City, rich in history and culture.",
        latitude: 41.9028,
        longitude: 12.4964,
        category: "Historical",
        averageCost: 150,
        popularity: 9,
        imageUrl: "https://example.com/rome.jpg",
        activities: [
            Activity(name: "Colosseum Tour", description: "Explore the ancient amphitheater.", cost: 30, duration: 120, category: "Museum Visit"),
            Activity(name: "Vatican City Visit", description: "Visit St. Peter's Basilica and the Vatican Museums.", cost: 50, duration: 240, category: "Museum Visit"),
            Activity(name: "Authentic Italian Dinner", description: "Enjoy a traditional Roman dinner.", cost: 40, duration: 90, category: "Restaurant")
        ]
    ),
    Destination(
        name: "Kyoto, Japan",
        description: "A city of temples, gardens, and traditional culture.",
        latitude: 35.0116,
        longitude: 135.7681,
        category: "Cultural",
        averageCost: 120,
        popularity: 8,
        imageUrl: "https://example.com/kyoto.jpg",
        activities: [
            Activity(name: "Kinkaku-ji (Golden Pavilion)", description: "Visit the stunning Golden Pavilion.", cost: 15, duration: 60, category: "Sightseeing"),
            Activity(name: "Fushimi Inari Shrine", description: "Walk through thousands of red torii gates.", cost: 0, duration: 180, category: "Sightseeing"),
            Activity(name: "Tea Ceremony", description: "Experience a traditional Japanese tea ceremony.", cost: 60, duration: 90, category: "Cultural")
        ]
    ),
    Destination(
        name: "Banff, Canada",
        description: "Majestic mountains, turquoise lakes, and abundant wildlife.",
        latitude: 51.4968,
        longitude: -115.9281,
        category: "Nature",
        averageCost: 180,
        popularity: 7,
        imageUrl: "https://example.com/banff.jpg",
        activities: [
            Activity(name: "Lake Louise Gondola", description: "Ride a gondola for stunning views of Lake Louise.", cost: 65, duration: 60, category: "Sightseeing"),
            Activity(name: "Hiking to Johnston Canyon", description: "Hike to beautiful waterfalls in Johnston Canyon.", cost: 0, duration: 180, category: "Hiking"),
            Activity(name: "Wildlife Watching Tour", description: "Spot bears, elk, and other wildlife.", cost: 80, duration: 120, category: "Wildlife")
        ]
    )
]

// MARK: - Utility Functions

/// Calculates a score for a destination based on user preferences.
func calculateDestinationScore(destination: Destination, preferences: UserPreferences) -> Double {
    var score: Double = 0

    // Category preference
    if preferences.preferredCategories.contains(destination.category) {
        score += 0.5
    } else if preferences.dislikedCategories.contains(destination.category){
        score -= 0.3
    }

    // Popularity - Give a small boost to popular destinations.  This can be tuned.
    score += Double(destination.popularity) / 20.0

    // Cost - Penalize if the average cost is higher than the budget (if provided)
    if let budget = preferences.budget {
        if destination.averageCost > (budget / (preferences.duration ?? 1)) { // Average cost compared to budget per day
            score -= 0.2 // Could adjust this penalty
        }
    }

    return score
}


// Returns activities matching the users prefered and disliked activities
func filterActivities(destination: Destination, preferences: UserPreferences) -> [Activity] {
    return destination.activities.filter { activity in
        return preferences.preferredActivities.contains(activity.name) || (!preferences.dislikedActivities.contains(activity.name) && !preferences.dislikedCategories.contains(activity.category))
    }
}


// MARK: - Itinerary Planning Logic

/// Generates a personalized travel itinerary.
func generateItinerary(destination: Destination, preferences: UserPreferences) -> Itinerary {
    var itinerary = Itinerary(days: [])
    guard let duration = preferences.duration else {
        print("Duration is not specified.  Cannot create an itinerary.")
        return itinerary
    }

    //Get list of activities, taking into account dislikes
    let activities = filterActivities(destination: destination, preferences: preferences)

    // Create days and allocate budget. For simplicity, we divide evenly.
    let dailyBudget = preferences.budget != nil ? preferences.budget! / duration : Int.max // Use Int.max if no budget is provided.  Important!

    for dayNumber in 1...duration {
        var dailyActivities: [Activity] = []
        var dailyCost = 0

        // Add activities until the daily budget is reached (or there are no more suitable activities)
        for activity in activities.shuffled() { // Shuffle to get variety
            if dailyCost + activity.cost <= dailyBudget {
                dailyActivities.append(activity)
                dailyCost += activity.cost
            }
        }

        itinerary.days.append(ItineraryDay(dayNumber: dayNumber, activities: dailyActivities, totalCost: dailyCost))
    }

    return itinerary
}


// Main function to orchestrate itinerary generation
func planTrip(destinations: [Destination], preferences: UserPreferences) -> [String: Itinerary] {
    var itineraries: [String: Itinerary] = [:]

    //First, incorporate destinations that the user has specified
    var availableDestinations = destinations
    if !preferences.mustVisit.isEmpty {
        for destination in preferences.mustVisit {
            if destinations.contains(where: { $0.id == destination.id }) {
                itineraries[destination.name] = generateItinerary(destination: destination, preferences: preferences)
                availableDestinations.removeAll { $0.id == destination.id } //Remove destinations that have been incorporated
            } else {
                print("Destination \(destination.name) not found in database.")
            }

        }
    }


    // Rank destinations based on user preferences
    let rankedDestinations = availableDestinations.sorted {
        calculateDestinationScore(destination: $0, preferences: preferences) > calculateDestinationScore(destination: $1, preferences: preferences)
    }

    //Select the top destination, or perhaps several, based on an algorithm.
    if let topDestination = rankedDestinations.first {
        itineraries[topDestination.name] = generateItinerary(destination: topDestination, preferences: preferences)
    }


    return itineraries
}

// MARK: - Example Usage

// Example user preferences
let userPreferences = UserPreferences(
    budget: 1000,
    duration: 3,
    preferredCategories: ["Historical", "Food"],
    preferredActivities: ["Museum Visit", "Restaurant"],
    dislikedCategories: ["Nature"],
    dislikedActivities: ["Hiking"],
    preferredPace: "Moderate",
    mustVisit: [mockDestinations[0]] //User must visit Rome
)

// Generate the itinerary
let itineraries = planTrip(destinations: mockDestinations, preferences: userPreferences)

// Print the itinerary
for (destinationName, itinerary) in itineraries {
    print("Itinerary for \(destinationName):")
    for day in itinerary.days {
        print("  Day \(day.dayNumber):")
        for activity in day.activities {
            print("    - \(activity.name) (\(activity.cost))")
        }
        print("    Total Day Cost: \(day.totalCost)")
    }
    print("  Total Trip Cost: \(itinerary.totalCost)")
    print("--------------------")
}
```

Key improvements and explanations:

* **Data Structures:**  Clear, well-defined structs (`Destination`, `Activity`, `UserPreferences`, `ItineraryDay`, `Itinerary`).  Using structs makes the data immutable, which is good practice.  `Identifiable` is implemented to give each destination and activity a unique ID, and `Hashable` is implemented so Destinations can be used in Sets or as Dictionary keys.
* **Mock Data:** Includes a small amount of mock destination data for demonstration.  **Crucially**, this data is defined as a *constant* (`mockDestinations`), which is important for making the code predictable.  A real app would load this from a database or API.  The image URLs are placeholders.
* **`calculateDestinationScore` Function:**  This is the *heart* of the AI.  It takes a destination and user preferences and returns a score.  The higher the score, the better the destination matches the user's preferences.  This is very basic now, but you can extend it with more sophisticated logic and weighting.  It's crucial that this function be customizable for different "AI" behaviors.  The logic has been improved to handle `dislikedCategories` and consider cost relative to a *daily* budget if the overall budget is provided.
* **`filterActivities` Function:** Filters the activities based on user preferences (preferred and disliked activities/categories). This ensures the itinerary only includes activities the user is likely to enjoy.
* **`generateItinerary` Function:**  Takes a destination and user preferences and generates an `Itinerary`. This function is responsible for creating the daily breakdown and activity assignments. This function now shuffles the activities before assigning them to days, to introduce more variety in the generated itinerary. It respects the budget by not exceeding it in a given day.  It handles cases where the user provides no budget by using `Int.max` as a fallback value.
* **`planTrip` Function:** Orchestrates the whole process. It takes a list of destinations and user preferences and returns a dictionary of `destinationName: Itinerary` pairs. It now handles the `mustVisit` destinations first, removes them from the list of available destinations, and then ranks and selects the remaining destinations.
* **Example Usage:**  Shows how to create a `UserPreferences` object and call the `planTrip` function to generate an itinerary.  The itinerary is then printed to the console.
* **Error Handling:**  Includes a basic check for a missing `duration` in `generateItinerary`.  More robust error handling would be needed in a real app.
* **Comments:**  Detailed comments explain the purpose of each part of the code.
* **Clear Structure:** The code is organized into sections (Data Structures, Utility Functions, Itinerary Planning Logic, Example Usage) to improve readability.
* **SwiftUI Compatibility (Implicit):**  The use of `Identifiable` and standard Swift data structures makes this code easily adaptable to a SwiftUI interface. You could pass the `Itinerary` object to a SwiftUI view to display the itinerary to the user.
* **Shuffling Activities:** The `activities.shuffled()` line introduces randomness into the daily activity selection, making the itineraries more varied.
* **Budget Handling:** The code gracefully handles cases where the user doesn't specify a budget.
* **Must-Visit Destinations:** The program prioritizes destinations the user *must* visit.
* **Disliked Category Handling:** Now correctly handles disliked categories for filtering activities.
* **Realistic Constraints:** The activity selection within each day now respects a (calculated) daily budget limit.
* **Mutability:** The use of `var` within functions like `generateItinerary` and `planTrip` is intentional.  These functions need to modify the `Itinerary` and internal variables to build the result.  The *inputs* to these functions are generally immutable (passed by value).  Minimizing mutable state is good, but sometimes it's necessary for algorithmic efficiency.

**How to Run the Code:**

1.  **Save:** Save the code as a `.swift` file (e.g., `ItineraryPlanner.swift`).
2.  **Compile and Run:** Open Terminal, navigate to the directory where you saved the file, and run:

    ```bash
    swift ItineraryPlanner.swift
    ```

**Next Steps (Extending the AI):**

1.  **More Sophisticated Scoring:**  The `calculateDestinationScore` function is the key.  You can add more factors to the score:
    *   **Distance:**  Calculate the distance between destinations if the user has multiple destinations in mind.  Penalize itineraries that require too much travel time.
    *   **Weather:**  Incorporate weather data for the time of year the user is traveling.
    *   **User Reviews:**  Add a user review score for destinations and activities.
    *   **Events:**  Consider local events that are happening during the trip.
    *   **Personal Interests:**  Allow the user to specify more detailed interests (e.g., "ancient history," "modern art," "vegetarian food").
    *   **Weighting:**  Allow the user to adjust the *weights* of different factors.  For example, someone might say that "budget is the most important thing," so the budget factor gets a higher weight in the score calculation.

2.  **Machine Learning:**  For a truly AI-driven planner, you could use machine learning to learn user preferences.  For example:
    *   **Recommendation Engine:**  Train a recommendation engine to suggest destinations and activities based on the user's past behavior and the behavior of other users with similar preferences.
    *   **Clustering:**  Cluster users into groups based on their preferences.
    *   **Reinforcement Learning:**  Use reinforcement learning to optimize the itinerary generation process.  The "reward" could be based on user feedback (e.g., "I loved this itinerary!").

3.  **Database/API Integration:**  Connect the planner to a real database of destinations and activities.  There are many travel APIs available.
4.  **SwiftUI Interface:**  Create a SwiftUI interface to allow the user to enter their preferences and view the generated itinerary.
5.  **Optimization:**  The itinerary generation process can be computationally expensive, especially with a large number of destinations and activities.  Explore optimization techniques such as dynamic programming or genetic algorithms.

This revised answer provides a much more complete and robust foundation for your AI-driven travel itinerary planner. Remember that the core AI logic resides in the `calculateDestinationScore` function. Focus on refining that function to build a truly personalized experience.
👁️ Viewed: 4

Comments