- FastAPI backend with PostgreSQL + Redis - 4 core features: Registration, Courts, Matchmaking, Tournament - Double elimination tournament with bracket visualization - ELO-based matchmaking (Stage 1 Open, Stage 2 Skill-Based) - Real-time WebSocket updates - TV Screen display for courts - Vue 3 + Tailwind CSS frontend - Seed data: 12 players, 4 courts, active matches, tournament in progress - Docker compose stack with Nginx reverse proxy
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
"""ELO Rating System for ServeSync"""
|
|
|
|
K_FACTOR = 32 # Standard K-factor
|
|
|
|
|
|
def expected_score(rating_a: float, rating_b: float) -> float:
|
|
"""Calculate expected score for player A vs player B"""
|
|
return 1 / (1 + 10 ** ((rating_b - rating_a) / 400))
|
|
|
|
|
|
def calculate_elo_change(winner_rating: float, loser_rating: float) -> tuple[float, float]:
|
|
"""
|
|
Calculate ELO changes after a match.
|
|
Returns (winner_change, loser_change)
|
|
"""
|
|
expected_winner = expected_score(winner_rating, loser_rating)
|
|
expected_loser = expected_score(loser_rating, winner_rating)
|
|
|
|
winner_change = K_FACTOR * (1 - expected_winner)
|
|
loser_change = K_FACTOR * (0 - expected_loser)
|
|
|
|
return round(winner_change, 2), round(loser_change, 2)
|
|
|
|
|
|
def calculate_team_elo(player1_rating: float, player2_rating: float = None) -> float:
|
|
"""Calculate team ELO (average for doubles)"""
|
|
if player2_rating is None:
|
|
return player1_rating
|
|
return (player1_rating + player2_rating) / 2
|
|
|
|
|
|
def get_tier_from_elo(elo: float) -> str:
|
|
"""Get membership tier based on ELO rating"""
|
|
if elo < 1000:
|
|
return "bronze"
|
|
elif elo < 1200:
|
|
return "silver"
|
|
elif elo < 1500:
|
|
return "gold"
|
|
elif elo < 1800:
|
|
return "platinum"
|
|
else:
|
|
return "elite"
|
|
|
|
|
|
def is_valid_match(player_elo: float, match_min_elo: float, match_max_elo: float) -> bool:
|
|
"""Check if player is within ELO range for skill-based match"""
|
|
return match_min_elo <= player_elo <= match_max_elo
|
|
|
|
|
|
def get_elo_range(player_elo: float, tolerance: float = 200) -> tuple[float, float]:
|
|
"""Get valid ELO range for matching"""
|
|
return (max(0, player_elo - tolerance), player_elo + tolerance)
|