Files
pirate-dice/tests/logik.gd
2026-09-16 17:00:41 +02:00

139 lines
5.7 KiB
GDScript

extends "res://tests/basis.gd"
## Solopartie headless: Münzen, Topf, Zählen, Stufe, Tipp-Schranke.
## Die Bots werden zu "Menschen" ohne Peer gemacht - dann wartet der Host
## auf unsere Eingaben, und der Ablauf ist steuerbar. Alles, was den eigenen
## Zug braucht, kommt zuerst: nach 30 s Spielzeit spielt der Host den Platz
## sonst automatisch. Genau und Aufdecken prüft tests/genau.gd.
var game: Node
func _ready() -> void:
add_child(preload("res://scenes/main.tscn").instantiate())
await get_tree().process_frame
Net.start_singleplayer("Tester", 3, 2)
_lauf()
func _menschen() -> void:
for i in [1, 2, 3]:
game.seats[i].ai = false
func _lauf() -> void:
await _warte(func(): game = _find(get_tree().root, "Game"); return game != null, 20.0)
# Nicht null: die Etappen beim Aufdecken sollen noch einzeln sichtbar sein.
game.tempo = 0.3
await _warte(func(): return game.phase == "bid", 40.0)
var me: int = game.local_seat
var hud: Node = game.hud
_pruef("Stufe aus der Konfiguration", DiceAI.stufe == 2)
_pruef("Jeder startet mit einer Münze", game.hand[me].size() == 1 and game.hand[1].size() == 1)
_menschen()
# --- Zugbindung
game.hand[1] = ["peek"]
await game._do_play(1, "peek", 0)
_pruef("Gegner wirft nicht am fremden Zug", game.hand[1] == ["peek"] and game.topf.is_empty())
game.hand[me] = ["count"]
await game._do_play(me, "count", 0)
_pruef("Zählen ohne Ansage abgelehnt", game.hand[me].has("count"))
game.hand[me] = ["gain_die"]
await game._do_play(me, "gain_die", 0)
_pruef("Nachschub bei voller Startzahl abgelehnt", game.hand[me].has("gain_die") and game.seats[me].count == 5)
# --- Topf: Deckel und Übertrag
game.topf.clear()
game.hand[me] = ["gag", "gag", "gag", "gag", "gag", "gag", "gag"]
for i in 7:
await game._do_play(me, "gag", 0)
_pruef("Topf höchstens %d" % BoosterDB.TOPF_MAX, game.topf.size() == BoosterDB.TOPF_MAX)
_pruef("Rausfallen steht im Protokoll", game.log_lines.any(func(z): return "vom Tisch" in z))
game.hand[2] = ["peek", "peek"]
game._topf_an(2)
_pruef("Gewinner nimmt bis zum Limit, Rest bleibt",
game.hand[2].size() == BoosterDB.HANDLIMIT and game.topf.size() == BoosterDB.TOPF_MAX - 2)
game.topf.clear()
game.must_bid.clear()
# --- Wirkungen beider Seiten
game.bid = {"seat": 1, "count": 1, "face": 4}
var n4: int = game._count_face(4)
await game._wirkung(me, "count", 0, false)
var f: Array = game.gezaehlt[me][4]
_pruef("Zählen: Fenster enthält die Wahrheit", int(f[0]) <= n4 and n4 <= int(f[1]) and int(f[1]) - int(f[0]) == 2)
await game._wirkung(me, "count", 0, true)
f = game.gezaehlt[me][4]
_pruef("Zählen: Wappen ist exakt", int(f[0]) == n4 and int(f[1]) == n4)
await game._wirkung(me, "set_face", 0, true)
_pruef("Falschspiel dreht zwei Würfel auf die Ansage", game.host_values[me].count(4) >= 2)
await game._wirkung(me, "gain_die", 0, true)
_pruef("Nachschub mit Wappen: neuer Würfel ist 6", game.host_values[me].size() == 6 and game.host_values[me][5] == 6)
await game._wirkung(me, "bid_up", 0, true)
_pruef("Erhöhen mit Wappen: +2", int(game.bid.count) == 3 and game.verlauf.back().erhoeht)
await game._wirkung(me, "gag", 0, false)
_pruef("Maulkorb trifft den Nächsten", game.must_bid.get(1, false) and not game.must_bid.get(2, false))
await game._wirkung(me, "peek", 0, true)
_pruef("Spähen mit Wappen: zwei je Gegner", game.peeked[me][1].size() == 2)
game.bid = {}
game.must_bid.clear()
# --- Fangnetz: ein Würfel unter dem Tisch kommt zurück und kommt zur Ruhe
var d0 = game.table.dice_of(me)[0]
d0.freeze = false
d0.place(Transform3D(Basis(), Vector3(0, -50.0, 0)))
_pruef("Fangnetz holt den Würfel zurück", game.table.fange_verirrte(me) and d0.global_position.y > 0.0)
d0.place(Transform3D(Basis(), Vector3(0, -50.0, 0)))
await game._neu_werfen(me, [0])
_pruef("Würfeln endet trotz verirrtem Würfel, Wert gelesen",
d0.global_position.y > 0.5 and game.host_values[me][0] >= 1 and game.host_values[me][0] <= 6)
# --- KI nutzt, was Spähen und Zählen verraten
var r := RandomNumberGenerator.new()
r.seed = 7
var gebot := {"seat": 1, "count": 4, "face": 5}
var a: Dictionary = DiceAI.choose_action([1, 2, 3], 9, gebot, false, r, {"fenster": {5: [1, 2]}})
_pruef("KI: Zählfenster unter der Ansage -> Anfechten", a.type == "challenge")
a = DiceAI.choose_action([5, 5, 2], 9, gebot, false, r, {"bekannt": [5, 5, 5]})
_pruef("KI: aufgedeckte Fünfen machen die Ansage sicher -> kein Anfechten", a.type != "challenge")
a = DiceAI.choose_action([1, 2, 3], 9, gebot, false, r, {"fenster": {5: [4, 4]}})
_pruef("KI: exaktes Fenster -> Genau", a.type == "exact")
# --- Tastatur: Taste tut nur, was der Knopf gerade darf
var taste := func(code: Key) -> void:
var ev := InputEventKey.new()
ev.keycode = code
ev.pressed = true
hud._unhandled_key_input(ev)
var gebote := []
hud.bid_pressed.connect(func(c, f): gebote.append([c, f]))
game._push_state()
await get_tree().create_timer(0.3).timeout
taste.call(KEY_3)
taste.call(KEY_PLUS)
taste.call(KEY_ENTER)
_pruef("Tastatur: 3, +, Enter sagt 2 x 3 an", gebote == [[2, 3]])
var angefochten := []
hud.challenge_pressed.connect(func(): angefochten.append(1))
taste.call(KEY_A)
_pruef("Tastatur: A ohne Ansage tut nichts", angefochten.is_empty())
# --- Protokoll und Tipp-Schranke im HUD
game._push_state()
await get_tree().create_timer(0.3).timeout
_pruef("Protokoll kommt im HUD an", hud.log_box.get_child_count() > 0)
hud._touch = true
var geworfen := []
hud.booster_chosen.connect(func(id, _t): geworfen.append(id))
game.hand[me] = ["peek"]
game._push_state()
await get_tree().create_timer(0.3).timeout
hud._pick("peek")
_pruef("Handy: erster Tipp wirft nicht", geworfen.is_empty())
hud._pick("peek")
_pruef("Handy: zweiter Tipp wirft", geworfen == ["peek"])
hud._touch = false
await get_tree().create_timer(2.5).timeout
_fertig()