import math from typing import Dict, Any from dataclasses import dataclass @dataclass class UserPreferences: """使用者偏好設定的資料結構""" living_space: str # "apartment", "house_small", "house_large" yard_access: str # "no_yard", "shared_yard", "private_yard" exercise_time: int # minutes per day exercise_type: str # "light_walks", "moderate_activity", "active_training" grooming_commitment: str # "low", "medium", "high" experience_level: str # "beginner", "intermediate", "advanced" time_availability: str # "limited", "moderate", "flexible" has_children: bool children_age: str # "toddler", "school_age", "teenager" noise_tolerance: str # "low", "medium", "high" space_for_play: bool other_pets: bool climate: str # "cold", "moderate", "hot" health_sensitivity: str = "medium" barking_acceptance: str = None size_preference: str = "no_preference" # "no_preference", "small", "medium", "large", "giant" training_commitment: str = "medium" # "low", "medium", "high" - 訓練投入程度 living_environment: str = "ground_floor" # "ground_floor", "with_elevator", "walk_up" - 居住環境細節 def __post_init__(self): if self.barking_acceptance is None: self.barking_acceptance = self.noise_tolerance class BonusPenaltyEngine: """ 加分扣分引擎類別 負責處理所有品種加分機制、額外評估因素和分數分布優化 """ def __init__(self): """初始化加分扣分引擎""" pass @staticmethod def calculate_breed_bonus(breed_info: dict, user_prefs: 'UserPreferences') -> float: """ 計算品種額外加分 Args: breed_info: 品種資訊字典 user_prefs: 使用者偏好設定 Returns: float: 品種加分 (-0.25 到 0.5 之間) """ bonus = 0.0 temperament = breed_info.get('Temperament', '').lower() # 1. 壽命加分(最高0.05) try: lifespan = breed_info.get('Lifespan', '10-12 years') years = [int(x) for x in lifespan.split('-')[0].split()[0:1]] longevity_bonus = min(0.05, (max(years) - 10) * 0.01) bonus += longevity_bonus except: pass # 2. 性格特徵加分(最高0.15) positive_traits = { 'friendly': 0.05, 'gentle': 0.05, 'patient': 0.05, 'intelligent': 0.04, 'adaptable': 0.04, 'affectionate': 0.04, 'easy-going': 0.03, 'calm': 0.03 } negative_traits = { 'aggressive': -0.08, 'stubborn': -0.06, 'dominant': -0.06, 'aloof': -0.04, 'nervous': -0.05, 'protective': -0.04 } personality_score = sum(value for trait, value in positive_traits.items() if trait in temperament) personality_score += sum(value for trait, value in negative_traits.items() if trait in temperament) bonus += max(-0.15, min(0.15, personality_score)) # 3. 適應性加分(最高0.1) adaptability_bonus = 0.0 if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment": adaptability_bonus += 0.05 if 'adaptable' in temperament or 'versatile' in temperament: adaptability_bonus += 0.05 bonus += min(0.1, adaptability_bonus) # 4. 家庭相容性(最高0.15) if user_prefs.has_children: family_traits = { 'good with children': 0.06, 'patient': 0.05, 'gentle': 0.05, 'tolerant': 0.04, 'playful': 0.03 } unfriendly_traits = { 'aggressive': -0.08, 'nervous': -0.07, 'protective': -0.06, 'territorial': -0.05 } # 年齡評估 age_adjustments = { 'toddler': {'bonus_mult': 0.7, 'penalty_mult': 1.3}, 'school_age': {'bonus_mult': 1.0, 'penalty_mult': 1.0}, 'teenager': {'bonus_mult': 1.2, 'penalty_mult': 0.8} } adj = age_adjustments.get(user_prefs.children_age, {'bonus_mult': 1.0, 'penalty_mult': 1.0}) family_bonus = sum(value for trait, value in family_traits.items() if trait in temperament) * adj['bonus_mult'] family_penalty = sum(value for trait, value in unfriendly_traits.items() if trait in temperament) * adj['penalty_mult'] bonus += min(0.15, max(-0.2, family_bonus + family_penalty)) # 5. 專門技能加分(最高0.1) skill_bonus = 0.0 special_abilities = { 'working': 0.03, 'herding': 0.03, 'hunting': 0.03, 'tracking': 0.03, 'agility': 0.02 } for ability, value in special_abilities.items(): if ability in temperament.lower(): skill_bonus += value bonus += min(0.1, skill_bonus) # 6. 適應性評估(增強版) adaptability_bonus = 0.0 if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment": adaptability_bonus += 0.08 # 小型犬更適合公寓 # 環境適應性評估 if 'adaptable' in temperament or 'versatile' in temperament: if user_prefs.living_space == "apartment": adaptability_bonus += 0.10 # 適應性在公寓環境更重要 else: adaptability_bonus += 0.05 # 其他環境仍有加分 # 氣候適應性 description = breed_info.get('Description', '').lower() climate = user_prefs.climate if climate == 'hot': if 'heat tolerant' in description or 'warm climate' in description: adaptability_bonus += 0.08 elif 'thick coat' in description or 'cold climate' in description: adaptability_bonus -= 0.10 elif climate == 'cold': if 'thick coat' in description or 'cold climate' in description: adaptability_bonus += 0.08 elif 'heat tolerant' in description or 'short coat' in description: adaptability_bonus -= 0.10 bonus += min(0.15, adaptability_bonus) return min(0.5, max(-0.25, bonus)) @staticmethod def calculate_additional_factors(breed_info: dict, user_prefs: 'UserPreferences') -> dict: """ 計算額外的評估因素,結合品種特性與使用者需求的全面評估系統 1. 多功能性評估 - 品種的多樣化能力 2. 訓練性評估 - 學習和服從能力 3. 能量水平評估 - 活力和運動需求 4. 美容需求評估 - 護理和維護需求 5. 社交需求評估 - 與人互動的需求程度 6. 氣候適應性 - 對環境的適應能力 7. 運動類型匹配 - 與使用者運動習慣的契合度 8. 生活方式適配 - 與使用者日常生活的匹配度 """ factors = { 'versatility': 0.0, # 多功能性 'trainability': 0.0, # 可訓練度 'energy_level': 0.0, # 能量水平 'grooming_needs': 0.0, # 美容需求 'social_needs': 0.0, # 社交需求 'weather_adaptability': 0.0,# 氣候適應性 'exercise_match': 0.0, # 運動匹配度 'lifestyle_fit': 0.0 # 生活方式適配度 } temperament = breed_info.get('Temperament', '').lower() description = breed_info.get('Description', '').lower() size = breed_info.get('Size', 'Medium') # 1. 多功能性評估 - 加強品種用途評估 versatile_traits = { 'intelligent': 0.25, 'adaptable': 0.25, 'trainable': 0.20, 'athletic': 0.15, 'versatile': 0.15 } working_roles = { 'working': 0.20, 'herding': 0.15, 'hunting': 0.15, 'sporting': 0.15, 'companion': 0.10 } # 計算特質分數 trait_score = sum(value for trait, value in versatile_traits.items() if trait in temperament) # 計算角色分數 role_score = sum(value for role, value in working_roles.items() if role in description) # 根據使用者需求調整多功能性評分 purpose_traits = { 'light_walks': ['calm', 'gentle', 'easy-going'], 'moderate_activity': ['adaptable', 'balanced', 'versatile'], 'active_training': ['intelligent', 'trainable', 'working'] } if user_prefs.exercise_type in purpose_traits: matching_traits = sum(1 for trait in purpose_traits[user_prefs.exercise_type] if trait in temperament) trait_score += matching_traits * 0.15 factors['versatility'] = min(1.0, trait_score + role_score) # 2. 訓練性評估 trainable_traits = { 'intelligent': 0.3, 'eager to please': 0.3, 'trainable': 0.2, 'quick learner': 0.2, 'obedient': 0.2 } base_trainability = sum(value for trait, value in trainable_traits.items() if trait in temperament) # 根據使用者經驗調整訓練性評分 experience_multipliers = { 'beginner': 1.2, # 新手更需要容易訓練的狗 'intermediate': 1.0, 'advanced': 0.8 # 專家能處理較難訓練的狗 } factors['trainability'] = min(1.0, base_trainability * experience_multipliers.get(user_prefs.experience_level, 1.0)) # 3. 能量水平評估 exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper() energy_levels = { 'VERY HIGH': { 'score': 1.0, 'min_exercise': 120, 'ideal_exercise': 150 }, 'HIGH': { 'score': 0.8, 'min_exercise': 90, 'ideal_exercise': 120 }, 'MODERATE': { 'score': 0.6, 'min_exercise': 60, 'ideal_exercise': 90 }, 'LOW': { 'score': 0.4, 'min_exercise': 30, 'ideal_exercise': 60 } } breed_energy = energy_levels.get(exercise_needs, energy_levels['MODERATE']) # 計算運動時間匹配度 if user_prefs.exercise_time >= breed_energy['ideal_exercise']: energy_score = breed_energy['score'] else: # 如果運動時間不足,按比例降低分數 deficit_ratio = max(0.4, user_prefs.exercise_time / breed_energy['ideal_exercise']) energy_score = breed_energy['score'] * deficit_ratio factors['energy_level'] = energy_score # 4. 美容需求評估 grooming_needs = breed_info.get('Grooming Needs', 'MODERATE').upper() grooming_levels = { 'HIGH': 1.0, 'MODERATE': 0.6, 'LOW': 0.3 } # 特殊毛髮類型評估 coat_adjustments = 0 if 'long coat' in description: coat_adjustments += 0.2 if 'double coat' in description: coat_adjustments += 0.15 if 'curly' in description: coat_adjustments += 0.15 # 根據使用者承諾度調整 commitment_multipliers = { 'low': 1.5, # 低承諾度時加重美容需求的影響 'medium': 1.0, 'high': 0.8 # 高承諾度時降低美容需求的影響 } base_grooming = grooming_levels.get(grooming_needs, 0.6) + coat_adjustments factors['grooming_needs'] = min(1.0, base_grooming * commitment_multipliers.get(user_prefs.grooming_commitment, 1.0)) # 5. 社交需求評估 social_traits = { 'friendly': 0.25, 'social': 0.25, 'affectionate': 0.20, 'people-oriented': 0.20 } antisocial_traits = { 'independent': -0.20, 'aloof': -0.20, 'reserved': -0.15 } social_score = sum(value for trait, value in social_traits.items() if trait in temperament) antisocial_score = sum(value for trait, value in antisocial_traits.items() if trait in temperament) # 家庭情況調整 if user_prefs.has_children: child_friendly_bonus = 0.2 if 'good with children' in temperament else 0 social_score += child_friendly_bonus factors['social_needs'] = min(1.0, max(0.0, social_score + antisocial_score)) # 6. 氣候適應性評估 - 更細緻的環境適應評估 climate_traits = { 'cold': { 'positive': ['thick coat', 'winter', 'cold climate'], 'negative': ['short coat', 'heat sensitive'] }, 'hot': { 'positive': ['short coat', 'heat tolerant', 'warm climate'], 'negative': ['thick coat', 'cold climate'] }, 'moderate': { 'positive': ['adaptable', 'all climate'], 'negative': [] } } climate_score = 0.4 # 基礎分數 if user_prefs.climate in climate_traits: # 正面特質加分 climate_score += sum(0.2 for term in climate_traits[user_prefs.climate]['positive'] if term in description) # 負面特質減分 climate_score -= sum(0.2 for term in climate_traits[user_prefs.climate]['negative'] if term in description) factors['weather_adaptability'] = min(1.0, max(0.0, climate_score)) # 7. 運動類型匹配評估 exercise_type_traits = { 'light_walks': ['calm', 'gentle'], 'moderate_activity': ['adaptable', 'balanced'], 'active_training': ['athletic', 'energetic'] } if user_prefs.exercise_type in exercise_type_traits: match_score = sum(0.25 for trait in exercise_type_traits[user_prefs.exercise_type] if trait in temperament) factors['exercise_match'] = min(1.0, match_score + 0.5) # 基礎分0.5 # 8. 生活方式適配評估 lifestyle_score = 0.5 # 基礎分數 # 空間適配 if user_prefs.living_space == 'apartment': if size == 'Small': lifestyle_score += 0.2 elif size == 'Large': lifestyle_score -= 0.2 elif user_prefs.living_space == 'house_large': if size in ['Large', 'Giant']: lifestyle_score += 0.2 # 時間可用性適配 time_availability_bonus = { 'limited': -0.1, 'moderate': 0, 'flexible': 0.1 } lifestyle_score += time_availability_bonus.get(user_prefs.time_availability, 0) factors['lifestyle_fit'] = min(1.0, max(0.0, lifestyle_score)) return factors def amplify_score_extreme(self, score: float) -> float: """ 優化分數分布,提供更有意義的評分範圍。 純粹進行數學轉換,不依賴外部資訊。 Parameters: score: 原始評分(0-1之間的浮點數) Returns: float: 調整後的評分(0-1之間的浮點數) """ def smooth_curve(x: float, steepness: float = 12) -> float: """創建平滑的S型曲線用於分數轉換""" return 1 / (1 + math.exp(-steepness * (x - 0.5))) # 90-100分的轉換(極佳匹配) if score >= 0.90: position = (score - 0.90) / 0.10 return 0.96 + (position * 0.04) # 80-90分的轉換(優秀匹配) elif score >= 0.80: position = (score - 0.80) / 0.10 return 0.90 + (position * 0.06) # 70-80分的轉換(良好匹配) elif score >= 0.70: position = (score - 0.70) / 0.10 return 0.82 + (position * 0.08) # 50-70分的轉換(可接受匹配) elif score >= 0.50: position = (score - 0.50) / 0.20 return 0.75 + (smooth_curve(position) * 0.07) # 50分以下的轉換(較差匹配) else: position = score / 0.50 return 0.70 + (smooth_curve(position) * 0.05) def apply_special_case_adjustments(self, score: float, user_prefs: UserPreferences, breed_info: dict) -> float: """ 處理特殊情況和極端案例的評分調整。這個函數特別關注: 1. 條件組合的協同效應 2. 品種特性的獨特需求 3. 極端情況的合理處理 這個函數就像是一個細心的裁判,會考慮到各種特殊情況, 並根據具體場景做出合理的評分調整。 Parameters: score: 初始評分 user_prefs: 使用者偏好 breed_info: 品種資訊 Returns: float: 調整後的評分(0.2-1.0之間) """ severity_multiplier = 1.0 def evaluate_spatial_exercise_combination() -> float: """ 評估空間與運動需求的組合效應。 這個函數不再過分懲罰大型犬,而是更多地考慮品種的實際特性。 就像評估一個運動員是否適合在特定場地訓練一樣,我們需要考慮 場地大小和運動需求的整體匹配度。 """ multiplier = 1.0 if user_prefs.living_space == 'apartment': temperament = breed_info.get('Temperament', '').lower() description = breed_info.get('Description', '').lower() # 檢查品種是否有利於公寓生活的特徵 apartment_friendly = any(trait in temperament or trait in description for trait in ['calm', 'adaptable', 'quiet']) # 大型犬的特殊處理 if breed_info['Size'] in ['Large', 'Giant']: if apartment_friendly: multiplier *= 0.85 # 從0.7提升到0.85,降低懲罰 else: multiplier *= 0.75 # 從0.5提升到0.75 # 檢查運動需求的匹配度 exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper() exercise_time = user_prefs.exercise_time if exercise_needs in ['HIGH', 'VERY HIGH']: if exercise_time >= 120: # 高運動量可以部分補償空間限制 multiplier *= 1.1 return multiplier def evaluate_experience_combination() -> float: """ 評估經驗需求的複合影響。 這個函數就像是評估一個工作崗位與應聘者經驗的匹配度, 需要綜合考慮工作難度和應聘者能力。 """ multiplier = 1.0 temperament = breed_info.get('Temperament', '').lower() care_level = breed_info.get('Care Level', 'MODERATE') # 新手飼主的特殊考慮,更寬容的評估標準 if user_prefs.experience_level == 'beginner': if care_level == 'HIGH': if user_prefs.has_children: multiplier *= 0.7 # 從0.5提升到0.7 else: multiplier *= 0.8 # 從0.6提升到0.8 # 性格特徵影響,降低懲罰程度 challenging_traits = { 'stubborn': -0.10, # 從-0.15降低 'independent': -0.08, # 從-0.12降低 'dominant': -0.08, # 從-0.12降低 'protective': -0.06, # 從-0.10降低 'aggressive': -0.15 # 保持較高懲罰因安全考慮 } for trait, penalty in challenging_traits.items(): if trait in temperament: multiplier *= (1 + penalty) return multiplier def evaluate_breed_specific_requirements() -> float: """ 評估品種特定需求。 這個函數就像是為每個品種量身定制評估標準, 考慮其獨特的特性和需求。 """ multiplier = 1.0 exercise_time = user_prefs.exercise_time exercise_type = user_prefs.exercise_type # 檢查品種特性 temperament = breed_info.get('Temperament', '').lower() description = breed_info.get('Description', '').lower() exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper() # 運動需求匹配度評估,更合理的標準 if exercise_needs == 'LOW': if exercise_time > 120: multiplier *= 0.85 # 從0.5提升到0.85 elif exercise_needs == 'VERY HIGH': if exercise_time < 60: multiplier *= 0.7 # 從0.5提升到0.7 # 特殊品種類型的考慮 if 'sprint' in temperament: if exercise_time > 120 and exercise_type != 'active_training': multiplier *= 0.85 # 從0.7提升到0.85 if any(trait in temperament for trait in ['working', 'herding']): if exercise_time < 90 or exercise_type == 'light_walks': multiplier *= 0.8 # 從0.7提升到0.8 return multiplier # 計算各項調整 space_exercise_mult = evaluate_spatial_exercise_combination() experience_mult = evaluate_experience_combination() breed_specific_mult = evaluate_breed_specific_requirements() # 整合所有調整因素 severity_multiplier *= space_exercise_mult severity_multiplier *= experience_mult severity_multiplier *= breed_specific_mult # 應用最終調整,確保分數在合理範圍內 final_score = score * severity_multiplier return max(0.2, min(1.0, final_score))