KI bewertet Kontrolle, Deckstärke mit Kurve und Statistik, Gegnervorschau, Deck-Vorschlag, Schwierigkeitstipp, DeckGen/NetState ausgelagert, Wasser gestärkt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPgKvy5W9WRNFHjkm5QsHx
This commit is contained in:
Team3
2026-09-16 15:16:49 +02:00
parent 7ae6017f12
commit 0dbc98b456
18 changed files with 595 additions and 236 deletions

View File

@@ -108,12 +108,14 @@ func _initialize() -> void:
test_herausforderung_und_bestenliste()
test_deckstaerke_und_schwierigkeit()
test_starter_gegen_passende_gegner()
test_kurve_statistik_vorschlag_verbesserung()
test_wasser_antworten()
print("%d Prüfungen bestanden, %d fehlgeschlagen" % [passed, failed])
quit(1 if failed > 0 else 0)
func test_kartenpool() -> void:
check(Cards.all().size() == 106, "106 Karten")
check(Cards.all().size() == 109, "109 Karten")
for key in Cards.COMBOS:
var found: bool = Cards.all().any(func(c): return c.is_combo() and Cards.combo_key(c.elements) == key)
check(found, "Kombo vorhanden: " + key)
@@ -458,7 +460,7 @@ func test_spielstand() -> void:
p.coins = 999
p.save(path)
var p2 := Profile.load_or_new(path)
check(p2.coins == 999 and p2.deck.size() == 20 and p2.collection.size() == 41, "geladen")
check(p2.coins == 999 and p2.deck.size() == 20 and p2.collection.size() == 42, "geladen")
for suffix in ["", ".bak"]:
if FileAccess.file_exists(path + suffix):
DirAccess.remove_absolute(abs + suffix)
@@ -510,7 +512,7 @@ func test_resonanz() -> void:
put(h, "f_kobold")
put(h, "f_kobold")
g.update_resonance()
check(h.board[0].power() == 7, "Resonanz höchstens +4")
check(h.board[0].power() == 6 and Game.resonance_for(1) == 1 and Game.resonance_for(3) == 3 and Game.resonance_for(4) == 3, "Resonanz höchstens +3")
for i in [1, 3, 4, 5, 6]:
h.board[i].health = 0
g.cleanup()
@@ -1214,7 +1216,7 @@ func test_deckstaerke_und_schwierigkeit() -> void:
var d := Game.build_opponent_deck_rated(r, elems, target)
check(Game.validate_deck(d)[0] and absf(Game.deck_rating(d) - target) <= 0.25, "Zielstärke %.1f für %s erreicht (%.2f)" % [target, "+".join(elems), Game.deck_rating(d)])
var strong := Game.build_opponent_deck_rated(r, ["Feuer", "Luft"], 3.0)
check(Game.deck_rating(strong) >= 2.8, "Zielstärke 3,0 mit zwei Elementen erreichbar (%.2f)" % Game.deck_rating(strong))
check(Game.deck_rating(strong) >= 2.7, "Zielstärke 3,0 mit zwei Elementen nahezu erreichbar (%.2f)" % Game.deck_rating(strong))
var g := Game.new_battle(starter, 2, r, {"target_rating": 1.3})
for o in g.opponents:
check(absf(Game.deck_rating(o.deck + o.hand) - 1.3) <= 0.3, "Gegner auf Zielstärke (%.2f)" % Game.deck_rating(o.deck + o.hand))
@@ -1261,3 +1263,59 @@ func test_starter_gegen_passende_gegner() -> void:
results[key] = wins
check(results.leicht > results.schwer, "Leicht ist leichter als Schwer (%d zu %d von 24)" % [results.leicht, results.schwer])
check(results.leicht >= 16, "Leicht: Starter gewinnt meistens (%d von 24)" % results.leicht)
func test_kurve_statistik_vorschlag_verbesserung() -> void:
var starter := Profile.starter().deck_cards()
check(is_zero_approx(DeckGen.curve_penalty(starter)), "Starterkurve ohne Abzug")
var teuer := []
for i in 20:
teuer.append(Cards.by_id("f_zunge"))
check(DeckGen.curve_penalty(teuer) >= 0.4 - 0.001 and Game.deck_rating(teuer) < 1.0, "teures Deck: Kurvenabzug")
var stats := {"f_kobold": {"played": 30, "games": 20, "wins": 16}}
check(DeckGen.card_value(Cards.by_id("f_kobold"), stats) > Cards.card_value(Cards.by_id("f_kobold")), "gute Statistik hebt den Wert")
check(Game.deck_rating(starter, stats) > Game.deck_rating(starter), "Deckstärke mit Statistik")
var few := {"f_kobold": {"played": 3, "games": 3, "wins": 3}}
check(is_equal_approx(DeckGen.card_value(Cards.by_id("f_kobold"), few), 1.0), "zu wenig Partien: Basiswert")
# Vorschlag nach Serien
var p := Profile.starter()
var mono := starter
check(p.suggest_difficulty() == "", "kein Vorschlag am Anfang")
for i in 3:
p.record_battle(true, 1, mono)
check(p.suggest_difficulty() == "schwer" and p.notices.any(func(n): return n.begins_with("Tipp: 3 Siege")), "drei Siege: Schwer vorschlagen")
p.settings.difficulty = "schwer"
p.take_notices()
for i in 3:
p.record_battle(false, 1, mono)
check(p.records.loss_streak == 3 and p.suggest_difficulty() == "normal", "drei Niederlagen: Normal vorschlagen")
p.settings.difficulty = "leicht"
check(p.suggest_difficulty() == "", "auf Leicht kein Abstieg")
# Deck verbessern
var q := Profile.starter()
check(q.improve_deck_with("ff_phoenix", true) != "", "nicht besessen")
q.collection["ff_phoenix"] = 1
check(q.improve_deck_with("ff_phoenix", true) == "", "Phönix passt (Feuer)")
q.collection["ww_leviathan"] = 1
check(q.improve_deck_with("ww_leviathan", true) != "", "Leviathan passt nicht (drittes Element)")
check(q.deck_suggestions(["ff_phoenix", "ww_leviathan", "f_kobold"]) == ["ff_phoenix"], "Vorschläge gefiltert")
var before := q.deck.duplicate()
check(q.improve_deck_with("ff_phoenix") == "" and q.deck.count("ff_phoenix") == 1 and q.deck.size() == 20 and q.deck_valid()[0], "Phönix eingetauscht")
check(Game.deck_rating(q.deck_cards()) > Game.deck_rating(before.map(func(id): return Cards.by_id(id))), "Deck stärker")
check(q.improve_deck_with("ff_phoenix", true) != "", "zweite Kopie nicht möglich")
func test_wasser_antworten() -> void:
var s := setup(["w_ebbe", "w_tiefenstoss", "w_strudel"])
var g: Game = s[0]
var h: Game.Player = s[1]
var o: Game.Player = s[2][0]
var t := put(o, "e_troll")
g.play_card(h, C("w_ebbe"), t)
check(t.health == 5 and t.frozen, "Ebbe: 1 Schaden und Eis")
g.play_card(h, C("w_tiefenstoss"), t)
check(t.health == 2, "Tiefenstoß: 3 Schaden")
var hand := h.hand.size()
g.play_card(h, C("w_strudel"), t)
check(o.board.is_empty() and C("e_troll") in o.hand and h.hand.size() == hand, "Strudel: zurück auf die Hand, eine Karte gezogen")
check(C("w_ebbe").rarity == "gewöhnlich" and Profile.starter().collection.has("w_ebbe"), "Ebbe in der Startsammlung")