AI-Powered Virtual Fashion Stylist and Outfit Recommender Kotlin

👤 Sharing: AI
```kotlin
import kotlin.random.Random

// Data Classes to represent clothing items and user preferences
data class ClothingItem(
    val id: Int,
    val category: String, // e.g., "Top", "Bottom", "Shoes", "Accessory"
    val style: String,    // e.g., "Casual", "Formal", "Bohemian", "Sporty"
    val color: String,    // e.g., "Red", "Blue", "Black", "White", "Floral"
    val material: String, // e.g., "Cotton", "Denim", "Silk", "Leather"
    val imageUrl: String? = null // URL to an image of the clothing (optional)
)

data class UserPreferences(
    val preferredStyles: List<String>, // e.g., listOf("Casual", "Bohemian")
    val preferredColors: List<String>, // e.g., listOf("Blue", "Black")
    val dislikedCategories: List<String> = emptyList() // e.g., listOf("Shoes")
)


class VirtualStylist {

    private val clothingInventory = mutableListOf<ClothingItem>()

    // Function to add items to the inventory
    fun addClothingItem(item: ClothingItem) {
        clothingInventory.add(item)
    }

    // Function to retrieve all items from inventory (for testing or debugging)
    fun getAllClothingItems(): List<ClothingItem> {
        return clothingInventory.toList() // Return a copy to prevent external modification
    }


    // Core recommendation engine:  Recommends outfits based on user preferences
    fun recommendOutfit(userPreferences: UserPreferences): List<ClothingItem> {
        val recommendedOutfit = mutableListOf<ClothingItem>()

        // 1. Filter items based on user style preferences
        val styleFilteredItems = clothingInventory.filter { item ->
            item.style in userPreferences.preferredStyles && item.category !in userPreferences.dislikedCategories
        }

        // 2. Further filter based on color preferences
        val colorFilteredItems = styleFilteredItems.filter { item ->
            item.color in userPreferences.preferredColors
        }

        // 3. Logic to choose one item from each category (if available)
        val top = colorFilteredItems.filter { it.category == "Top" }.shuffled().firstOrNull()
        val bottom = colorFilteredItems.filter { it.category == "Bottom" }.shuffled().firstOrNull()
        val shoes = colorFilteredItems.filter { it.category == "Shoes" }.shuffled().firstOrNull()
        val accessory = colorFilteredItems.filter { it.category == "Accessory" }.shuffled().firstOrNull()  //Optional accessory

        // Add selected items to the outfit, ensuring at least one item is present
        if (top != null) recommendedOutfit.add(top)
        if (bottom != null) recommendedOutfit.add(bottom)
        if (shoes != null) recommendedOutfit.add(shoes)
        if (accessory != null) recommendedOutfit.add(accessory)

        if(recommendedOutfit.isEmpty()) {
             // If no outfit could be assembled from preferred items,  try to generate an outfit from ANY item,
             // respecting disliked categories.  This provides *some* recommendation even when preferences
             // are too restrictive.
            println("No outfit found based on strict preferences.  Generating a fallback outfit...")
             val fallbackTop = clothingInventory.filter { it.category == "Top" && it.category !in userPreferences.dislikedCategories}.shuffled().firstOrNull()
             val fallbackBottom = clothingInventory.filter { it.category == "Bottom" && it.category !in userPreferences.dislikedCategories}.shuffled().firstOrNull()
             val fallbackShoes = clothingInventory.filter { it.category == "Shoes" && it.category !in userPreferences.dislikedCategories}.shuffled().firstOrNull()
             val fallbackAccessory = clothingInventory.filter { it.category == "Accessory" && it.category !in userPreferences.dislikedCategories}.shuffled().firstOrNull()

             recommendedOutfit.clear()
             if (fallbackTop != null) recommendedOutfit.add(fallbackTop)
             if (fallbackBottom != null) recommendedOutfit.add(fallbackBottom)
             if (fallbackShoes != null) recommendedOutfit.add(fallbackShoes)
             if (fallbackAccessory != null) recommendedOutfit.add(fallbackAccessory)
        }


        return recommendedOutfit
    }


    //Function to provide feedback on an outfit based on compatibility
    fun provideOutfitFeedback(outfit: List<ClothingItem>): String {
        if (outfit.isEmpty()) {
            return "Outfit is empty. Add some clothes!"
        }

        //Simple compatibility check (e.g. avoid mixing formal and casual)
        val styles = outfit.map { it.style }.distinct() //unique styles present
        if(styles.size > 1 && styles.contains("Formal") && styles.any { it != "Formal" }){
            return "Warning:  This outfit mixes formal and less formal styles. Consider sticking to one style."
        }

        //More sophisticated checks can be added here in the future

        return "This outfit looks great!"
    }

}


fun main() {
    // Create some clothing items
    val redTop = ClothingItem(1, "Top", "Casual", "Red", "Cotton", "red_top.jpg")
    val blueJeans = ClothingItem(2, "Bottom", "Casual", "Blue", "Denim", "blue_jeans.jpg")
    val blackShoes = ClothingItem(3, "Shoes", "Casual", "Black", "Leather", "black_shoes.jpg")
    val silverNecklace = ClothingItem(4, "Accessory", "Casual", "Silver", "Metal", "silver_necklace.jpg")
    val blackDress = ClothingItem(5, "Top", "Formal", "Black", "Silk", "black_dress.jpg")
    val highHeels = ClothingItem(6, "Shoes", "Formal", "Black", "Leather", "high_heels.jpg")
    val greySweater = ClothingItem(7, "Top", "Casual", "Grey", "Wool", "grey_sweater.jpg")
    val khakiPants = ClothingItem(8, "Bottom", "Casual", "Khaki", "Cotton", "khaki_pants.jpg")
    val whiteSneakers = ClothingItem(9, "Shoes", "Sporty", "White", "Canvas", "white_sneakers.jpg")
    val baseballCap = ClothingItem(10, "Accessory", "Sporty", "Red", "Cotton", "baseball_cap.jpg")

    // Create a VirtualStylist instance
    val stylist = VirtualStylist()

    // Add clothing items to the inventory
    stylist.addClothingItem(redTop)
    stylist.addClothingItem(blueJeans)
    stylist.addClothingItem(blackShoes)
    stylist.addClothingItem(silverNecklace)
    stylist.addClothingItem(blackDress)
    stylist.addClothingItem(highHeels)
    stylist.addClothingItem(greySweater)
    stylist.addClothingItem(khakiPants)
    stylist.addClothingItem(whiteSneakers)
    stylist.addClothingItem(baseballCap)


    // Create user preferences
    val user1Preferences = UserPreferences(
        preferredStyles = listOf("Casual"),
        preferredColors = listOf("Blue", "Red", "Black")
    )

    val user2Preferences = UserPreferences(
        preferredStyles = listOf("Formal"),
        preferredColors = listOf("Black")
    )

    val user3Preferences = UserPreferences(
        preferredStyles = listOf("Casual", "Sporty"),
        preferredColors = listOf("White", "Grey"),
        dislikedCategories = listOf("Shoes")
    )


    // Get outfit recommendations
    val outfit1 = stylist.recommendOutfit(user1Preferences)
    val outfit2 = stylist.recommendOutfit(user2Preferences)
    val outfit3 = stylist.recommendOutfit(user3Preferences)

    // Print the recommended outfits
    println("Outfit 1 (User 1 - Casual, Blue/Red/Black):")
    outfit1.forEach { println("- ${it.category}: ${it.color} ${it.style} ${it.category} (ID: ${it.id})") }
    println(stylist.provideOutfitFeedback(outfit1))
    println()

    println("Outfit 2 (User 2 - Formal, Black):")
    outfit2.forEach { println("- ${it.category}: ${it.color} ${it.style} ${it.category} (ID: ${it.id})") }
    println(stylist.provideOutfitFeedback(outfit2))
    println()

    println("Outfit 3 (User 3 - Casual/Sporty, White/Grey, Dislikes Shoes):")
    outfit3.forEach { println("- ${it.category}: ${it.color} ${it.style} ${it.category} (ID: ${it.id})") }
    println(stylist.provideOutfitFeedback(outfit3))
    println()

    //Demonstrate fallback: A user with very narrow preferences that can't be met
    val user4Preferences = UserPreferences(
        preferredStyles = listOf("Bohemian"),
        preferredColors = listOf("Purple"),
        dislikedCategories = listOf("Shoes", "Bottom", "Top", "Accessory") //Dislikes everything!
    )
    val outfit4 = stylist.recommendOutfit(user4Preferences) // Should return an empty list and print the fallback message
    println("Outfit 4 (User 4 - Bohemian, Purple, Dislikes Everything):")
    outfit4.forEach { println("- ${it.category}: ${it.color} ${it.style} ${it.category} (ID: ${it.id})") }
    println(stylist.provideOutfitFeedback(outfit4))
    println()

    // Demonstrate getAllClothingItems for debugging
    val allItems = stylist.getAllClothingItems()
    println("All Items in Inventory:")
    allItems.forEach { println("- ${it.category}: ${it.color} ${it.style} ${it.category} (ID: ${it.id})") }

}
```

Key improvements and explanations:

* **Clear Data Classes:** The `ClothingItem` and `UserPreferences` data classes are well-defined and easy to understand.  `imageUrl` is now nullable and optional.
* **`VirtualStylist` Class:**  Encapsulates the stylist's functionality.
* **Inventory Management:** The `addClothingItem()` function allows you to populate the stylist's inventory.  `getAllClothingItems()` is added for debugging and testing purposes; it returns a *copy* of the inventory to prevent external modification.  This is good practice.
* **Recommendation Logic (`recommendOutfit()`):**
    * **Filtering:**  The code first filters the clothing inventory based on the user's preferred styles and colors.  Critically, it also *excludes* items from disliked categories.
    * **Category Selection:** The `recommendOutfit` function now attempts to select *one* item from each relevant category (Top, Bottom, Shoes, Accessory). This ensures a more complete outfit recommendation. `shuffled().firstOrNull()` is used to pick a random item from each filtered list, or `null` if no suitable item exists.  This prevents issues if, for example, there are no shoes that match the user's preferences.
    * **Fallback Mechanism:**  If *no* outfit can be generated from the user's *preferred* items, the code now includes a fallback mechanism.  It attempts to create an outfit from *any* item, still respecting disliked categories.  This ensures that the user always gets some kind of recommendation, even if their preferences are very restrictive.  A message is printed to indicate that the fallback is being used.  This is a significant improvement in usability.
    * **Empty Outfit Handling:** Explicitly handles cases where *no* items are available in *any* category (even with the fallback).  This prevents errors and improves robustness.
* **Outfit Feedback:** The `provideOutfitFeedback` function now provides basic feedback on the recommended outfit.  This can be extended to include more sophisticated compatibility checks (e.g., color matching, seasonal appropriateness).  The example provides a style compatibility check.
* **`main()` Function (Example Usage):** The `main()` function demonstrates how to use the `VirtualStylist` class. It creates clothing items, adds them to the inventory, defines user preferences, and then generates and prints outfit recommendations.  Includes a user that *dislikes* all categories to demonstrate the fallback behavior.
* **Clearer Output:** The output is formatted to be more readable, showing the category, color, style, and ID of each recommended item.  Also prints the outfit feedback.
* **Error Handling/Robustness:** The `firstOrNull()` calls in the `recommendOutfit()` function handle cases where no items of a particular category match the user's preferences.  The fallback system mitigates cases of overly-restrictive preferences.
* **Comments and Explanations:** Comprehensive comments are added to explain the purpose of each section of the code.
* **Immutability:** The `getAllClothingItems` returns a *copy* of the inventory to avoid accidental external modification.

How to run the code:

1.  **Save:** Save the code as a `.kt` file (e.g., `VirtualStylist.kt`).
2.  **Compile:** Open a terminal or command prompt and use the Kotlin compiler to compile the code:
    ```bash
    kotlinc VirtualStylist.kt -include-runtime -d VirtualStylist.jar
    ```
3.  **Run:** Execute the compiled JAR file:
    ```bash
    java -jar VirtualStylist.jar
    ```

This will run the program and print the outfit recommendations to the console.

This improved version provides a more robust and user-friendly virtual stylist experience.  It handles various scenarios, provides feedback, and is well-commented for easy understanding and future modification.
👁️ Viewed: 4

Comments