196 lines
6.0 KiB
Python
196 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Update player statistics (ELO, gamesPlayed, wins, losses) based on matches in the database
|
|
"""
|
|
|
|
import sqlite3
|
|
import math
|
|
|
|
DB_PATH = "prisma/prisma/dev.db"
|
|
K_FACTOR = 32 # Standard K-factor for Elo calculations
|
|
|
|
|
|
def get_all_matches():
|
|
"""Get all matches from the database"""
|
|
conn = sqlite3.connect(DB_PATH)
|
|
cursor = conn.cursor()
|
|
cursor.execute("""
|
|
SELECT id, team1P1Id, team1P2Id, team2P1Id, team2P2Id,
|
|
team1Score, team2Score, playedAt
|
|
FROM matches
|
|
ORDER BY playedAt
|
|
""")
|
|
matches = cursor.fetchall()
|
|
conn.close()
|
|
return matches
|
|
|
|
|
|
def get_player(player_id):
|
|
"""Get a player by ID"""
|
|
conn = sqlite3.connect(DB_PATH)
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
"SELECT id, name, currentElo, gamesPlayed, wins, losses FROM players WHERE id = ?",
|
|
(player_id,),
|
|
)
|
|
player = cursor.fetchone()
|
|
conn.close()
|
|
return player
|
|
|
|
|
|
def update_player(player_id, current_elo, games_played, wins, losses):
|
|
"""Update a player's statistics"""
|
|
conn = sqlite3.connect(DB_PATH)
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
"""
|
|
UPDATE players
|
|
SET currentElo = ?, gamesPlayed = ?, wins = ?, losses = ?
|
|
WHERE id = ?
|
|
""",
|
|
(current_elo, games_played, wins, losses, player_id),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
def calculate_elo_change(rating_a, rating_b, score_a, score_b):
|
|
"""Calculate Elo change for a match"""
|
|
# Calculate expected scores
|
|
expected_a = 1 / (1 + math.pow(10, (rating_b - rating_a) / 400))
|
|
expected_b = 1 - expected_a
|
|
|
|
# Actual scores (1 for win, 0.5 for tie, 0 for loss)
|
|
actual_a = 0.5 if score_a == score_b else (1 if score_a > score_b else 0)
|
|
actual_b = 0.5 if score_a == score_b else (1 if score_b > score_a else 0)
|
|
|
|
# Calculate Elo change
|
|
elo_change_a = K_FACTOR * (actual_a - expected_a)
|
|
elo_change_b = K_FACTOR * (actual_b - expected_b)
|
|
|
|
return elo_change_a, elo_change_b
|
|
|
|
|
|
def main():
|
|
print("Updating player statistics based on matches in database...")
|
|
print("=" * 60)
|
|
|
|
# Get all matches
|
|
matches = get_all_matches()
|
|
print(f"Found {len(matches)} matches in database")
|
|
|
|
# Reset all player stats to 0 before recalculating
|
|
conn = sqlite3.connect(DB_PATH)
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
"UPDATE players SET currentElo = 1000, gamesPlayed = 0, wins = 0, losses = 0"
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
print("Reset all player statistics to initial values (ELO: 1000, games: 0)")
|
|
|
|
# Process each match
|
|
match_count = 0
|
|
for (
|
|
match_id,
|
|
team1_p1,
|
|
team1_p2,
|
|
team2_p1,
|
|
team2_p2,
|
|
team1_score,
|
|
team2_score,
|
|
played_at,
|
|
) in matches:
|
|
# Get player data
|
|
p1 = get_player(team1_p1)
|
|
p2 = get_player(team1_p2)
|
|
p3 = get_player(team2_p1)
|
|
p4 = get_player(team2_p2)
|
|
|
|
if not all([p1, p2, p3, p4]):
|
|
print(f"Warning: Could not find all players for match {match_id}")
|
|
continue
|
|
|
|
# Calculate team ratings
|
|
team1_rating = (p1[2] + p2[2]) / 2 # currentElo
|
|
team2_rating = (p3[2] + p4[2]) / 2 # currentElo
|
|
|
|
# Calculate Elo changes
|
|
team1_elo_change, team2_elo_change = calculate_elo_change(
|
|
team1_rating, team2_rating, team1_score, team2_score
|
|
)
|
|
|
|
# Individual Elo changes (split evenly between team members)
|
|
p1_elo_change = team1_elo_change / 2
|
|
p2_elo_change = team1_elo_change / 2
|
|
p3_elo_change = team2_elo_change / 2
|
|
p4_elo_change = team2_elo_change / 2
|
|
|
|
# Determine winners
|
|
team1_won = team1_score > team2_score
|
|
team2_won = team2_score > team1_score
|
|
|
|
# Update player 1 stats
|
|
p1_new_elo = int(p1[2] + p1_elo_change)
|
|
p1_new_games = p1[3] + 1
|
|
p1_new_wins = p1[4] + 1 if team1_won else p1[4]
|
|
p1_new_losses = p1[5] if team1_won else p1[5] + 1
|
|
update_player(p1[0], p1_new_elo, p1_new_games, p1_new_wins, p1_new_losses)
|
|
|
|
# Update player 2 stats
|
|
p2_new_elo = int(p2[2] + p2_elo_change)
|
|
p2_new_games = p2[3] + 1
|
|
p2_new_wins = p2[4] + 1 if team1_won else p2[4]
|
|
p2_new_losses = p2[5] if team1_won else p2[5] + 1
|
|
update_player(p2[0], p2_new_elo, p2_new_games, p2_new_wins, p2_new_losses)
|
|
|
|
# Update player 3 stats
|
|
p3_new_elo = int(p3[2] + p3_elo_change)
|
|
p3_new_games = p3[3] + 1
|
|
p3_new_wins = p3[4] + 1 if team2_won else p3[4]
|
|
p3_new_losses = p3[5] if team2_won else p3[5] + 1
|
|
update_player(p3[0], p3_new_elo, p3_new_games, p3_new_wins, p3_new_losses)
|
|
|
|
# Update player 4 stats
|
|
p4_new_elo = int(p4[2] + p4_elo_change)
|
|
p4_new_games = p4[3] + 1
|
|
p4_new_wins = p4[4] + 1 if team2_won else p4[4]
|
|
p4_new_losses = p4[5] if team2_won else p4[5] + 1
|
|
update_player(p4[0], p4_new_elo, p4_new_games, p4_new_wins, p4_new_losses)
|
|
|
|
match_count += 1
|
|
if match_count % 20 == 0:
|
|
print(f"Processed {match_count}/{len(matches)} matches...")
|
|
|
|
print(f"Processed {match_count} matches")
|
|
|
|
# Display updated player rankings
|
|
print("\n" + "=" * 60)
|
|
print("Top 10 Players by ELO Rating:")
|
|
print("-" * 60)
|
|
|
|
conn = sqlite3.connect(DB_PATH)
|
|
cursor = conn.cursor()
|
|
cursor.execute("""
|
|
SELECT id, name, currentElo, gamesPlayed, wins, losses
|
|
FROM players
|
|
ORDER BY currentElo DESC
|
|
LIMIT 10
|
|
""")
|
|
top_players = cursor.fetchall()
|
|
conn.close()
|
|
|
|
for rank, (player_id, name, elo, games, wins, losses) in enumerate(top_players, 1):
|
|
win_rate = (wins / games * 100) if games > 0 else 0
|
|
print(
|
|
f"{rank:2}. {name:15} | ELO: {elo:4} | Games: {games:3} | W/L: {wins}/{losses} ({win_rate:.1f}%)"
|
|
)
|
|
|
|
print("\n" + "=" * 60)
|
|
print("Player statistics updated successfully!")
|
|
print("Now run the application to see updated rankings.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|