# BaoLife Phase 4: Dating System Improvements

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Transform dating from 50% random matching into an engaging relationship system with compatibility, bios, events, and mini-games.

**Architecture:** Backend compatibility algorithm + OpenAI bio generation + relationship event triggers. Frontend profile cards + event modals + date mini-games.

**Tech Stack:**
- Backend: Python, MySQL, OpenAI API
- Frontend: SwiftUI with animations

**Total Time Estimate:** 30-35 hours (Week 7)

---

## Backend Components (../lichun)

### Component 32: Compatibility Algorithm

**Files:**
- Create: `../lichun/dating/compatibility.py`
- Modify: `../lichun/dating/matching.py`
- Create: `../lichun/tests/test_compatibility.py`

**Implementation:**

#### Database Schema

```sql
ALTER TABLE match_attempts ADD COLUMN compatibility_score INT DEFAULT 50;
ALTER TABLE persons ADD COLUMN interests JSON DEFAULT '[]';
ALTER TABLE persons ADD COLUMN personality_traits JSON DEFAULT '[]';
```

**Run:** `mysql -u root -p baolife_db < schema_update.sql`

---

#### Test File

Create: `../lichun/tests/test_compatibility.py`

```python
import pytest
from dating.compatibility import calculate_compatibility

def test_shared_interests_boost():
    player = {'likes': ['music', 'sports', 'reading'], 'age_years': 25, 'prestige': 50, 'education_level': 3}
    match = {'likes': ['music', 'sports'], 'age_years': 26, 'prestige': 55, 'education_level': 3}
    
    score = calculate_compatibility(player, match)
    assert score >= 60  # 2 shared interests = +10

def test_age_compatibility():
    player = {'likes': [], 'age_years': 25, 'prestige': 50, 'education_level': 3}
    match_close = {'likes': [], 'age_years': 27, 'prestige': 50, 'education_level': 3}  # 2 years diff
    match_far = {'likes': [], 'age_years': 40, 'prestige': 50, 'education_level': 3}   # 15 years diff
    
    score_close = calculate_compatibility(player, match_close)
    score_far = calculate_compatibility(player, match_far)
    
    assert score_close > score_far
```

**Run:** `pytest tests/test_compatibility.py -v`
**Expected:** FAIL

---

#### Implementation

Create: `../lichun/dating/compatibility.py`

```python
def calculate_compatibility(player: dict, match: dict) -> int:
    """
    Calculate compatibility score (0-100) between player and potential match
    
    Factors:
    - Shared interests: +5 per match
    - Age difference: +15 (0-3 years), +5 (4-5 years), -15 (>10 years)
    - Education level: +10 (same), +5 (±1 level)
    - Wealth/prestige: +10 (similar), -5 (very different)
    """
    score = 50  # Base compatibility
    
    # Shared interests
    player_interests = set(player.get('likes', []))
    match_interests = set(match.get('likes', []))
    common = player_interests.intersection(match_interests)
    score += len(common) * 5
    
    # Age compatibility
    age_diff = abs(player['age_years'] - match['age_years'])
    if age_diff <= 3:
        score += 15
    elif age_diff <= 5:
        score += 5
    elif age_diff > 10:
        score -= 15
    
    # Education compatibility
    edu_diff = abs(player.get('education_level', 0) - match.get('education_level', 0))
    if edu_diff == 0:
        score += 10
    elif edu_diff == 1:
        score += 5
    
    # Wealth/prestige compatibility
    prestige_diff = abs(player.get('prestige', 0) - match.get('prestige', 0))
    if prestige_diff < 20:
        score += 10
    elif prestige_diff > 50:
        score -= 5
    
    return max(0, min(100, score))
```

**Run:** `pytest tests/test_compatibility.py -v`
**Expected:** PASS

---

#### Update Matching Logic

Modify: `../lichun/dating/matching.py`

```python
from dating.compatibility import calculate_compatibility

def attempt_match(player_id: int, target_id: int) -> dict:
    """Attempt to match with target using compatibility"""
    player = get_person(player_id)
    target = get_person(target_id)
    
    # Calculate compatibility
    compatibility = calculate_compatibility(player, target)
    
    # Match if score > 60, or allow 20% random matches (serendipity!)
    import random
    if compatibility > 60 or random.random() < 0.2:
        # Create match
        create_match(player_id, target_id, compatibility)
        return {'success': True, 'compatibility': compatibility}
    
    return {'success': False, 'compatibility': compatibility}
```

**Commit:**
```bash
git add dating/compatibility.py dating/matching.py tests/test_compatibility.py
git commit -m "feat(dating): add compatibility algorithm

- Shared interests, age, education, wealth factors
- 0-100 compatibility score
- Match threshold at 60+ or 20% random
- Full test coverage

🤖 Generated with Claude Code"
```

**Estimated Time:** 4-5 hours

---

### Component 33: NPC Bio Generation

**Files:**
- Create: `../lichun/dating/bio_generator.py`
- Modify: `../lichun/database_schema.sql`
- Create: `../lichun/tests/test_bio_generation.py`

**Database Schema:**
```sql
ALTER TABLE persons ADD COLUMN bio TEXT;
ALTER TABLE persons ADD COLUMN last_bio_update DATETIME;
```

**Implementation:**

```python
import openai
from config import Config

openai.api_key = Config.OPENAI_API_KEY

def generate_dating_bio(person: dict) -> str:
    """Generate dating profile bio using OpenAI"""
    
    prompt = f"""Write a natural, engaging dating profile bio (2-3 sentences) for:
    Name: {person['first_name']}, Age: {person['age_years']}
    Occupation: {person.get('occupation', 'Student')}
    Interests: {', '.join(person.get('likes', ['reading', 'music']))}
    Personality: {person.get('personality_trait', 'friendly')}
    
    Make it conversational and authentic."""
    
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=100,
        temperature=0.8
    )
    
    bio = response.choices[0].message.content.strip()
    
    # Save to database
    db.execute_query(
        "UPDATE persons SET bio = %s, last_bio_update = NOW() WHERE id = %s",
        (bio, person['id']),
        fetch=False
    )
    
    return bio

def get_or_generate_bio(person_id: int) -> str:
    """Get existing bio or generate new one"""
    person = db.execute_query("SELECT * FROM persons WHERE id = %s", (person_id,))[0]
    
    # Check if bio exists and is recent (< 30 days old)
    if person['bio'] and person['last_bio_update']:
        from datetime import datetime, timedelta
        if datetime.now() - person['last_bio_update'] < timedelta(days=30):
            return person['bio']
    
    # Generate new bio
    return generate_dating_bio(person)
```

**Commit:**
```bash
git add dating/bio_generator.py tests/test_bio_generation.py database_schema.sql
git commit -m "feat(dating): add AI-powered bio generation

- OpenAI integration for natural bios
- Cache bios for 30 days
- Regenerate on major life changes
- Personality and interest-based content

🤖 Generated with Claude Code"
```

**Estimated Time:** 5-6 hours

---

### Component 34: Relationship Events System

**Files:**
- Create: `../lichun/dating/relationship_events.py`
- Create: `../lichun/tests/test_relationship_events.py`

**Database Schema:**
```sql
CREATE TABLE relationship_events (
    id INT PRIMARY KEY AUTO_INCREMENT,
    event_type ENUM('argument', 'anniversary', 'jealousy', 'proposal', 'breakup_threat') NOT NULL,
    trigger_condition VARCHAR(200),
    description_template TEXT,
    resolution_options JSON,
    INDEX idx_event_type (event_type)
);

CREATE TABLE player_relationship_events (
    id INT PRIMARY KEY AUTO_INCREMENT,
    player_id INT NOT NULL,
    npc_id INT NOT NULL,
    event_id INT NOT NULL,
    occurred_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    resolution_chosen VARCHAR(100),
    FOREIGN KEY (player_id) REFERENCES players(id)
);
```

**Event Definitions:**
```python
RELATIONSHIP_EVENTS = [
    {
        'type': 'argument',
        'trigger': 'affinity_drop_15',
        'description': "You and {partner_name} had a disagreement. They seem upset.",
        'options': [
            {'choice': 'apologize', 'affinity_change': 10, 'diamond_cost': 0},
            {'choice': 'buy_gift', 'affinity_change': 25, 'diamond_cost': 20},
            {'choice': 'ignore', 'affinity_change': -10, 'diamond_cost': 0}
        ]
    },
    {
        'type': 'anniversary',
        'trigger': 'days_together_365',
        'description': "It's your {years}year anniversary with {partner_name}!",
        'options': [
            {'choice': 'dinner_date', 'affinity_change': 5, 'diamond_cost': 0},
            {'choice': 'luxury_date', 'affinity_change': 15, 'diamond_cost': 50},
            {'choice': 'forgot', 'affinity_change': -20, 'diamond_cost': 0}
        ]
    }
]

def check_relationship_triggers(player_id: int, npc_id: int):
    """Check if any relationship events should trigger"""
    relationship = get_relationship(player_id, npc_id)
    
    # Check affinity drop
    if relationship['affinity_change_24h'] <= -15:
        trigger_event(player_id, npc_id, 'argument')
    
    # Check anniversary
    days_together = (datetime.now() - relationship['started_date']).days
    if days_together % 365 == 0 and days_together > 0:
        trigger_event(player_id, npc_id, 'anniversary')
```

**Commit:**
```bash
git add dating/relationship_events.py tests/test_relationship_events.py
git commit -m "feat(dating): add relationship event system

- Arguments, anniversaries, jealousy, proposals
- Diamond-based resolutions
- Affinity consequences
- Event history tracking

🤖 Generated with Claude Code"
```

**Estimated Time:** 6-7 hours

---

### Component 35: Enhanced Date Activities

**Files:**
- Create: `../lichun/dating/date_activities.py`
- Create: `../lichun/tests/test_date_activities.py`

**Database Schema:**
```sql
CREATE TABLE date_activities (
    id INT PRIMARY KEY AUTO_INCREMENT,
    activity_name VARCHAR(100) NOT NULL,
    base_cost INT DEFAULT 0,
    premium_version BOOLEAN DEFAULT FALSE,
    diamond_cost INT DEFAULT 0,
    affinity_gain_min INT,
    affinity_gain_max INT,
    energy_cost INT,
    minigame_questions JSON
);
```

**Activity Definitions:**
```python
DATE_ACTIVITIES = [
    {
        'name': 'dinner',
        'cost': 0,
        'diamond_cost': 0,
        'affinity_range': (3, 8),
        'energy': 10,
        'questions': [
            {'text': 'Partner mentions loving concerts', 'correct': 'ask_about_music', 'wrong': 'change_subject'},
            {'text': 'Partner seems interested in your hobby', 'correct': 'share_more', 'wrong': 'talk_about_work'}
        ]
    },
    {
        'name': 'luxury_restaurant',
        'cost': 0,
        'diamond_cost': 50,
        'premium': True,
        'affinity_range': (15, 25),
        'energy': 10,
        'questions': [/* more questions */]
    }
]

def execute_date(player_id: int, npc_id: int, activity: str, minigame_results: list) -> dict:
    """Execute date with mini-game scoring"""
    activity_data = get_activity(activity)
    
    # Calculate affinity gain based on mini-game performance
    correct_answers = sum(1 for r in minigame_results if r['correct'])
    total_questions = len(minigame_results)
    performance = correct_answers / total_questions if total_questions > 0 else 0.5
    
    # Affinity gain scales with performance
    min_affinity, max_affinity = activity_data['affinity_range']
    base_gain = min_affinity + (max_affinity - min_affinity) * performance
    
    # Bonus for perfect score
    if performance == 1.0:
        base_gain *= 1.2
    
    # Apply affinity gain
    update_affinity(player_id, npc_id, int(base_gain))
    
    return {
        'success': True,
        'affinity_gain': int(base_gain),
        'performance': performance,
        'feedback': generate_date_feedback(performance)
    }
```

**Commit:**
```bash
git add dating/date_activities.py tests/test_date_activities.py
git commit -m "feat(dating): add enhanced date activities

- Multiple date types (dinner, movies, luxury, weekend)
- Mini-game conversation mechanics
- Performance-based affinity gains
- Premium diamond-gated dates

🤖 Generated with Claude Code"
```

**Estimated Time:** 3-4 hours

---

## Frontend Components (lichunWebsocket)

### Component 36-41: Dating UI Improvements

**Summary of Frontend Components:**

**Component 36: Enhanced Profile Cards** (3h)
- Show bio, interests, compatibility %
- Match reasoning tooltip

**Component 37: Relationship Event Modals** (2h)
- Special modal for relationship events
- Clear consequence preview

**Component 38: Date Activity Selection UI** (2h)
- Grid of date cards
- Energy/diamond costs shown
- Expected affinity range

**Component 39: Date Mini-Game Interface** (3h)
- Conversation scene with choices
- 3-5 rounds per date
- Result animation

**Component 40: Relationship Detail View** (2h)
- Timeline of relationship
- Statistics and milestones

**Component 41: Match Explanation Toast** (1h)
- "Great match!" toasts with reasoning

**Combined Implementation Time:** 12-15 hours

**Key Code Example for Component 39:**

```swift
struct DateMiniGameView: View {
    let npc: Person
    @State private var currentRound: Int = 0
    @State private var correctAnswers: Int = 0
    
    let questions: [DateQuestion] = [
        DateQuestion(
            text: "I love going to concerts!",
            options: [
                ("Me too! What kind of music?", true),
                ("That's nice.", false),
                ("I prefer staying home.", false)
            ]
        )
    ]
    
    var body: some View {
        VStack {
            // NPC avatar
            SVGImageView(urlString: npc.image)
                .frame(width: 100, height: 100)
            
            // Speech bubble
            Text(questions[currentRound].text)
                .padding()
                .background(Color.blue.opacity(0.1))
                .cornerRadius(12)
            
            // Response choices
            ForEach(questions[currentRound].options, id: \.0) { option in
                Button(action: {
                    handleChoice(option.1)
                }) {
                    Text(option.0)
                        .padding()
                        .background(Color.white)
                        .cornerRadius(8)
                }
            }
        }
    }
    
    func handleChoice(_ correct: Bool) {
        if correct {
            correctAnswers += 1
            showFeedback("+3 affinity", positive: true)
        } else {
            showFeedback("Awkward...", positive: false)
        }
        
        if currentRound < questions.count - 1 {
            currentRound += 1
        } else {
            completeMiniGame()
        }
    }
}
```

**Commit all frontend:**
```bash
git add SwipeDatingView.swift RelationshipEventView.swift DateActivitySelectionView.swift \
        DateMiniGameView.swift RelationshipDetailView.swift
        
git commit -m "feat(dating): complete dating UI overhaul

- Enhanced profile cards with bios and compatibility
- Relationship event modals with consequences
- Date activity selection with costs
- Mini-game conversation interface
- Relationship timeline and stats
- Match explanation toasts

🤖 Generated with Claude Code"
```

---

## Phase 4 Summary

**Total Time:** 30-35 hours (Week 7)

**Backend Components (18-20h):**
- ✅ Component 32: Compatibility Algorithm (4-5h)
- ✅ Component 33: Bio Generation (5-6h)
- ✅ Component 34: Relationship Events (6-7h)
- ✅ Component 35: Enhanced Dates (3-4h)

**Frontend Components (12-15h):**
- ✅ Components 36-41: Dating UI (12-15h)

**What's Working:**
- Smart compatibility matching (not random!)
- AI-generated bios for all NPCs
- Dynamic relationship events with consequences
- Engaging date mini-games
- Detailed relationship tracking

**Next Phase:** Polish & Stability (Week 8)

