# Einheiten: backend/tft/sim/player.py stand: f41ac76b5789 ## backend/tft/sim/player.py::InvalidAction beschreibung: Markiert ungültige Spielaktionen als Ausnahme. input: eine Fehlermeldung als Zeichenkette. output: eine Exception, die von Aufrufern abgefangen wird. entscheidungen: - Wird als eigene Exception-Klasse statt generischer `Exception` definiert, damit Aufrufer gezielt reagieren können. beleg: "class InvalidAction(Exception):" - Enthält bewusst keine eigene Logik, sondern dient nur als Typ-Marker. beleg: " pass" kanten: - wird-genutzt-von: backend/tft/sim/player.py::buy - wird-genutzt-von: backend/tft/sim/player.py::sell - wird-genutzt-von: backend/tft/sim/player.py::buy_xp - wird-genutzt-von: backend/tft/sim/player.py::move - wird-genutzt-von: backend/tft/sim/player.py::equip - wird-genutzt-von: backend/tft/sim/player.py::pick_augment ## backend/tft/sim/player.py::PlayerState beschreibung: Hält den kompletten Zustand eines Spielers über alle Runden hinweg. input: Initialwerte für Name, Archetyp, HP, Gold und Level. output: ein Datensatz-Objekt mit Board, Bench, Items, Augments und Shop. entscheidungen: - Verwendet `field(default_factory=list)` für mutable Defaults, um die geteilte-Liste-Falle zu vermeiden. beleg: " board: list = field(default_factory=list)" - `alive` ist als abgeleitete Property modelliert, damit Konsistenz mit `hp` garantiert ist. beleg: " return self.hp > 0" - Optionale Felder wie `placement` und `last_opponent` werden explizit typisiert, damit Phasen ohne Gegner sauber abgebildet werden. beleg: " placement: int | None = None" kanten: - nutzt: backend/tft/sim/player.py::score - wird-genutzt-von: backend/tft/sim/player.py::new_player ## backend/tft/sim/player.py::new_player beschreibung: Erzeugt einen frischen `PlayerState` aus einer Konfiguration. input: Name, Archetyp und ein Konfigurations-Dictionary. output: ein neu initialisierter `PlayerState` mit Startwerten. entscheidungen: - Liest Startwerte aus `cfg`, um Regelvarianten ohne Code-Änderung zu erlauben. beleg: " hp=cfg[\"damage\"][\"player_hp\"]," - Fallback auf Level 1, falls `start_level` fehlt. beleg: " level=cfg[\"xp\"].get(\"start_level\", 1)," kanten: - ruft-auf: backend/tft/sim/player.py::PlayerState - nutzt: backend/tft/sim/player.py::PlayerState ## backend/tft/sim/player.py::score beschreibung: Berechnet die Spielstärke eines Spielers für die Schaden-Berechnung. input: ein Spielerzustand und ein Artifact-Dictionary. output: ein Float-Score des aktuellen Boards inklusive Augments. entscheidungen: - Delegiert die eigentliche Berechnung an `score_board`, um die Bewertungslogik zentral zu halten. beleg: " return score_board(p.board, p.augments, artifact)" - Bezieht Augments in den Score ein, damit gewählte Bonüsse kampfrelevant zählen. beleg: " return score_board(p.board, p.augments, artifact)" kanten: ## backend/tft/sim/player.py::unit_worth beschreibung: Liefert den ökonomischen Gegenwert einer Einheit für Aufstellungsentscheidungen. input: Artifact-Dictionary und eine Einheit mit `api_name` und `stars`. output: ein Float-Wert als Kombination aus Kosten und Sternen. entscheidungen: - Skaliert den Wert mit `3 ** (stars - 1)`, da Merge-Stufen das Vielfache an Einheiten ersetzen. beleg: " return artifact[\"static\"][\"units\"][u[\"api_name\"]][\"cost\"] * 3 ** (u[\"stars\"] - 1)" - Liest Einheitenkosten aus den statischen Artifact-Daten, damit Balance-Anpassungen ohne Code-Change greifen. beleg: " return artifact[\"static\"][\"units\"][u[\"api_name\"]][\"cost\"] * 3 ** (u[\"stars\"] - 1)" kanten: - wird-genutzt-von: backend/tft/sim/player.py::fill_board ## backend/tft/sim/player.py::refresh_shop beschreibung: Erneuert die Shop-Anzeige eines Spielers mit neuen Roll-Ergebnissen. input: Spielerzustand, Pool, Konfiguration und RNG. output: keine Rückgabe; der Shop des Spielers wird ersetzt. entscheidungen: - Überschreibt `p.shop` vollständig statt zu mischen, um das TFT-typische „neuer Roll" zu modellieren. beleg: " p.shop = roll(pool, p.level, cfg, rng)" kanten: - wird-genutzt-von: backend/tft/sim/player.py::reroll ## backend/tft/sim/player.py::merge beschreibung: Führt automatisch passende Einheiten gleicher Sternstufe zu höheren Sternen zusammen. input: Spielerzustand, API-Name und aktuelle Sternstufe. output: keine Rückgabe; Board und Bench werden konsolidiert. entscheidungen: - Iteriert mit `while True`, bis weniger als drei Kopien vorhanden sind. beleg: " while True:" - Übernimmt die Items der fusionierten Kopien auf die verbleibende Einheit. beleg: " keep[\"items\"].extend(u[\"items\"])" - Kürzt das Item-Inventar der behaltenen Einheit auf das maximale Limit. beleg: " keep[\"items\"] = keep[\"items\"][:MAX_ITEMS_PER_UNIT]" - Entfernt überschüssige Einheiten aus Board oder Bench je nach Herkunft. beleg: " (p.board if u in p.board else p.bench).remove(u)" kanten: - wird-genutzt-von: backend/tft/sim/player.py::buy - wird-genutzt-von: backend/tft/sim/player.py::grab_carousel_unit ## backend/tft/sim/player.py::buy beschreibung: Kauft eine Einheit aus dem Shop und legt sie auf die Bank. input: Spielerzustand, Shop-Slot, Pool und Artifact-Dictionary. output: keine Rückgabe; Bench und Pool werden verändert. entscheidungen: - Prüft Shop-Slot, Gold, Bank-Kapazität und Pool-Verfügbarkeit in dieser Reihenfolge, um Mehrfach-Meldungen pro Aktion zu vermeiden. beleg: " if not (0 <= slot < len(p.shop)) or p.shop[slot] is None:" - Leert den Shop-Slot nach erfolgreichem Kauf explizit. beleg: " p.shop[slot] = None" - Startet sofort einen Merge-Versuch auf Sternstufe 1, damit Käufe direkt stapeln. beleg: " merge(p, api, 1)" kanten: - ruft-auf: backend/tft/sim/player.py::merge - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::sell beschreibung: Verkauft eine Einheit vom Board oder Bench und erstattet Gold und Items zurück. input: Spielerzustand, Ort, Index, Pool und Artifact-Dictionary. output: keine Rückgabe; Gold, Items und Pool werden aktualisiert. entscheidungen: - Berechnet die Anzahl der Pool-Copies exponentiell zur Sternstufe. beleg: " copies = 3 ** (u[\"stars\"] - 1)" - Zieht 1 Gold ab, sobald die Einheit nicht 1 kostet und mindestens 2 Sterne hat. beleg: " if cost > 1 and u[\"stars\"] >= 2:" - Gibt Items aus dem Inventar der Einheit in das Spieler-Inventar frei. beleg: " p.items.extend(u[\"items\"])" kanten: - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::reroll beschreibung: Würfelt den Shop gegen Gold neu aus. input: Spielerzustand, Pool, Konfiguration und RNG. output: keine Rückgabe; der Shop wird aktualisiert. entscheidungen: - Prüft Gold erst nach dem Lesen der Kosten, damit einheitliche Fehlermeldungen entstehen. beleg: " if p.gold < cost:" - Delegiert das eigentliche Neu-Würfeln an `refresh_shop`, um Logik-Duplikation zu vermeiden. beleg: " refresh_shop(p, pool, cfg, rng)" kanten: - ruft-auf: backend/tft/sim/player.py::refresh_shop - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::buy_xp beschreibung: Kauft zusätzliche Erfahrungspunkte und aktualisiert das Level. input: Spielerzustand und Konfiguration. output: keine Rückgabe; Level und XP werden fortgeschrieben. entscheidungen: - Lehnt den Kauf ab, wenn das maximale Level bereits erreicht ist. beleg: " if p.level >= xp[\"max_level\"]:" - Übergibt die XP-Berechnung an `economy.apply_xp`, damit Level-Schwellen zentral gepflegt werden. beleg: " p.level, p.xp = economy.apply_xp(p.level, p.xp, xp[\"buy_amount\"], cfg)" kanten: - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::move beschreibung: Verschiebt eine Einheit zwischen Bank und Kampf-Board. input: Spielerzustand, Richtung und Index. output: keine Rückgabe; Board oder Bench werden mutiert. entscheidungen: - Wählt Quell- und Ziel-Liste abhängig von der Richtung in einer Zeile. beleg: " src, dst = (p.bench, p.board) if where == \"bench\" else (p.board, p.bench)" - Prüft die Board-Kapazität gegen das aktuelle Level des Spielers, nicht gegen einen festen Maximalwert. beleg: " if dst is p.board and len(p.board) >= p.level:" kanten: - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::equip beschreibung: Legt ein Item aus dem Inventar auf eine Board-Einheit und craftet bei passender Komponente. input: Spielerzustand, Item-Index, Board-Index und optionales Artifact. output: keine Rückgabe; Einheit und Inventar werden angepasst. entscheidungen: - Sortiert die beiden Komponenten vor dem Lookup, damit die Reihenfolge keine Rolle spielt. beleg: " crafted = recipes.get(\"|\".join(sorted((held, new_item))))" - Bricht die Funktion mit `return` ab, sobald ein Rezept erfolgreich gecraftet wurde. beleg: " p.items.pop(item_idx)" beleg: " return" kanten: - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::pick_augment beschreibung: Wählt einen Augment aus dem aktuellen Angebot des Spielers. input: Spielerzustand und Wahl-Index. output: keine Rückgabe; Augments-Liste wird erweitert und Angebot geleert. entscheidungen: - Lehnt die Aktion ab, wenn aktuell kein Angebot vorliegt. beleg: " if not p.augment_offer:" - Leert das Angebot nach der Auswahl, damit kein zweiter Pick möglich ist. beleg: " p.augment_offer = []" kanten: - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::grant_loot beschreibung: Verteilt Loot-Belohnungen wie Komponenten und Gold an einen Spieler. input: Spielerzustand, Anzahl Komponenten, Gold, Artifact und RNG. output: keine Rückgabe; Inventar und Gold werden erhöht. entscheidungen: - Wählt die Item-Quelle in absteigender Priorität aus Komponenten- und Item-Pool. beleg: " pool = (artifact.get(\"component_pool\") or artifact.get(\"item_pool\")" - Erlaubt nur das Droppen von Komponenten, fertige Items entstehen ausschließlich durch Crafting. beleg: " # Loot droppt Komponenten; fertige Items entstehen nur durch Craften." kanten: - nutzt: backend/tft/sim/player.py::InvalidAction ## backend/tft/sim/player.py::grab_carousel_unit beschreibung: Greift eine zufällige Karussell-Einheit und legt sie auf die Bank. input: Spielerzustand, Pool und RNG. output: keine Rückgabe; Bench und Pool werden verändert. entscheidungen: - Sammelt Kandidaten aus den Kostenstufen 1 bis 3, da Karussell-Einheiten günstig sind. beleg: " candidates = [a for c in (1, 2, 3) for a in pool.units_of_cost(c)]" - Überspringt den Greif, wenn die Bank voll ist, ohne Fehler zu werfen. beleg: " if candidates and len(p.bench) < BENCH_SIZE:" - Startet sofort einen Merge-Versuch, damit Drops direkt stapeln. beleg: " merge(p, api, 1)" kanten: - ruft-auf: backend/tft/sim/player.py::merge ## backend/tft/sim/player.py::fill_board beschreibung: Füllt freie Board-Plätze und tauscht stärkere Bank-Einheiten ein. input: Spielerzustand und Artifact-Dictionary. output: keine Rückgabe; Board und Bench werden umsortiert. entscheidungen: - Sortiert die Bank absteigend nach `unit_worth`, damit starke Einheiten vorne stehen. beleg: " p.bench.sort(key=lambda u: -unit_worth(artifact, u))" - Tauscht Board-Einheiten nur aus, wenn der Bank-Kandidat strikt stärker ist. beleg: " if unit_worth(artifact, best) > unit_worth(artifact, u):" - Bricht die Swap-Schleife ab, sobald die Bank leer ist. beleg: " if not p.bench:" kanten: - ruft-auf: backend/tft/sim/player.py::unit_worth