# Einheiten: backend/tft/sim/game.py stand: f41ac76b5789 ## backend/tft/sim/game.py::pair_players beschreibung: Ermittelt die PvP-Paarungen der lebenden Spieler und einen eventuellen Ghost-Gegner unter Vermeidung von Wiederholungsgegnern. input: Liste lebender PlayerState und ein random.Random output: Tupel aus Paaren, einem möglichen odd-Spieler und einem möglichen Ghost-Spieler entscheidungen: - Es werden bis zu 20 Shuffle-Versuche unternommen, um eine kollisionsfreie Paarung zu finden. beleg: "for _ in range(20):" - Wiederholungsgegner werden bei mehr als zwei Lebenden explizit ausgeschlossen. beleg: "all(a.last_opponent != b.name for a, b in pairs)" - Bei ungerader Spielerzahl wird ein Spieler als Ghost gegen einen zufälligen anderen gespiegelt. beleg: "ghost_src = rng.choice([p for p in alive if p is not odd]) if odd else None" - Die Paarungsinformation wird über last_opponent beidseitig festgehalten. beleg: "a.last_opponent, b.last_opponent = b.name, a.name" kanten: - nutzt: backend/tft/sim/player.py::PlayerState ## backend/tft/sim/game.py::Game beschreibung: Orchestriert eine komplette TFT-Partie als endliche Zustandsmaschine über Runden, Spielerauswahl und Kampfauswertung. input: artifact (dict), cfg (dict), optional seed (int) output: ein lauffähiger Spielzustand mit Spieler, Bots, Rundenplan, Pool und Log, der per `step()` voranschreitet entscheidungen: - Der Mensch steuert nur `self.player`, Bots werden fest auf 7 Instanzen aus rotierenden Archetypen aufgeteilt. beleg: "for i in range(7)" - Carousels laufen automatisch für alle ab, ohne Einkommen oder XP zu gewähren. beleg: "for p in self.players:" - Bei Rundenstart werden leere Shop-Slots vorbereitet, da zu 1-2 noch niemand Gold hat. beleg: "for p in self.players:" - Streak-Gold und Siegbonus werden ausschließlich in PvP-Runden gewertet, in PvE nicht. beleg: "if is_pvp:" - Augment-Angebote werden deterministisch pro Runde aus einer gewichteten Stufe gezogen, nicht pro Spieler separat. beleg: "self.rng.choices((1, 2, 3), weights=weights)[0]" kanten: - ruft-auf: backend/tft/sim/pool.py::Pool - ruft-auf: backend/tft/sim/player.py::new_player - ruft-auf: backend/tft/sim/player.py::score - ruft-auf: backend/tft/sim/player.py::grant_loot - ruft-auf: backend/tft/sim/player.py::grab_carousel_unit - ruft-auf: backend/tft/sim/player.py::buy - ruft-auf: backend/tft/sim/player.py::sell - ruft-auf: backend/tft/sim/player.py::reroll - ruft-auf: backend/tft/sim/player.py::buy_xp - ruft-auf: backend/tft/sim/player.py::move - ruft-auf: backend/tft/sim/player.py::equip - ruft-auf: backend/tft/sim/player.py::pick_augment - ruft-auf: backend/tft/sim/player.py::fill_board - ruft-auf: backend/tft/sim/player.py::merge - ruft-auf: backend/tft/sim/player.py::refresh_shop - ruft-auf: backend/tft/sim/policy.py::act - ruft-auf: backend/tft/sim/policy.py::ARCHETYPES - ruft-auf: backend/tft/sim/economy.py::apply_xp - ruft-auf: backend/tft/sim/economy.py::round_income - ruft-auf: backend/tft/sim/combat.py::fight - ruft-auf: backend/tft/sim/combat.py::player_damage - ruft-auf: backend/tft/sim/combat.py::pair_players - ruft-auf: backend/tft/sim/combat.py::board_profiles ## backend/tft/sim/game.py::_delegate beschreibung: Erzeugt eine Property, die Lese- und Schreibzugriff transparent an ein Attribut eines delegierten Objekts weiterreicht. input: ein Attributname als String output: ein `property`-Objekt mit Getter und Setter, die das gleichnamige Attribut auf `self.player` lesen bzw. schreiben entscheidungen: - Getter und Setter greifen beide über `self.player` auf das Zielattribut zu, sodass Aufrufer das Delegate-Objekt nicht vom echten Spieler unterscheiden müssen. beleg: "lambda self: getattr(self.player, attr)" - Schreibzugriff wird ebenfalls an `self.player` durchgereicht, wodurch das Delegate zweiseitig konsistent bleibt. beleg: "lambda self, value: setattr(self.player, attr, value)" - Die Attribute werden als ungeprüfte Strings übergeben, was eine sehr flexible, aber untypisierte Bindung an beliebige `player`-Attribute erlaubt. beleg: "attr: str" kanten: