File size: 7,593 Bytes
7c13c18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import pandas as pd
import numpy as np
from scipy.stats import poisson
import gradio as gr
import os
import matplotlib.pyplot as plt
import seaborn as sns
import requests
import json

# --- 1. แƒ™แƒแƒœแƒคแƒ˜แƒ’แƒฃแƒ แƒแƒชแƒ˜แƒ ---
GEMINI_API_KEY = "AIzaSyBe9TNXLWuZO995kKxbj4KvqjSLhZcJwvo"

# --- 2. แƒ›แƒแƒœแƒแƒชแƒ”แƒ›แƒ”แƒ‘แƒ˜แƒก แƒ›แƒแƒ›แƒ–แƒแƒ“แƒ”แƒ‘แƒ ---
file_path = 'top_50_european_teams_2026.csv'
if os.path.exists(file_path):
    df = pd.read_csv(file_path, sep=None, engine='python')
    df.columns = df.columns.str.strip()
else:
    data = {
        'team_nam': [
            'Real Madrid', 'Manchester City', 'Bayern Munich',
            'FC Barcelona', 'Arsenal', 'PSG', 'Inter Milan', 'Liverpool'
        ],
        'fifa_elo': [1950, 1980, 1890, 1910, 1880, 1860, 1870, 1885],
        'last_10_w': [8, 7, 6, 7, 8, 6, 7, 5],
        'win_vs_high': [3, 4, 2, 3, 2, 1, 2, 1],
        'injuries': [1, 0, 3, 1, 2, 4, 1, 2]
    }
    df = pd.DataFrame(data)

# --- 3. แƒจแƒ”แƒœแƒ˜ แƒ’แƒแƒแƒœแƒ’แƒแƒ แƒ˜แƒจแƒ”แƒ‘แƒ˜แƒก แƒคแƒฃแƒœแƒฅแƒชแƒ˜แƒ”แƒ‘แƒ˜ (แƒฃแƒชแƒ•แƒšแƒ”แƒšแƒ˜) ---
def plot_score_heatmap(matrix, home_name, away_name):
    fig, ax = plt.subplots(figsize=(8, 6))
    sns.heatmap(
        matrix,
        annot=True,
        fmt=".1%",
        cmap="YlGnBu",
        cbar_kws={'label': 'แƒแƒšแƒ‘แƒแƒ—แƒแƒ‘แƒ'},
        ax=ax
    )
    ax.set_title(f"แƒ–แƒฃแƒกแƒขแƒ˜ แƒแƒœแƒ’แƒแƒ แƒ˜แƒจแƒ˜แƒก แƒแƒšแƒ‘แƒแƒ—แƒแƒ‘แƒ”แƒ‘แƒ˜: {home_name} vs {away_name}")
    ax.set_xlabel(f"{away_name} (แƒกแƒขแƒฃแƒ›แƒแƒ แƒ˜) แƒ’แƒแƒšแƒ”แƒ‘แƒ˜")
    ax.set_ylabel(f"{home_name} (แƒ›แƒแƒกแƒžแƒ˜แƒœแƒซแƒ”แƒšแƒ˜) แƒ’แƒแƒšแƒ”แƒ‘แƒ˜")
    ax.set_xticks(np.arange(0.5, 8.5, 1))
    ax.set_xticklabels(range(8))
    ax.set_yticks(np.arange(0.5, 8.5, 1))
    ax.set_yticklabels(range(8))
    plt.tight_layout()
    plot_path = "score_matrix_heatmap.png"
    plt.savefig(plot_path, dpi=120)
    plt.close(fig)
    return plot_path

def calculate_balanced_odds_with_plot(home_team, away_team):
    if home_team == away_team:
        return "แƒแƒ˜แƒ แƒฉแƒ˜แƒ”แƒ— แƒ’แƒแƒœแƒกแƒฎแƒ•แƒแƒ•แƒ”แƒ‘แƒฃแƒšแƒ˜ แƒ’แƒฃแƒœแƒ“แƒ”แƒ‘แƒ˜", 0, 0, 0, None

    try:
        h = df[df['team_nam'] == home_team].iloc[0]
        a = df[df['team_nam'] == away_team].iloc[0]
    except IndexError:
        return "แƒ’แƒฃแƒœแƒ“แƒ˜ แƒแƒ  แƒ›แƒแƒ˜แƒซแƒ”แƒ‘แƒœแƒ", 0, 0, 0, None

    def get_conservative_power(row):
        base_elo = row['fifa_elo'] / 400
        form_bonus = np.log1p(row['last_10_w']) * 0.15
        giant_killer = row['win_vs_high'] * 0.05
        injury_penalty = row['injuries'] * 0.05
        return base_elo + form_bonus + giant_killer - injury_penalty

    h_power = get_conservative_power(h)
    a_power = get_conservative_power(a)

    diff = h_power - a_power
    l1 = max(0.6, 1.35 + diff * 0.8 + 0.2)
    l2 = max(0.6, 1.35 - diff * 0.8)

    max_goals = 8
    home_probs = poisson.pmf(range(max_goals), l1)
    away_probs = poisson.pmf(range(max_goals), l2)
    matrix = np.outer(home_probs, away_probs)

    plot_file = plot_score_heatmap(matrix, home_team, away_team)

    p_win = np.sum(np.tril(matrix, -1))
    p_draw = np.sum(np.diag(matrix))
    p_lose = np.sum(np.triu(matrix, 1))

    total = p_win + p_draw + p_lose
    if total == 0: total = 1
    p_win, p_draw, p_lose = p_win / total, p_draw / total, p_lose / total

    margin = 0.06
    o1 = round(1 / (p_win + margin / 3), 2) if p_win > 0 else 10.0
    ox = round(1 / (p_draw + margin / 3), 2) if p_draw > 0 else 10.0
    o2 = round(1 / (p_lose + margin / 3), 2) if p_lose > 0 else 10.0

    return f"xG {l1:.2f} - {l2:.2f}", max(o1, 1.15), max(ox, 1.15), max(o2, 1.15), plot_file

# --- 4. แƒฉแƒแƒขแƒ˜ (แƒจแƒ”แƒœแƒ˜ แƒ›แƒแƒ—แƒฎแƒแƒ•แƒœแƒ˜แƒšแƒ˜ แƒคแƒฃแƒœแƒฅแƒชแƒ˜แƒ) ---
def football_chat_bot(message, history):
    api_key = GEMINI_API_KEY.strip()
    list_url = f"https://generativelanguage.googleapis.com/v1/models?key={api_key}"
    try:
        models_res = requests.get(list_url).json()
        working_models = [m['name'] for m in models_res.get('models', [])
                          if 'generateContent' in m.get('supportedGenerationMethods', [])]
        if not working_models:
            return "แƒจแƒ”แƒชแƒ“แƒแƒ›แƒ: แƒ—แƒฅแƒ•แƒ”แƒœแƒก API แƒ’แƒแƒกแƒแƒฆแƒ”แƒ‘แƒ–แƒ” แƒแƒ แƒชแƒ”แƒ แƒ—แƒ˜ แƒ›แƒแƒ“แƒ”แƒšแƒ˜ แƒแƒ  แƒแƒ แƒ˜แƒก แƒ’แƒแƒแƒฅแƒขแƒ˜แƒฃแƒ แƒ”แƒ‘แƒฃแƒšแƒ˜."
        target_model = working_models[0]
    except Exception as e:
        return f"แƒ›แƒแƒ“แƒ”แƒšแƒ”แƒ‘แƒ˜แƒก แƒซแƒ˜แƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒชแƒ“แƒแƒ›แƒ: {str(e)}"

    chat_url = f"https://generativelanguage.googleapis.com/v1/{target_model}:generateContent?key={api_key}"
    headers = {'Content-Type': 'application/json'}
    data = {
        "contents": [{"parts": [{"text": f"แƒจแƒ”แƒœ แƒฎแƒแƒ  แƒคแƒ”แƒฎแƒ‘แƒฃแƒ แƒ—แƒ˜แƒก แƒ”แƒฅแƒกแƒžแƒ”แƒ แƒขแƒ˜ 2026 แƒฌแƒ”แƒšแƒก. แƒฃแƒžแƒแƒกแƒฃแƒฎแƒ” แƒฅแƒแƒ แƒ—แƒฃแƒšแƒแƒ“: {message}"}]}]
    }

    try:
        response = requests.post(chat_url, headers=headers, data=json.dumps(data))
        res_json = response.json()
        if response.status_code == 200:
            return res_json['candidates'][0]['content']['parts'][0]['text']
        else:
            return f"API แƒฃแƒแƒ แƒ˜ ({target_model}): {res_json.get('error', {}).get('message', 'แƒฃแƒชแƒœแƒแƒ‘แƒ˜ แƒจแƒ”แƒชแƒ“แƒแƒ›แƒ')}"
    except Exception as e:
        return f"แƒ™แƒแƒ•แƒจแƒ˜แƒ แƒ˜แƒก แƒจแƒ”แƒชแƒ“แƒแƒ›แƒ: {str(e)}"

# --- 5. Gradio แƒ˜แƒœแƒขแƒ”แƒ แƒคแƒ”แƒ˜แƒกแƒ˜ ---
teams_list = sorted(df['team_nam'].unique())
start_home = 'Real Madrid' if 'Real Madrid' in teams_list else teams_list[0]
start_away = 'FC Barcelona' if 'FC Barcelona' in teams_list else teams_list[1]

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    with gr.Row():
        gr.Image(
            "https://images.eu.ctfassets.net/psnuheg7hu1m/2855AOJwV8YSxZmk2TfF5T/1d0af7cd5353cd782e217f91e3bfc77b/TBC-ge.png?fm=jpg&fl=progressive&q=90",
            show_label=False, height=100, width=200)
        gr.Markdown("# โšฝ Football AI Hub 2026")

    with gr.Tabs():
        with gr.TabItem("๐Ÿ“Š แƒžแƒ แƒแƒ’แƒœแƒแƒ–แƒ˜ แƒ“แƒ แƒ›แƒแƒขแƒ แƒ˜แƒชแƒ"):
            gr.Markdown("แƒแƒ˜แƒ แƒฉแƒ˜แƒ”แƒ— แƒ’แƒฃแƒœแƒ“แƒ”แƒ‘แƒ˜ AI แƒ›แƒแƒ“แƒ”แƒšแƒ˜แƒก แƒแƒœแƒแƒšแƒ˜แƒ–แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก")
            with gr.Row():
                with gr.Column(scale=1):
                    h_drop = gr.Dropdown(teams_list, label="แƒ›แƒแƒกแƒžแƒ˜แƒœแƒซแƒ”แƒšแƒ˜", value=start_home)
                    a_drop = gr.Dropdown(teams_list, label="แƒกแƒขแƒฃแƒ›แƒแƒ แƒ˜", value=start_away)
                    btn = gr.Button("๐Ÿ“Š แƒแƒœแƒแƒšแƒ˜แƒ–แƒ˜แƒก แƒ“แƒแƒฌแƒงแƒ”แƒ‘แƒ", variant="primary")
                    xg_val = gr.Textbox(label="แƒ›แƒแƒกแƒแƒšแƒแƒ“แƒœแƒ”แƒšแƒ˜ แƒ’แƒแƒšแƒ”แƒ‘แƒ˜ (xG)")
                    out_1 = gr.Number(label="1 (Home Win)")
                    out_x = gr.Number(label="X (Draw)")
                    out_2 = gr.Number(label="2 (Away Win)")
                with gr.Column(scale=2):
                    matrix_plot = gr.Image(label="แƒ–แƒฃแƒกแƒขแƒ˜ แƒแƒœแƒ’แƒแƒ แƒ˜แƒจแƒ˜แƒก แƒ›แƒแƒขแƒ แƒ˜แƒชแƒ")

        with gr.TabItem("๐Ÿ’ฌ แƒฉแƒแƒขแƒ˜ แƒ”แƒฅแƒกแƒžแƒ”แƒ แƒขแƒ—แƒแƒœ"):
            gr.ChatInterface(fn=football_chat_bot)

    btn.click(
        calculate_balanced_odds_with_plot,
        inputs=[h_drop, a_drop],
        outputs=[xg_val, out_1, out_x, out_2, matrix_plot]
    )

if __name__ == "__main__":
    demo.launch()