77 lines
4.1 KiB
Markdown
77 lines
4.1 KiB
Markdown
# Einheiten: backend/tft/sim/combat.py
|
|
stand: f41ac76b5789
|
|
|
|
## backend/tft/sim/combat.py::FightResult
|
|
beschreibung: Datenklasse, die das Ergebnis eines Kampfes zwischen zwei Teams festhält.
|
|
input: Siegerkennzeichen "a"/"b"/None, gespielte Frames und überlebende Units beider Seiten
|
|
output: Ein `FightResult`-Objekt mit `winner`, `frames`, `survivors_a` und `survivors_b`
|
|
entscheidungen:
|
|
- Das Ergebnis-Format schließt einen Unentschieden-Fall explizit als `None` mit ein.
|
|
beleg: "winner: str | None # \"a\" | \"b\" | None = Unentschieden"
|
|
- Überlebende werden seitenweise als Liste von Unit-Dicts geführt, nicht als Indizes.
|
|
beleg: "survivors_a: list[dict]"
|
|
- Die Anzahl der simulierten Frames wird als Ganzzahl gerundet (`ceil`).
|
|
beleg: "frames=math.ceil(elapsed * FPS),"
|
|
kanten:
|
|
- wird-genutzt-von: backend/tft/sim/combat.py::fight
|
|
|
|
## backend/tft/sim/combat.py::_queue
|
|
beschreibung: Bestimmt die Bearbeitungsreihenfolge der Units eines Teams als defensive zuerst, intern zufällig gemischt.
|
|
input: Team-Liste von Unit-Dicts und ein `random.Random`-RNG
|
|
output: Neue Liste, in der defensive Units vor offensiven stehen, beide Gruppen intern gemischt
|
|
entscheidungen:
|
|
- Defensive Units werden grundsätzlich vor offensiven angegriffen.
|
|
beleg: "defensive = [p for p in team if p[\"defensive\"]]"
|
|
- Innerhalb jeder Gruppe wird die Reihenfolge per `rng.shuffle` zufällig gemischt.
|
|
beleg: "rng.shuffle(defensive)"
|
|
- Die Sortierung ist nicht stabil, da die ursprüngliche Reihenfolge überschrieben wird.
|
|
beleg: "return defensive + offensive"
|
|
kanten:
|
|
- nutzt: backend/tft/sim/combat.py::_suffix_off
|
|
- wird-genutzt-von: backend/tft/sim/combat.py::fight
|
|
|
|
## backend/tft/sim/combat.py::_suffix_off
|
|
beschreibung: Berechnet die Suffix-Summe der Offensive-Werte einer Ziel-Queue.
|
|
input: Liste von Unit-Dicts mit `"off"`-Feld
|
|
output: Liste gleicher Länge + 1, in der `suffix[i]` die Gesamt-Offense ab Unit `i` ist
|
|
entscheidungen:
|
|
- Die Liste ist absichtlich um einen Eintrag länger, damit `suffix[len(queue)] = 0` als Sicherheitselement dient.
|
|
beleg: "suffix = [0.0] * (len(queue) + 1)"
|
|
- Die Summation läuft rückwärts, um die echte Suffix-Summe zu bilden.
|
|
beleg: "for i in range(len(queue) - 1, -1, -1):"
|
|
kanten:
|
|
- wird-genutzt-von: backend/tft/sim/combat.py::fight
|
|
|
|
## backend/tft/sim/combat.py::fight
|
|
beschreibung: Simuliert einen deterministischen Kampf zweier Teams eventbasiert bis zum nächsten Tod oder Zeitlimit.
|
|
input: Zwei Team-Profil-Listen, ein `random.Random`-RNG und eine Config mit `combat.max_frames`
|
|
output: `FightResult` mit Sieger, Framezahl und Überlebenden beider Seiten
|
|
entscheidungen:
|
|
- Zwischen zwei Todesereignissen werden beide Offensen als konstant angenommen, `dt = min(ta, tb)`.
|
|
beleg: "dt = min(ta, tb)"
|
|
- Bei Überschreitung des Zeitlimits wird `dt` auf das verbleibende Restbudget begrenzt.
|
|
beleg: "dt = limit - elapsed"
|
|
- Überzähliger Schaden wird verlustfrei auf das nächste Ziel übertragen, indem die Rest-Defense weiter reduziert wird.
|
|
beleg: "rb -= oa * dt"
|
|
- Bei Zeitlimit ohne verbleibende Offense wird der Sieg nach größerer Rest-Defense entschieden.
|
|
beleg: "winner = \"a\" if rest_a > rest_b + EPS else \"b\" if rest_b > rest_a + EPS else None"
|
|
- Ein gleicher Kill-Frame kann nicht beide Seiten treffen, da `else`-Zweige getrennt ausgewertet werden.
|
|
beleg: "else:"
|
|
kanten:
|
|
- ruft-auf: backend/tft/sim/combat.py::_queue
|
|
- ruft-auf: backend/tft/sim/combat.py::_suffix_off
|
|
- nutzt: backend/tft/sim/combat.py::FightResult
|
|
- wird-genutzt-von: backend/tft/sim/combat.py::player_damage
|
|
|
|
## backend/tft/sim/combat.py::player_damage
|
|
beschreibung: Berechnet den Schaden, den ein Spieler nach einer Runde abhängig von erreichter Stage und Überlebenden erhält.
|
|
input: Stage-Index (1-basiert), Liste der überlebenden Units und eine Config mit `damage`
|
|
output: Schadenswert als `int`
|
|
entscheidungen:
|
|
- Der Basis-Schaden wird stageweise aus `cfg["damage"]["stage_base"]` gelesen, bei Überschreitung wird der letzte Eintrag verwendet.
|
|
beleg: "min(stage - 1, len(d[\"stage_base\"]) - 1)"
|
|
- Pro überlebender Unit wird ein linearer Zusatzschaden addiert.
|
|
beleg: "return base + d[\"per_surviving_unit\"] * len(survivors)"
|
|
kanten:
|
|
- nutzt: backend/tft/sim/combat.py::fight
|