Spaces:
Sleeping
Sleeping
File size: 11,086 Bytes
d518bf8 3be7282 681b063 3be7282 681b063 3be7282 4efefa6 3be7282 681b063 4efefa6 3be7282 5ab9912 681b063 3be7282 681b063 3be7282 4efefa6 681b063 4efefa6 3be7282 681b063 3be7282 681b063 3be7282 4efefa6 681b063 3be7282 681b063 3be7282 681b063 3be7282 681b063 3be7282 4efefa6 681b063 3be7282 4efefa6 3be7282 681b063 3be7282 681b063 4efefa6 681b063 d518bf8 3be7282 681b063 3be7282 681b063 3be7282 681b063 d518bf8 3be7282 681b063 3be7282 4efefa6 3be7282 4efefa6 681b063 3be7282 681b063 3be7282 4efefa6 3be7282 4efefa6 3be7282 681b063 3be7282 4efefa6 3be7282 d518bf8 3be7282 d518bf8 681b063 3be7282 c026d41 |
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
import gradio as gr
import pandas as pd
from huggingface_hub import HfApi, HfFolder
import os
import uuid
from pathlib import Path
# --- Constants for the Calculation ---
GRUNDWERTE = {"Karo": 9, "Herz": 10, "Pik": 11, "Kreuz": 12, "Grand": 24}
NULL_SPIELE = {"Null": 23, "Null Hand": 35, "Null Ouvert": 46, "Null Hand Ouvert": 59}
SPIELER = ["Kirsten", "Robert", "Samuel"]
# Added a new game type 'Ramsch' to the list of choices
SPIELARTEN = ['Karo', 'Herz', 'Pik', 'Kreuz', 'Grand', 'Null', 'Null Hand', 'Null Ouvert', 'Null Hand Ouvert', 'Ramsch']
HISTORY_DF_HEADERS = ["Kirsten", "Robert", "Samuel", "Spielwert", "Spielart", "Alleinspieler", "Details", "Verloren"]
# --- Hugging Face Configuration ---
LOCAL_TOURNAMENT_DIR = Path("saved_tournaments/")
LOCAL_TOURNAMENT_DIR.mkdir(exist_ok=True)
token = os.getenv('dataset_access_token')
# --- Core Functions ---
def calculate_spielwert(spielart, spitzen_str, hand, schneider, schwarz, schneider_angesagt, schwarz_angesagt, offen):
"""Calculates the pure game value (Reizwert) based on the game rules."""
if spielart in NULL_SPIELE:
return NULL_SPIELE[spielart]
if spielart not in GRUNDWERTE or not spitzen_str:
return 0
spitzen_anzahl = int(spitzen_str.split(" ")[1])
gewinnstufe = 1
if hand: gewinnstufe += 1
if schneider: gewinnstufe += 1
if schneider_angesagt: gewinnstufe += 1
if schwarz: gewinnstufe += 1
if schwarz_angesagt: gewinnstufe += 1
if offen: gewinnstufe += 1
grundwert = GRUNDWERTE[spielart]
return (spitzen_anzahl + gewinnstufe) * grundwert
def spiel_hinzufuegen(history_df, spieler, spielart, spitzen, hand, schneider, schwarz, schneider_angesagt, schwarz_angesagt, offen, verloren, ramsch_verlierer=None, ramsch_punkte=None, ramsch_geschoben=None):
"""Adds a new game to the history DataFrame and dynamically calculates the total scores."""
# --- New logic for 'Ramsch' game type ---
if spielart == 'Ramsch':
if not ramsch_verlierer or not isinstance(ramsch_punkte, (int, float)) or ramsch_punkte <= 0:
# Return current state if Ramsch inputs are invalid or no points were made.
total_scores = history_df[SPIELER].sum() if not history_df.empty else {s: 0 for s in SPIELER}
totals_df = pd.DataFrame([total_scores])
return totals_df, history_df
# Per user request: a negative score for the loser based on their points and how often the skat was passed ("geschoben").
# A standard rule is that each "schieben" doubles the game's value.
# Final score = loser_points * (2 ** number_of_passes). This is robust and handles the 0-pass case correctly.
spielwert = ramsch_punkte * (2 ** ramsch_geschoben)
neue_punkte = {name: 0 for name in SPIELER}
# Only the loser's score is affected, as specified.
neue_punkte[ramsch_verlierer] = -spielwert
details = f"Augen: {int(ramsch_punkte)}, Geschoben: {ramsch_geschoben} mal"
neues_spiel_eintrag = {
"Kirsten": neue_punkte["Kirsten"],
"Robert": neue_punkte["Robert"],
"Samuel": neue_punkte["Samuel"],
"Spielwert": spielwert,
"Spielart": "Ramsch",
"Alleinspieler": "N/A", # No soloist in Ramsch
"Details": details,
"Verloren": "Ja" # A Ramsch game always has a loser
}
new_row_df = pd.DataFrame([neues_spiel_eintrag])
updated_history_df = pd.concat([history_df, new_row_df], ignore_index=True)
total_scores = updated_history_df[SPIELER].sum().astype(int)
totals_df = pd.DataFrame([total_scores])
return totals_df, updated_history_df
# --- Original logic for standard games ---
spielwert = calculate_spielwert(spielart, spitzen, hand, schneider, schwarz, schneider_angesagt, schwarz_angesagt, offen)
neue_punkte = {name: 0 for name in SPIELER}
if spieler:
if not verloren:
neue_punkte[spieler] = 50 + spielwert
for p in SPIELER:
if p != spieler:
neue_punkte[p] = -40
else: # Game lost
neue_punkte[spieler] = -50 - (2 * spielwert)
for p in SPIELER:
if p != spieler:
neue_punkte[p] = 40
details = f"Spitzen: {spitzen or 'N/A'}"
if hand: details += ", Hand"
if schneider_angesagt: details += ", Schneider angesagt"
if schwarz_angesagt: details += ", Schwarz angesagt"
if offen: details += ", Offen"
neues_spiel_eintrag = {
"Kirsten": neue_punkte["Kirsten"],
"Robert": neue_punkte["Robert"],
"Samuel": neue_punkte["Samuel"],
"Spielwert": spielwert,
"Spielart": spielart,
"Alleinspieler": spieler or "N/A",
"Details": details,
"Verloren": "Ja" if verloren else "Nein"
}
new_row_df = pd.DataFrame([neues_spiel_eintrag])
updated_history_df = pd.concat([history_df, new_row_df], ignore_index=True)
total_scores = updated_history_df[SPIELER].sum().astype(int)
totals_df = pd.DataFrame([total_scores])
return totals_df, updated_history_df
def reset_turnier():
"""Resets the entire score and history."""
empty_totals = pd.DataFrame([{"Kirsten": 0, "Robert": 0, "Samuel": 0}])
empty_history = pd.DataFrame(columns=HISTORY_DF_HEADERS)
return empty_totals, empty_history
def save_turnier_to_hf(history_df):
"""Saves the current tournament history to a local JSON file and immediately uploads it to the Hugging Face Hub."""
if HfFolder.get_token() is None:
return "Hugging Face Token nicht gefunden. Bitte via `huggingface-cli login` anmelden."
if history_df.empty:
return "Keine Spieldaten zum Speichern vorhanden."
tournament_filename = f"skat_tournament_{uuid.uuid4()}.jsonl"
local_file_path = LOCAL_TOURNAMENT_DIR / tournament_filename
history_df.to_json(local_file_path, orient="records", lines=True, force_ascii=False)
api = HfApi()
repo_id = "slevis/skat-scores"
try:
api.upload_file(
path_or_fileobj=local_file_path,
path_in_repo=f"data/{tournament_filename}",
repo_id=repo_id,
repo_type="dataset",
commit_message=f"Add new skat tournament data: {tournament_filename}"
)
return f"Turnier erfolgreich nach '{repo_id}' auf Hugging Face hochgeladen."
except Exception as e:
return f"Fehler beim Upload: {e}"
# --- UI Definition with Gradio ---
with gr.Blocks() as demo:
gr.Markdown("# Skat-Punkte-Tracker")
gr.Markdown("### Gesamtpunktestand")
initial_totals_df, initial_history_df = reset_turnier()
totals_output = gr.Dataframe(
value=initial_totals_df,
interactive=False,
show_row_numbers=False
)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Neues Spiel eingeben")
spielart_input = gr.Dropdown(
choices=SPIELARTEN,
label="Spielart"
)
# --- Container for Standard Game Inputs (visible by default) ---
with gr.Group(visible=True) as standard_spiel_group:
gr.Markdown("#### Standard-Spiel")
spieler_input = gr.Dropdown(SPIELER, label="Alleinspieler")
spitzen_input = gr.Dropdown(
choices=[f"{m} {i}" for m in ["Mit", "Ohne"] for i in range(1, 5)],
label="Spitzen"
)
with gr.Row():
schneider_input = gr.Checkbox(label="Schneider")
schwarz_input = gr.Checkbox(label="Schwarz")
verloren_input = gr.Checkbox(label="Verloren?")
with gr.Accordion("Optionen für Handspiele", open=False):
hand_input = gr.Checkbox(label="Handspiel")
schneider_angesagt_input = gr.Checkbox(label="Schneider angesagt")
schwarz_angesagt_input = gr.Checkbox(label="Schwarz angesagt")
offen_input = gr.Checkbox(label="Offen / Ouvert")
# --- Container for Ramsch Game Inputs (hidden by default) ---
with gr.Group(visible=False) as ramsch_spiel_group:
gr.Markdown("#### Ramsch-Spiel")
ramsch_verlierer_input = gr.Dropdown(SPIELER, label="Verlierer")
ramsch_punkte_input = gr.Number(label="Augen des Verlierers", value=0, precision=0)
ramsch_geschoben_input = gr.Dropdown(
choices=[("Keinmal (Standard)", 0), ("1 mal", 1), ("2 mal", 2), ("3 mal", 3)],
value=0,
label="Wie oft wurde der Skat geschoben?"
)
with gr.Row():
speichere_spiel = gr.Button("Speichere Spiel", variant="primary", scale=2)
reset_button = gr.Button("Reset", variant="stop", scale=1)
save_to_hf_button = gr.Button("Turnier auf Hugging Face speichern", variant="secondary")
hf_status_output = gr.Label(value="", label="Upload Status")
with gr.Column(scale=2):
gr.Markdown("### Spielverlauf")
turnier_output = gr.Dataframe(
value=initial_history_df,
headers=HISTORY_DF_HEADERS,
interactive=False,
wrap=True
)
# --- Function to dynamically show/hide input sections ---
def toggle_spielart_inputs(spielart):
if spielart == "Ramsch":
return {
standard_spiel_group: gr.update(visible=False),
ramsch_spiel_group: gr.update(visible=True)
}
else:
return {
standard_spiel_group: gr.update(visible=True),
ramsch_spiel_group: gr.update(visible=False)
}
# --- Event Handlers ---
# Link the dropdown change to the visibility function
spielart_input.change(
fn=toggle_spielart_inputs,
inputs=spielart_input,
outputs=[standard_spiel_group, ramsch_spiel_group]
)
# Consolidate all possible inputs for the click handler
all_inputs = [
turnier_output,
# Standard game inputs
spieler_input, spielart_input, spitzen_input,
hand_input, schneider_input, schwarz_input,
schneider_angesagt_input, schwarz_angesagt_input, offen_input,
verloren_input,
# Ramsch game inputs
ramsch_verlierer_input, ramsch_punkte_input, ramsch_geschoben_input
]
speichere_spiel.click(
fn=spiel_hinzufuegen,
inputs=all_inputs,
outputs=[totals_output, turnier_output]
)
reset_button.click(
fn=reset_turnier,
inputs=[],
outputs=[totals_output, turnier_output]
)
save_to_hf_button.click(
fn=save_turnier_to_hf,
inputs=[turnier_output],
outputs=[hf_status_output]
)
if __name__ == "__main__":
demo.launch(pwa=True) |