File size: 4,612 Bytes
5b07ff1
 
 
d48eef6
38c6a34
5b07ff1
 
 
d48eef6
5b07ff1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e012a04
5b07ff1
e012a04
 
5b07ff1
e012a04
 
 
 
 
 
 
 
 
 
 
 
 
5b07ff1
 
e012a04
 
5b07ff1
 
 
e012a04
5b07ff1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d48eef6
5b07ff1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9678fdb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import csv
import os
from datetime import datetime

from ..config import FIGHTS_CSV_PATH, FIGHTERS_CSV_PATH

# --- ELO Configuration ---
INITIAL_ELO = 1500
K_FACTOR = 40
# --- End Configuration ---

def calculate_expected_score(rating1, rating2):
    """Calculates the expected score for player 1 against player 2."""
    return 1 / (1 + 10 ** ((rating2 - rating1) / 400))

def update_elo(winner_elo, loser_elo):
    """Calculates the new ELO ratings for a win/loss scenario."""
    expected_win = calculate_expected_score(winner_elo, loser_elo)
    change = K_FACTOR * (1 - expected_win)
    return winner_elo + change, loser_elo - change

def update_elo_draw(elo1, elo2):
    """Calculates the new ELO ratings for a draw."""
    expected1 = calculate_expected_score(elo1, elo2)
    change1 = K_FACTOR * (0.5 - expected1)
    
    expected2 = calculate_expected_score(elo2, elo1)
    change2 = K_FACTOR * (0.5 - expected2)

    return elo1 + change1, elo2 + change2

def process_fights_for_elo(fights_data=FIGHTS_CSV_PATH):
    """
    Processes fights chronologically to calculate ELO scores.
    Accepts either a CSV file path or a pre-loaded list of fights.
    """
    fights = []
    if isinstance(fights_data, str):
        # If a string is passed, treat it as a file path
        if not os.path.exists(fights_data):
            print(f"Error: Fights data file not found at '{fights_data}'.")
            return None
        with open(fights_data, 'r', encoding='utf-8') as f:
            fights = list(csv.DictReader(f))
    elif isinstance(fights_data, list):
        # If a list is passed, use it directly
        fights = fights_data
    else:
        print(f"Error: Invalid data type passed to process_fights_for_elo: {type(fights_data)}")
        return None

    # Sort fights by date to process them in chronological order.
    # This is crucial if loading from a file and a good safeguard if a list is passed.
    try:
        fights.sort(key=lambda x: datetime.strptime(x['event_date'], '%B %d, %Y'))
    except (ValueError, KeyError) as e:
        print(f"Error sorting fights by date: {e}")
        return None
        
    elos = {}
    
    for fight in fights:
        fighter1 = fight.get('fighter_1')
        fighter2 = fight.get('fighter_2')
        winner = fight.get('winner')

        # Initialize ELO for new fighters
        if fighter1 not in elos: elos[fighter1] = INITIAL_ELO
        if fighter2 not in elos: elos[fighter2] = INITIAL_ELO

        elo1 = elos[fighter1]
        elo2 = elos[fighter2]

        if winner == fighter1:
            elos[fighter1], elos[fighter2] = update_elo(elo1, elo2)
        elif winner == fighter2:
            elos[fighter2], elos[fighter1] = update_elo(elo2, elo1)
        elif winner == "Draw":
            elos[fighter1], elos[fighter2] = update_elo_draw(elo1, elo2)
        # NC (No Contest) fights do not affect ELO

    return elos

def add_elo_to_fighters_csv(elos, fighters_csv_path=FIGHTERS_CSV_PATH):
    """
    Adds the final ELO scores as a new column to the fighters CSV data.
    """
    if not elos:
        print("No ELO data to process. Aborting.")
        return
        
    if not os.path.exists(fighters_csv_path):
        print(f"Error: Fighters data file not found at '{fighters_csv_path}'. Cannot add ELO column.")
        return

    rows = []
    with open(fighters_csv_path, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        headers = reader.fieldnames
        # Ensure 'elo' column is added if not present
        if 'elo' not in headers:
            headers.append('elo')
        rows = list(reader)

    for row in rows:
        full_name = f"{row.get('first_name', '')} {row.get('last_name', '')}".strip()
        row['elo'] = round(elos.get(full_name, INITIAL_ELO))

    with open(fighters_csv_path, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=headers)
        writer.writeheader()
        writer.writerows(rows)

    print(f"Successfully updated '{fighters_csv_path}' with ELO ratings.")


def main():
    print("--- Starting ELO Calculation ---")
    final_elos = process_fights_for_elo()

    if final_elos:
        add_elo_to_fighters_csv(final_elos)
        
        # Sort fighters by ELO and print the top 10
        sorted_fighters = sorted(final_elos.items(), key=lambda item: item[1], reverse=True)
        
        print("\n--- Top 10 Fighters by ELO Rating ---")
        for i, (fighter, elo) in enumerate(sorted_fighters[:10]):
            print(f"{i+1}. {fighter}: {round(elo)}")
        print("------------------------------------")