diff --git a/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json b/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json index 93161d0..9963eea 100644 --- a/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json +++ b/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json @@ -1 +1 @@ -{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Verzweifelter Regelvergesser","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file +{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Daniel Nagel","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file diff --git a/data/data_api.py b/data/data_api.py index be3524f..99fde59 100644 --- a/data/data_api.py +++ b/data/data_api.py @@ -375,4 +375,103 @@ def get_match_by_id(match_id): return None # Falls die ID nicht existiert - \ No newline at end of file +from datetime import datetime + +def get_days_since_last_game(player_id): + """ + Sucht das absolut letzte Spiel eines Spielers (über alle Systeme) + und berechnet, wie viele Tage das her ist. + """ + connection = sqlite3.connect(DB_PATH) + cursor = connection.cursor() + + # MAX(last_played) sucht den absolut neuesten Zeitstempel aus allen Einträgen dieses Spielers + query = """ + SELECT MAX(last_played) + FROM player_game_statistic + WHERE player_id = ? + """ + + cursor.execute(query, (player_id,)) + row = cursor.fetchone() + connection.close() + + # row[0] enthält jetzt unseren Zeitstempel (z.B. '2026-03-05 14:30:00') + last_played_str = row[0] if row else None + + # Sicherheitscheck: Hat der Spieler überhaupt schon Einträge? + if not last_played_str: + return None + + try: + # [:19] schneidet Millisekunden ab, damit das Format exakt passt. + last_played_date = datetime.strptime(last_played_str[:19], '%Y-%m-%d %H:%M:%S') + + # 2. Die Differenz zu "Jetzt genau in diesem Moment" berechnen + time_difference = datetime.now() - last_played_date + + # 3. .days holt aus der Zeitdifferenz nur die reinen, vollen Tage heraus + days_ago = time_difference.days + + return { + "date_string": last_played_str[:10], # Nur das Datum (YYYY-MM-DD) für die GUI + "days_ago": days_ago # Die nackte Zahl zum Rechnen (z.B. 14) + } + + except ValueError: + # Falls in der Datenbank mal ein kaputter String steht + return None + + + + +def apply_match_to_player_statistic (player_id, gamesystem_id, mmr_change, scored_points): + connection = sqlite3.connect(DB_PATH) + cursor = connection.cursor() + + # CURRENT_TIMESTAMP setzt die Uhrzeit für das "Letzte Spiel" automatisch auf genau JETZT. + query = """ + UPDATE player_game_statistic + SET + mmr = mmr + ?, + games_in_system = games_in_system + 1, + points = points + ?, + avv_points = (points + ?) / (games_in_system + 1), + last_played = CURRENT_TIMESTAMP + WHERE player_id = ? AND gamesystem_id = ? + """ + + # ACHTUNG: Wir müssen scored_points ZWEIMAL übergeben! + # Einmal für die 'points' und einmal für die Berechnung der 'avv_points'. + cursor.execute(query, (mmr_change, scored_points, scored_points, player_id, gamesystem_id)) + + connection.commit() + connection.close() + + + +def get_leaderboard(system_name): + """Holt alle Spieler eines Systems sortiert nach MMR für die Rangliste.""" + connection = sqlite3.connect(DB_PATH) + connection.row_factory = sqlite3.Row + cursor = connection.cursor() + + # Wir holen ID, Namen, Discord und MMR, sortiert vom höchsten MMR zum niedrigsten + query = """ + SELECT p.id, p.display_name, p.discord_name, stat.mmr + FROM players p + JOIN player_game_statistic stat ON p.id = stat.player_id + JOIN gamesystems sys ON stat.gamesystem_id = sys.id + WHERE sys.name = ? + ORDER BY stat.mmr DESC + """ + + cursor.execute(query, (system_name,)) + rows = cursor.fetchall() + connection.close() + + result = [] + for row in rows: + result.append(dict(row)) + + return result diff --git a/data/setup_database.py b/data/setup_database.py index 0ef0b29..c551ad5 100644 --- a/data/setup_database.py +++ b/data/setup_database.py @@ -60,7 +60,9 @@ def init_db(): score_player1 INTEGER, player2_id INTEGER, score_player2 INTEGER, - played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + mmr_change_winner INTEGER, + mmr_change_looser INTEGER, FOREIGN KEY (gamesystem_id) REFERENCES gamesystems (id), FOREIGN KEY (player1_id) REFERENCES players (id), FOREIGN KEY (player2_id) REFERENCES players (id) @@ -138,7 +140,7 @@ def seed_achievements(): connection.commit() connection.close() print("Achievements angelegt.") - seed_dummy_player() + #seed_dummy_player() def seed_dummy_player(): diff --git a/gui/league_statistic.py b/gui/league_statistic.py index eda8beb..ca9856b 100644 --- a/gui/league_statistic.py +++ b/gui/league_statistic.py @@ -27,16 +27,56 @@ def setup_routes(): # --- BLOCK 1 (2 Karten) --- - with ui.card().classes("w-full"): - with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4"): + # 1. Daten für die Rangliste holen + leaderboard_data = data_api.get_leaderboard(systemname) - with ui.card().classes("items-center justify-center text-center"): + # 2. Tabelle vorbereiten und den EIGENEN Rang herausfinden + table_rows = [] + my_rank = "-" + + for index, player in enumerate(leaderboard_data): + current_rank = index + 1 + + # Wenn wir in der Liste über uns selbst stolpern, merken wir uns den Rang für unsere Karte! + if player['id'] == player_id: + my_rank = current_rank + + table_rows.append({ + 'rank': current_rank, + 'trend': '➖', # Platzhalter für später + 'name': f"{player['display_name']} aka {player['discord_name']}", + 'mmr': player['mmr'] + }) + + table_columns = [ + {'name': 'rank', 'label': '#', 'field': 'rank', 'align': 'left'}, + {'name': 'trend', 'label': 'Trend', 'field': 'trend', 'align': 'center'}, + {'name': 'name', 'label': 'Spieler', 'field': 'name', 'align': 'left'}, + {'name': 'mmr', 'label': 'MMR', 'field': 'mmr', 'align': 'left'} + ] + + # --- BLOCK 1 (Links: MMR & Rang | Rechts: Rangliste) --- + # lg:grid-cols-3 teilt den Bildschirm auf großen Monitoren in 3 gleich große unsichtbare Spalten + with ui.element('div').classes("w-full grid grid-cols-1 lg:grid-cols-3 gap-4 mt-4"): + + # LINKE SEITE (Belegt 1 Spalte) + # flex-col setzt die beiden Karten exakt übereinander + with ui.column().classes("w-full gap-4"): + + with ui.card().classes("w-full items-center justify-center text-center"): ui.label("MMR Punkte: ").classes('text-2xl font-bold') ui.label(str(stats["mmr"])).classes('text-4xl font-bold text-blue-100') - with ui.card().classes("items-center justify-center text-center"): + with ui.card().classes("w-full items-center justify-center text-center"): ui.label("Rang: ").classes('text-2xl font-bold') - ui.label("-").classes('text-4xl font-bold text-blue-100') + # Hier tragen wir jetzt unsere gefundene Platzierung ein! + ui.label(str(my_rank)).classes('text-4xl font-bold text-blue-100') + + # RECHTE SEITE (Belegt 2 Spalten -> lg:col-span-2) + with ui.card().classes("w-full lg:col-span-2"): + ui.label("Liga Rangliste").classes("text-xl font-bold text-white mb-2") + ui.table(columns=table_columns, rows=table_rows, row_key='rank').classes('w-full bg-zinc-900 text-white') + # --- BLOCK 2 (5 Karten) --- with ui.card().classes("w-full"): diff --git a/gui/match_gui.py b/gui/match_gui.py index 5880d2a..78cc17c 100644 --- a/gui/match_gui.py +++ b/gui/match_gui.py @@ -1,7 +1,7 @@ from nicegui import ui, app from gui import gui_style from data import data_api -from mmr_calculations import calc_match +from match_calculations import calc_match def setup_routes(): @@ -11,70 +11,68 @@ def setup_routes(): gui_style.apply_design() # --- SICHERHEITS-CHECK --- - # Prüfen, ob der User wirklich eingeloggt ist. if not app.storage.user.get('authenticated', False): ui.label('Access Denied. Please log in first.').classes('text-red-500') ui.button('Back to Home', on_click=lambda: ui.navigate.to('/')) return - - with ui.card().classes('w-150 items-center mt-10 justify-center'): - ui.label('Neues Spiel für '+ systemname + " eintragen").classes('text-2xl font-bold') - ui.space() + # ÄNDERUNG: w-full (für Handy) + max-w-md (für PC) + mx-auto (Zentrieren) + p-6 (Innenabstand) + with ui.card().classes('w-full max-w-md mx-auto items-center mt-10 p-6 shadow-xl'): - ui.label("Meine Punkte:").classes('text-xl font-bold') + # Text-Center hinzugefügt, falls der Systemname sehr lang ist und auf dem Handy umbricht + ui.label(f'Neues Spiel für {systemname} eintragen').classes('text-2xl font-bold text-center mb-6') - ui.space() + ui.label("Meine Punkte:").classes('text-xl font-bold w-full text-left') + + # ÄNDERUNG: h-60 entfernt, stattdessen gap-6 (Abstand zwischen den Elementen) + with ui.column().classes("w-full items-center gap-6"): - with ui.column().classes("w-full items-center h-60"): # 1. Daten aus der DB holen raw_players = data_api.get_all_players_from_system(systemname) my_id = app.storage.user.get('db_id') - # 3. Eine saubere Optionen-Liste (Dictionary) für NiceGUI bauen + # 3. Eine saubere Optionen-Liste für NiceGUI bauen dropdown_options = {} for p in raw_players: - # FILTERN: Wenn die ID des Spielers MEINE EIGENE ID ist, überspringen wir ihn! if p['player_id'] == my_id: - continue # Geht sofort zum nächsten Spieler in der Schleife - - # Die ID wird der Schlüssel, der kombinierte Name wird der Anzeigetext - # Ergebnis sieht z.B. so aus: { 2: "Dummy Mc DummDumm aka dummy_user" } + continue dropdown_options[p['player_id']] = f"{p['display_name']} aka {p['discord_name']}" - # 4. Eigene Punkte eintragen - p1_points = ui.slider(min= 0, max=100, value = 10).props("label-always") + # ÄNDERUNG: .classes('w-full') hinzugefügt, damit der Slider sich anpasst + p1_points = ui.slider(min=0, max=100, value=10).props("label-always").classes('w-full') - ui.space() + ui.separator().classes('w-full mt-4') # Ein schöner Trennstrich für die Optik - # 5. Das Dropdown mit unseren sauberen Optionen füttern - ui.label("Gegner:").classes('text-xl font-bold') - opponent_select = ui.select(options=dropdown_options, label='Gegner auswählen').classes('w-64') - p2_points = ui.slider(min= 0, max=100, value = 10).props("label-always") + # 5. Dropdown und Gegner Punkte + ui.label("Gegner:").classes('text-xl font-bold w-full text-left') + + # ÄNDERUNG: w-64 durch w-full ersetzt + opponent_select = ui.select(options=dropdown_options, label='Gegner auswählen').classes('w-full') + + # ÄNDERUNG: .classes('w-full') hinzugefügt + p2_points = ui.slider(min=0, max=100, value=10).props("label-always").classes('w-full') ui.space() # Das Match in die Datenbank eintragen lassen und die MMR Berechnung triggern. def input_match_to_database(): - # 1. Prüfen, ob ein Gegner ausgewählt wurde p2_id = opponent_select.value if p2_id is None: ui.notify("Bitte wähle zuerst einen Gegner aus!", color="red", position="top") - return # Bricht die Funktion hier ab! + return - # 2. Punkte von den Slidern holen score_p1 = p1_points.value score_p2 = p2_points.value - # 3. Daten an die API schicken (my_id haben wir oben schon von app.storage.user geholt) match_id = data_api.add_new_match(systemname, my_id, p2_id, score_p1, score_p2) - # 4. Erfolgsmeldung und zurück zur Übersicht + # 4. Erfolgsmeldung und Berechnung ui.notify("Match erfolgreich eingetragen!", color="green") calc_match.calculate_inserted_match(systemname, match_id) ui.navigate.to('/') - with ui.row().classes("w-full items-center justify-between"): - ui.button('Cancel', on_click=lambda: ui.navigate.to('/')).classes('bg-gray-500 text-white') - ui.button(text="Absenden", color="positive", on_click=lambda: input_match_to_database()).classes("") + # Buttons ganz unten in einer Reihe + with ui.row().classes("w-full items-center justify-between mt-8"): + ui.button('Cancel', on_click=lambda: ui.navigate.to('/statistic/{systemname}')).classes('bg-gray-500 text-white') + ui.button(text="Absenden", color="positive", on_click=lambda: input_match_to_database()) diff --git a/match_calculations/calc_match.py b/match_calculations/calc_match.py new file mode 100644 index 0000000..7ad1ef5 --- /dev/null +++ b/match_calculations/calc_match.py @@ -0,0 +1,73 @@ +from data import data_api +from match_calculations import calc_mmr_change +import json +import os + +# Mach die DB abfrage für die Relevanten Daten. Von hier aus werden die "Aufgaben" und Daten dann an die kleineren Berechnungs Funktionen verteilt. +def calculate_inserted_match (gamesystem, match_id): + match_data = data_api.get_match_by_id(match_id) + if not match_data: + print("Fehler: Match nicht gefunden!") + return + # Laden und umsetzen der Match Daten + p1_id = match_data['player1_id'] + p2_id = match_data['player2_id'] + p1_score = match_data['score_player1'] + p2_score = match_data['score_player2'] + sys_name = match_data['gamesystem_name'] + sys_id = match_data['gamesystem_id'] + + rules = load_mmr_rule_matrix(sys_name) + draw_diff = rules["draw_point_difference"] + + calculated = False + winner_id = None + looser_id = None + match_is_draw = False + winner_score = 0 + looser_score = 0 + + + # Abgrenzen ob das Match schon berechnet wurde. Weil ein Draw kann 4 Punkte unterschied haben + # 43-41 ist ein Draw aber rein Mathematisch würde es auch ein anderes if triggern + # Abgrenzen, wer gewonnen hat (if, elif, else Kette) + + # 1. Ist es ein Unentschieden (Draw)? + if -draw_diff <= (p1_score - p2_score) <= draw_diff: + match_is_draw = True + winner_id = p1_id # Bei Draw ist egal, wer wo steht + looser_id = p2_id + winner_score = p1_score + looser_score = p2_score + + # 2. Wenn KEIN Draw: Hat Spieler 1 gewonnen? + elif p1_score > p2_score: + match_is_draw = False + winner_id = p1_id + looser_id = p2_id + winner_score = p1_score + looser_score = p2_score + + # 3. Wenn weder Draw noch P1 Sieg, MUSS P2 gewonnen haben! + else: + match_is_draw = False + winner_id = p2_id + looser_id = p1_id + winner_score = p2_score + looser_score = p1_score + + mmr_change_winner, mmr_change_looser = calc_mmr_change.calc_mmr_change(sys_name, winner_id, looser_id, winner_score, looser_score, match_is_draw, rules) + + data_api.apply_match_to_player_statistic (winner_id, sys_id, mmr_change_winner, winner_score) + data_api.apply_match_to_player_statistic (looser_id, sys_id, mmr_change_looser, looser_score) + + + +def load_mmr_rule_matrix(systemname): + safe_name = systemname.replace(" ", "_").lower() + + file_path = f"match_calculations/mmr_rules_{safe_name}.json" + with open(file_path, "r", encoding="utf-8") as file: + rules = json.load(file) + + return rules diff --git a/match_calculations/calc_mmr_change.py b/match_calculations/calc_mmr_change.py new file mode 100644 index 0000000..99bbf50 --- /dev/null +++ b/match_calculations/calc_mmr_change.py @@ -0,0 +1,65 @@ +from data import data_api + +# Faktor für die Punkte Inflation. Um diesen Wert verliert der Verlierer weniger Punkte als der Sieger bekommt. Über Kurz oder Lang werden +# die meisten Spieler über 1000MMR sein. Sprich: Neueinsteiger, oder leute die weniger spielen sind eher im unteren Ende als in der Mitte. +point_inflation = 0.7 # => entspricht % ! z.B. 0.7 = 70% + +def calc_mmr_change(systemname, winner_id, looser_id, winner_points, looser_points, match_is_draw, rules): + #Rang der Spieler holen. + gamesystem_id = data_api.get_gamesystem_id_by_name(systemname) + winner_rank = data_api.get_player_rank(winner_id,gamesystem_id) + looser_rank = data_api.get_player_rank(looser_id, gamesystem_id) + + if match_is_draw: + mmr_change = rules["rank_matrix"][str(max(-10, min(10, winner_rank-looser_rank)))]["draw"] + else: + mmr_change = rules["rank_matrix"][str(max(-10, min(10, winner_rank-looser_rank)))]["win"] + + # Slaanesh Points berechnen und dem Change hinzufügen. + mmr_change += (sla_points := slaanesh_delight(winner_points, looser_points, rules)) + + # Variablen für den mmr_change anlegen. Sieger-Verlierer sind unterschiedlich! + winner_mmr_change = 0 + (mmr_change + wrath_of_khorne(winner_id)) + looser_mmr_change = 0 + (mmr_change + wrath_of_khorne(looser_id)) + + if not match_is_draw: + # Verlierer verliert nur einen Teil der Punkte + looser_mmr_change = -int(winner_mmr_change * point_inflation) + + return winner_mmr_change, looser_mmr_change + + + + +# ----------------- +khorne_days = 16 +khorne_bonus = 8 + + +# ----------------- +def wrath_of_khorne(player_id): + last_played = data_api.get_days_since_last_game(player_id)["days_ago"] + if last_played <= khorne_days: + return khorne_bonus + else: + return 0 + + +def slaanesh_delight(winner_points, looser_points, rules): + + point_diff = winner_points - looser_points + # Standardwert, falls gar nichts zutrifft + bonus = 0 + + for threshold in rules["score_bonus"]: + if point_diff >= threshold["min_diff"]: + bonus = threshold["bonus"] + break + return bonus + + +def tzeentch_scemes(): + print("k") + +def nurgles_entropy(): + print("k") \ No newline at end of file diff --git a/match_calculations/mmr_rules_spearhead.json b/match_calculations/mmr_rules_spearhead.json new file mode 100644 index 0000000..4140846 --- /dev/null +++ b/match_calculations/mmr_rules_spearhead.json @@ -0,0 +1,113 @@ +{ + "system_info": "Balancing für Spearhead", + "draw_point_difference": 5, + "rank_matrix": { + "10": { + "win": 10, + "draw": 30 + }, + "9": { + "win": 10, + "draw": 30 + }, + "8": { + "win": 10, + "draw": 20 + }, + "7": { + "win": 10, + "draw": 20 + }, + "6": { + "win": 20, + "draw": 10 + }, + "5": { + "win": 20, + "draw": 10 + }, + "4": { + "win": 20, + "draw": 0 + }, + "3": { + "win": 20, + "draw": 0 + }, + "2": { + "win": 30, + "draw": 0 + }, + "1": { + "win": 30, + "draw": 0 + }, + "0": { + "win": 30, + "draw": 0 + }, + "-1": { + "win": 30, + "draw": 0 + }, + "-2": { + "win": 40, + "draw": 0 + }, + "-3": { + "win": 40, + "draw": -10 + }, + "-4": { + "win": 50, + "draw": -20 + }, + "-5": { + "win": 60, + "draw": -20 + }, + "-6": { + "win": 70, + "draw": -20 + }, + "-7": { + "win": 80, + "draw": -50 + }, + "-8": { + "win": 100, + "draw": -50 + }, + "-9": { + "win": 120, + "draw": -60 + }, + "-10": { + "win": 150, + "draw": -60 + } + }, + "score_bonus": [ + { + "min_diff": 15, + "bonus": 40 + }, + { + "min_diff": 10, + "bonus": 30 + }, + { + "min_diff": 6, + "bonus": 20 + }, + { + "min_diff": 3, + "bonus": 10 + }, + { + "min_diff": 0, + "bonus": 0 + } + ] + +} \ No newline at end of file diff --git a/match_calculations/mmr_rules_warhammer_40k.json b/match_calculations/mmr_rules_warhammer_40k.json new file mode 100644 index 0000000..892fe7d --- /dev/null +++ b/match_calculations/mmr_rules_warhammer_40k.json @@ -0,0 +1,112 @@ +{ + "system_info": "Balancing für Warhammer 40k", + "draw_point_difference": 5, + "rank_matrix": { + "10": { + "win": 10, + "draw": 30 + }, + "9": { + "win": 10, + "draw": 30 + }, + "8": { + "win": 10, + "draw": 20 + }, + "7": { + "win": 10, + "draw": 20 + }, + "6": { + "win": 20, + "draw": 10 + }, + "5": { + "win": 20, + "draw": 10 + }, + "4": { + "win": 20, + "draw": 0 + }, + "3": { + "win": 20, + "draw": 0 + }, + "2": { + "win": 30, + "draw": 0 + }, + "1": { + "win": 30, + "draw": 0 + }, + "0": { + "win": 30, + "draw": 0 + }, + "-1": { + "win": 30, + "draw": 0 + }, + "-2": { + "win": 40, + "draw": 0 + }, + "-3": { + "win": 40, + "draw": -10 + }, + "-4": { + "win": 50, + "draw": -20 + }, + "-5": { + "win": 60, + "draw": -20 + }, + "-6": { + "win": 70, + "draw": -20 + }, + "-7": { + "win": 80, + "draw": -50 + }, + "-8": { + "win": 100, + "draw": -50 + }, + "-9": { + "win": 120, + "draw": -60 + }, + "-10": { + "win": 150, + "draw": -60 + } + }, + "score_bonus": [ + { + "min_diff": 95, + "bonus": 40 + }, + { + "min_diff": 85, + "bonus": 30 + }, + { + "min_diff": 65, + "bonus": 20 + }, + { + "min_diff": 35, + "bonus": 10 + }, + { + "min_diff": 0, + "bonus": 0 + } + ] +} \ No newline at end of file diff --git a/match_calculations/mmr_rules_warhammer_age_of_sigmar.json b/match_calculations/mmr_rules_warhammer_age_of_sigmar.json new file mode 100644 index 0000000..cd836e3 --- /dev/null +++ b/match_calculations/mmr_rules_warhammer_age_of_sigmar.json @@ -0,0 +1,113 @@ +{ + "system_info": "Balancing für Warhammer Age of Sigmar", + "draw_point_difference": 5, + "rank_matrix": { + "10": { + "win": 10, + "draw": 30 + }, + "9": { + "win": 10, + "draw": 30 + }, + "8": { + "win": 10, + "draw": 20 + }, + "7": { + "win": 10, + "draw": 20 + }, + "6": { + "win": 20, + "draw": 10 + }, + "5": { + "win": 20, + "draw": 10 + }, + "4": { + "win": 20, + "draw": 0 + }, + "3": { + "win": 20, + "draw": 0 + }, + "2": { + "win": 30, + "draw": 0 + }, + "1": { + "win": 30, + "draw": 0 + }, + "0": { + "win": 30, + "draw": 0 + }, + "-1": { + "win": 30, + "draw": 0 + }, + "-2": { + "win": 40, + "draw": 0 + }, + "-3": { + "win": 40, + "draw": -10 + }, + "-4": { + "win": 50, + "draw": -20 + }, + "-5": { + "win": 60, + "draw": -20 + }, + "-6": { + "win": 70, + "draw": -20 + }, + "-7": { + "win": 80, + "draw": -50 + }, + "-8": { + "win": 100, + "draw": -50 + }, + "-9": { + "win": 120, + "draw": -60 + }, + "-10": { + "win": 150, + "draw": -60 + } + }, + "score_bonus": [ + { + "min_diff": 40, + "bonus": 40 + }, + { + "min_diff": 30, + "bonus": 30 + }, + { + "min_diff": 20, + "bonus": 20 + }, + { + "min_diff": 10, + "bonus": 10 + }, + { + "min_diff": 0, + "bonus": 0 + } + ] + +} \ No newline at end of file diff --git a/mmr_calculations/calc_match.py b/mmr_calculations/calc_match.py deleted file mode 100644 index 660b035..0000000 --- a/mmr_calculations/calc_match.py +++ /dev/null @@ -1,62 +0,0 @@ -from data import data_api -from mmr_calculations import * - -# Mach die DB abfrage für die Relevanten Daten. Von hier aus werden die "Aufgaben" und Daten dann an die kleineren Berechnungs Funktionen verteilt. -def calculate_inserted_match (gamesystem, match_id): - match_data = data_api.get_match_by_id(match_id) - if not match_data: - print("Fehler: Match nicht gefunden!") - return - # Laden und umsetzen der Match Daten - p1_id = match_data['player1_id'] - p2_id = match_data['player2_id'] - p1_score = match_data['score_player1'] - p2_score = match_data['score_player2'] - sys_name = match_data['gamesystem_name'] - sys_id = match_data['gamesystem_id'] - - rules = load_mmr_rule_matrix(systemname) - draw_diff = rules["draw_point_difference"] - - calculated = False - winner_id = None - looser_id = None - match_is_draw = False - winner_score = 0 - looser_score = 0 - - # Abgrenzen ob das Match schon berechnet wurde. Weil ein Draw kan 4 Punkte unterschied haben - # 43-41 ist ein Draw aber rein Mathematisch würde es auch ein anderes if triggern - while not calculated: - # Match is a Draw - if -draw_diff <= (p1_score-p2_score) <= draw_diff: - match_is_draw = True - winner_id = p1_id - looser_id = p2_id - winner_score = p1_score - looser_score = p2_score - - calculated = True - break - - # p1 ist der Sieger. - if score_p1 > score_p2: - winner_id = p1_id - looser_id = p2_id - winner_score = p1_score - looser_score = p2_score - - calculated = True - break - - # p2 ist der Sieger. - if score_p1 < score_p2: - winner_id = p2_id - looser_id = p1_id - winner_score = p2_score - looser_score = p1_score - - calculated = True - break - - calc_mmr_change.calc_mmr_change(sys_name, winner_id, looser_id, winner_score, looser_score, match_is_draw) \ No newline at end of file diff --git a/mmr_calculations/calc_mmr_change.py b/mmr_calculations/calc_mmr_change.py deleted file mode 100644 index 0a3bf80..0000000 --- a/mmr_calculations/calc_mmr_change.py +++ /dev/null @@ -1,29 +0,0 @@ -from data import data_api -import json -import os - -def calc_mmr_change(systemname, winner_id, looser_id, winner_points, looser_points, match_is_draw): - gamesystem_id = data_api.get_gamesystem_id_by_name(systemname) - winner_rank = data_api.get_player_rank(winner_id,gamesystem_id) - looser_rank = data_api.get_player_rank(looser_id, gamesystem_id) - - rules = load_mmr_rule_matrix(systemname) - - if match_is_draw: - mmr_change = rules["rank_matrix"][str(winner_rank-looser_rank)]["draw"] - - else: - mmr_change = rules["rank_matrix"][str(winner_rank-looser_rank)]["win"] - - - - -def load_mmr_rule_matrix(systemname): - safe_name = systemname.replace(" ", "_").lower() - - file_path = f"mmr_calculations/mmr_rules_{safe_name}.json" - with open(file_path, "r", encoding="utf-8") as file: - rules = json.load(file) - - return rules -