Alon Albalak
major update: all data saved on HF (prompts, results), unified utilities
57be184
"""Scoring logic and enhanced score display functionality"""
import random
from .achievements import AchievementSystem
class Scorer:
"""Handles scoring calculations and display generation"""
def __init__(self, template_renderer=None):
self.template_renderer = template_renderer
def calculate_difficulty(self, user_input, partial_response, llm_manager):
"""Calculate difficulty based on user input tokens and partial completion length"""
if not user_input.strip():
return "Easy", 0
# Count tokens for user input
user_token_count = llm_manager.count_tokens(user_input)
# Count tokens for partial response
partial_token_count = llm_manager.count_tokens(partial_response)
# Difficulty based on tokens and completion percentage
completion_percentage = partial_token_count / 100 # Assume 100 is typical full response
if user_token_count <= 2:
if completion_percentage < 0.3:
return "Medium", user_token_count
else:
return "Easy", user_token_count
elif user_token_count <= 4:
return "Medium", user_token_count
else:
return "Hard", user_token_count
def calculate_rank_and_percentile(self, user_score, prompt_results, user_tokens, separate_by_token_count=False):
"""Calculate user's rank and percentile among users with same prompt and token count."""
# Filter to only same prompt and same token count
if separate_by_token_count:
comparable_scores = [r["cosine_distance"] for r in prompt_results
if r["num_user_tokens"] == user_tokens]
else:
comparable_scores = [r["cosine_distance"] for r in prompt_results]
if not comparable_scores:
return None, None
# Sort scores in descending order (higher cosine distance = better creativity)
sorted_scores = sorted(comparable_scores, reverse=True)
# Find rank (1-based)
rank = None
for i, score in enumerate(sorted_scores):
if user_score >= score:
rank = i + 1
break
if rank is None:
rank = len(sorted_scores) + 1
# Calculate percentile (0-100)
if len(sorted_scores) <= 1:
percentile = 50.0
else:
# Compute raw percentile: proportion of users the current user is better than.
raw_percentile = (len(sorted_scores) - rank) / len(sorted_scores) * 100.0
# Ensure percentile is always within 0-100 even if rank is out of expected bounds
percentile = max(0.0, min(100.0, raw_percentile))
return rank, percentile
def create_enhanced_score_display(self, cosine_distance, rank, percentile, user_tokens, same_category_attempts):
"""Create an enhanced score display with progress bars and metrics"""
# Calculate progress bar width (cosine distance is 0-1, so multiply by 100 for percentage)
score_percentage = min(100, cosine_distance * 100)
# Determine score level and message
if cosine_distance >= 0.7:
score_level = "🌟 LEGENDARY CREATIVITY!"
bar_color = "linear-gradient(45deg, #ff6b6b, #ffa726)"
elif cosine_distance >= 0.5:
score_level = "🔥 EXCEPTIONAL CREATIVITY!"
bar_color = "linear-gradient(45deg, #4CAF50, #8bc34a)"
elif cosine_distance >= 0.3:
score_level = "✨ GREAT CREATIVITY!"
bar_color = "linear-gradient(45deg, #2196F3, #03DAC6)"
elif cosine_distance >= 0.15:
score_level = "💡 GOOD CREATIVITY!"
bar_color = "linear-gradient(45deg, #FF9800, #FFC107)"
else:
score_level = "🤔 ROOM FOR IMPROVEMENT"
bar_color = "linear-gradient(45deg, #9E9E9E, #607D8B)"
html_content = f"""
<div class="score-container">
<div class="score-header">{score_level}</div>
<div class="progress-bar-container">
<div class="progress-bar" style="width: {score_percentage}%; background: {bar_color};"></div>
</div>
<div style="text-align: center; margin: 10px 0;">
<span style="font-size: 24px; font-weight: bold;">
Creativity Score: {cosine_distance:.3f}
<span class="help-tooltip">ℹ️
<span class="tooltip-text">
<strong>Creativity Score (0-1):</strong><br/>
Measures how different your influenced response is from the AI's original response.
Higher scores = more creative divergence!<br/><br/>
• 0.7+: Legendary creativity<br/>
• 0.5+: Exceptional creativity<br/>
• 0.3+: Great creativity<br/>
• 0.15+: Good creativity
</span>
</span>
</span>
</div>
<div class="score-details">
"""
if rank is not None and percentile is not None:
html_content += f"""
<div class="score-metric">
<div class="metric-value">#{rank}</div>
<div class="metric-label">Rank out of {same_category_attempts}</div>
</div>
<div class="score-metric">
<div class="metric-value">{percentile:.1f}%</div>
<div class="metric-label">Better than others</div>
</div>
"""
html_content += f"""
<div class="score-metric">
<div class="metric-value">{user_tokens}</div>
<div class="metric-label">Token{'s' if user_tokens != 1 else ''} used
<span class="help-tooltip">ℹ️
<span class="tooltip-text">
<strong>More tokens = Higher potential score!</strong><br/>
Using more tokens gives you more ways to steer me in creative directions,
but the real challenge is making any number of tokens count!
</span>
</span>
</div>
</div>
</div>
"""
# Add achievement titles
achievement_system = AchievementSystem()
achievements = achievement_system.determine_achievement_titles(cosine_distance, user_tokens)
if achievements:
html_content += """
<div class="achievements-container">
<div class="achievements-title">
🏆 Achievements Earned
<span class="help-tooltip">ℹ️
<span class="tooltip-text">
<strong>Achievements:</strong><br/>
Earned based on your performance this session:<br/><br/>
• Creativity level (Genius, Master, Spark, etc.)<br/>
• Token efficiency (Word Wizard, Phrase Magician)<br/>
• Difficulty tackled (Challenge Conqueror)<br/>
• Special combos (One-Word Wonder, Elite Performer)
</span>
</span>
</div>
<div class="achievement-badges">
"""
for title in achievements:
html_content += f'<div class="achievement-badge">{title}</div>'
html_content += """
</div>
</div>
"""
# Add smart contextual tip
smart_tip = self.generate_smart_tip(cosine_distance, user_tokens, rank, percentile)
html_content += f"""
<div class="smart-tip-container">
<span class="smart-tip-icon">💡</span>
<span class="smart-tip-text">{smart_tip}</span>
</div>
<div style="text-align: center; margin-top: 15px; opacity: 0.9; font-size: 14px;">
Higher scores indicate more creative divergence from the original response!
</div>
</div>
"""
return html_content
def generate_smart_tip(self, cosine_distance, user_tokens, rank, percentile):
"""Generate personalized tips based on user performance"""
tips = []
# Performance-based tips
if cosine_distance < 0.1:
tips.append("💡 Try using more unexpected or creative words to steer me in a completely new direction!")
elif cosine_distance < 0.2:
tips.append("✨ Good start! Try thinking more out-of-the-box to maximize your creativity score.")
elif cosine_distance >= 0.5:
tips.append("🌟 Excellent creativity! You're successfully making me generate very different responses.")
# Token efficiency tips
if user_tokens >= 4:
tips.append("⚡ Challenge yourself: try achieving the same creative impact with fewer tokens!")
elif user_tokens == 1 and cosine_distance >= 0.2:
tips.append("🎯 Outstanding! You're a master of single-token creativity.")
elif user_tokens <= 2 and cosine_distance >= 0.3:
tips.append("🏆 Incredible efficiency - maximum creativity with minimal tokens!")
# Ranking tips
if rank and percentile:
if percentile >= 80:
tips.append("👑 You're in the top performers - keep up the amazing work!")
elif percentile >= 50:
tips.append("📈 You're above average! Push a bit further to join the top tier.")
else:
tips.append("🎯 Room to grow - try more unexpected word combinations!")
# Return a random tip if multiple apply, or a default encouragement
if tips:
return random.choice(tips)
else:
return "🎮 Great job playing! Try a new prompt to test different creative strategies."