158 lines
5.7 KiB
GDScript3
158 lines
5.7 KiB
GDScript3
|
|
extends Node
|
||
|
|
|
||
|
|
func _ready():
|
||
|
|
print("=== Guandan Card Game - Core Logic Validation ===\n")
|
||
|
|
_test_card()
|
||
|
|
_test_deck()
|
||
|
|
_test_hand_evaluator()
|
||
|
|
_test_rule_engine()
|
||
|
|
_test_game_state()
|
||
|
|
_test_ai()
|
||
|
|
print("\n=== All validation checks passed ===")
|
||
|
|
get_tree().quit(0)
|
||
|
|
|
||
|
|
func _assert_eq(actual, expected, name: String):
|
||
|
|
if actual == expected:
|
||
|
|
print(" PASS: %s" % name)
|
||
|
|
else:
|
||
|
|
printerr(" FAIL: %s (expected %s, got %s)" % [name, expected, actual])
|
||
|
|
|
||
|
|
func _assert_true(condition: bool, name: String):
|
||
|
|
_assert_eq(condition, true, name)
|
||
|
|
|
||
|
|
func _assert_false(condition: bool, name: String):
|
||
|
|
_assert_eq(condition, false, name)
|
||
|
|
|
||
|
|
func _make_card(orig_id: int, deck_idx: int = 0) -> Card:
|
||
|
|
var suit: int
|
||
|
|
var rank: int
|
||
|
|
if orig_id == 52:
|
||
|
|
suit = 4; rank = 15
|
||
|
|
elif orig_id == 53:
|
||
|
|
suit = 5; rank = 16
|
||
|
|
else:
|
||
|
|
suit = orig_id % 4
|
||
|
|
rank = 2 + (orig_id / 4)
|
||
|
|
var c := Card.create(orig_id, suit, rank)
|
||
|
|
c.card_id = Card.card_id_from_deck(orig_id, deck_idx)
|
||
|
|
return c
|
||
|
|
|
||
|
|
func _make_cards(ids: Array) -> Array[Card]:
|
||
|
|
var result: Array[Card] = []
|
||
|
|
for spec in ids:
|
||
|
|
if spec is int:
|
||
|
|
result.append(_make_card(spec as int))
|
||
|
|
elif spec is Array:
|
||
|
|
result.append(_make_card(spec[0] as int, spec[1] as int))
|
||
|
|
return result
|
||
|
|
|
||
|
|
func _test_card():
|
||
|
|
print("--- Card Tests ---")
|
||
|
|
var c := Card.create(0, 2, 10)
|
||
|
|
_assert_eq(c.card_id, 0, "card_id")
|
||
|
|
_assert_eq(c.suit(), 2, "suit")
|
||
|
|
_assert_eq(c.rank(), 10, "rank")
|
||
|
|
|
||
|
|
var a := Card.create(0, 2, 10)
|
||
|
|
var b := Card.create(1, 2, 10)
|
||
|
|
_assert_true(a.matches(b), "matches (same suit+rank)")
|
||
|
|
_assert_false(a.equals(b), "equals (different card_id)")
|
||
|
|
|
||
|
|
var low := Card.create(0, 0, 5)
|
||
|
|
var high := Card.create(1, 1, 14)
|
||
|
|
_assert_true(low.compare_to(high) < 0, "compare_to (5 < A)")
|
||
|
|
|
||
|
|
func _test_deck():
|
||
|
|
print("--- Deck Tests ---")
|
||
|
|
var deck := Deck.create()
|
||
|
|
_assert_eq(deck.remaining(), 108, "deck size 108")
|
||
|
|
var hand := deck.deal(27)
|
||
|
|
_assert_eq(hand.size(), 27, "deal 27 cards")
|
||
|
|
_assert_eq(deck.remaining(), 81, "remaining 81 after deal")
|
||
|
|
|
||
|
|
var d1 := Deck.create(42)
|
||
|
|
var d2 := Deck.create(42)
|
||
|
|
var all1 := d1.deal(27)
|
||
|
|
var all2 := d2.deal(27)
|
||
|
|
var deterministic := true
|
||
|
|
for i in range(27):
|
||
|
|
if all1[i].card_id != all2[i].card_id:
|
||
|
|
deterministic = false
|
||
|
|
_assert_true(deterministic, "deterministic shuffle (seed=42)")
|
||
|
|
|
||
|
|
func _test_hand_evaluator():
|
||
|
|
print("--- HandEvaluator Tests ---")
|
||
|
|
var config := RuleConfig.standard()
|
||
|
|
|
||
|
|
var single := HandEvaluator.evaluate(_make_cards([0]), 5, config)
|
||
|
|
_assert_eq(single.type, 0, "single card type")
|
||
|
|
|
||
|
|
var cards: Array[Card] = [_make_card(0, 0), _make_card(0, 1)]
|
||
|
|
var pair := HandEvaluator.evaluate(cards, 5, config)
|
||
|
|
_assert_eq(pair.type, 1, "pair type")
|
||
|
|
|
||
|
|
var straight := HandEvaluator.evaluate(_make_cards([4, 8, 12, 16, 20]), 5, config)
|
||
|
|
_assert_eq(straight.type, 4, "straight type (3-7)")
|
||
|
|
_assert_eq(straight.primary_rank, 7, "straight max rank=7")
|
||
|
|
|
||
|
|
var bomb_cards: Array[Card] = [_make_card(0, 0), _make_card(1, 0), _make_card(0, 1), _make_card(1, 1)]
|
||
|
|
var bomb := HandEvaluator.evaluate(bomb_cards, 5, config)
|
||
|
|
_assert_eq(bomb.type, 8, "bomb type (4 twos)")
|
||
|
|
_assert_true(bomb.is_pure_bomb, "is_pure_bomb")
|
||
|
|
|
||
|
|
var rocket_cards: Array[Card] = [_make_card(52, 0), _make_card(52, 1), _make_card(53, 0), _make_card(53, 1)]
|
||
|
|
var rocket := HandEvaluator.evaluate(rocket_cards, 5, config)
|
||
|
|
_assert_eq(rocket.type, 9, "rocket type")
|
||
|
|
|
||
|
|
func _test_rule_engine():
|
||
|
|
print("--- RuleEngine Tests ---")
|
||
|
|
var config := RuleConfig.standard()
|
||
|
|
var hand: Array[Card] = [_make_card(0), _make_card(4)]
|
||
|
|
var single := HandEvaluator.evaluate(_make_cards([0]), 5, config)
|
||
|
|
var result := RuleEngine.can_play(hand, single, [], -1, 5, config)
|
||
|
|
_assert_true(result.ok, "can play on fresh table")
|
||
|
|
|
||
|
|
var pure := HandEvaluator.EvaluatedPlay.new()
|
||
|
|
pure.type = 8; pure.primary_rank = 10; pure.is_pure_bomb = true
|
||
|
|
var mixed := HandEvaluator.EvaluatedPlay.new()
|
||
|
|
mixed.type = 8; mixed.primary_rank = 10; mixed.is_pure_bomb = false
|
||
|
|
_assert_eq(RuleEngine.compare_bombs(pure, mixed, config), 1, "pure bomb beats mixed")
|
||
|
|
_assert_eq(RuleEngine.compare_bombs(mixed, pure, config), -1, "mixed bomb loses to pure")
|
||
|
|
|
||
|
|
var rocket := HandEvaluator.EvaluatedPlay.new()
|
||
|
|
rocket.type = 9; rocket.primary_rank = 999
|
||
|
|
_assert_eq(RuleEngine.compare(rocket, pure, config), 1, "rocket beats bomb")
|
||
|
|
|
||
|
|
func _test_game_state():
|
||
|
|
print("--- GameState Tests ---")
|
||
|
|
var gs := GameState.create(RuleConfig.standard(), 42)
|
||
|
|
_assert_eq(gs.seed, 42, "seed=42")
|
||
|
|
_assert_eq(gs.teams.size(), 2, "2 teams")
|
||
|
|
_assert_eq(gs.current_rank, 2, "start at level 2")
|
||
|
|
|
||
|
|
var deck := Deck.create(1)
|
||
|
|
gs.deal_cards(deck)
|
||
|
|
for i in range(4):
|
||
|
|
_assert_eq(gs.get_hand(i).size(), 27, "player %d got 27 cards" % i)
|
||
|
|
|
||
|
|
var t := Actions.Team.create_team(0, 0, 2)
|
||
|
|
_assert_eq(t.teammate_of(0), 2, "teammate_of(0)=2")
|
||
|
|
_assert_eq(t.teammate_of(2), 0, "teammate_of(2)=0")
|
||
|
|
|
||
|
|
func _test_ai():
|
||
|
|
print("--- AI Tests ---")
|
||
|
|
var config := RuleConfig.standard()
|
||
|
|
var hand := _make_cards([0])
|
||
|
|
var l1 := L1BasicAI.new()
|
||
|
|
var table: Array[HandEvaluator.EvaluatedPlay] = []
|
||
|
|
var decision := l1.decide(hand, table, 5, config)
|
||
|
|
_assert_true(decision.type > 0 or decision.type == -1, "L1 AI returns valid play or pass")
|
||
|
|
|
||
|
|
var l2 := L2RuleAI.new()
|
||
|
|
var decision2 := l2.decide(hand, table, 5, config)
|
||
|
|
_assert_true(decision2.type > 0 or decision2.type == -1, "L2 AI returns valid play or pass")
|
||
|
|
|
||
|
|
var hand2 := _make_cards([0, 4, 8, 12, 16])
|
||
|
|
var decision3 := l1.decide(hand2, table, 5, config)
|
||
|
|
_assert_true(decision3.type == 4, "L1 AI should play straight when possible on fresh table")
|