test: add GUT test files for core modules (Card, Deck, HandEvaluator, RuleEngine, GameState)
This commit is contained in:
42
tests/test_cards.gd
Normal file
42
tests/test_cards.gd
Normal file
@@ -0,0 +1,42 @@
|
||||
extends GutTest
|
||||
|
||||
func test_card_create():
|
||||
var card = Card.create(0, 3, 14)
|
||||
assert_eq(card.card_id, 0)
|
||||
assert_eq(card.original_id, 0)
|
||||
assert_eq(card.suit(), 3)
|
||||
assert_eq(card.rank(), 14)
|
||||
|
||||
func test_card_equality():
|
||||
var a := Card.create(0, 2, 10)
|
||||
var b := Card.create(1, 2, 10)
|
||||
assert_true(a.matches(b))
|
||||
assert_false(a.equals(b))
|
||||
|
||||
func test_card_id_range():
|
||||
assert_eq(Card.MIN_ID, 0)
|
||||
assert_eq(Card.MAX_ID, 107)
|
||||
|
||||
func test_is_joker():
|
||||
var small := Card.create(52, 4, 15)
|
||||
var big := Card.create(53, 5, 16)
|
||||
assert_true(small.is_joker())
|
||||
assert_true(big.is_joker())
|
||||
|
||||
func test_compare_to():
|
||||
var low := Card.create(0, 0, 5)
|
||||
var high := Card.create(1, 1, 14)
|
||||
assert_lt(low.compare_to(high), 0)
|
||||
|
||||
func test_card_id_from_deck():
|
||||
assert_eq(Card.card_id_from_deck(0, 0), 0)
|
||||
assert_eq(Card.card_id_from_deck(0, 1), 54)
|
||||
assert_eq(Card.card_id_from_deck(53, 0), 53)
|
||||
assert_eq(Card.card_id_from_deck(53, 1), 107)
|
||||
|
||||
func test_packed():
|
||||
var c := Card.create(10, 1, 13)
|
||||
var packed := c.to_packed()
|
||||
var unpacked := Card.from_packed(packed)
|
||||
assert_eq(unpacked.suit(), 1)
|
||||
assert_eq(unpacked.rank(), 13)
|
||||
35
tests/test_deck.gd
Normal file
35
tests/test_deck.gd
Normal file
@@ -0,0 +1,35 @@
|
||||
extends GutTest
|
||||
|
||||
func test_deck_has_108_cards():
|
||||
var deck := Deck.create()
|
||||
assert_eq(deck.remaining(), 108)
|
||||
|
||||
func test_deal_reduces_count():
|
||||
var deck := Deck.create()
|
||||
var cards := deck.deal(27)
|
||||
assert_eq(cards.size(), 27)
|
||||
assert_eq(deck.remaining(), 81)
|
||||
|
||||
func test_shuffle_deterministic():
|
||||
var seed := 12345
|
||||
var deck1 := Deck.create(seed)
|
||||
var deck2 := Deck.create(seed)
|
||||
var hand1 := deck1.deal(108)
|
||||
var hand2 := deck2.deal(108)
|
||||
for i in range(108):
|
||||
assert_eq(hand1[i].card_id, hand2[i].card_id)
|
||||
|
||||
func test_deal_empty_deck():
|
||||
var deck := Deck.create()
|
||||
deck.deal(108)
|
||||
var extra := deck.deal(1)
|
||||
assert_eq(extra.size(), 0)
|
||||
|
||||
func test_deck_card_ids_unique():
|
||||
var deck := Deck.create()
|
||||
var all_cards := deck.deal(108)
|
||||
var ids := {}
|
||||
for c in all_cards:
|
||||
assert_false(ids.has(c.card_id))
|
||||
ids[c.card_id] = true
|
||||
assert_eq(ids.size(), 108)
|
||||
74
tests/test_game_state.gd
Normal file
74
tests/test_game_state.gd
Normal file
@@ -0,0 +1,74 @@
|
||||
extends GutTest
|
||||
|
||||
var _config: RuleConfig
|
||||
const _C = preload("res://src/core/constants.gd")
|
||||
|
||||
func before_each():
|
||||
_config = RuleConfig.standard()
|
||||
|
||||
func _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 _cards(ids: Array) -> Array[Card]:
|
||||
var result: Array[Card] = []
|
||||
for id in ids:
|
||||
result.append(_card(id as int))
|
||||
return result
|
||||
|
||||
func test_generate_includes_pass():
|
||||
var hand := _cards([0])
|
||||
var moves := MoveGenerator.generate(hand, 5, _config)
|
||||
var has_pass := false
|
||||
for m in moves:
|
||||
if m.type == -1 and m.cards.is_empty():
|
||||
has_pass = true
|
||||
assert_true(has_pass)
|
||||
|
||||
func test_generate_singles():
|
||||
var hand := _cards([0, 4, 8])
|
||||
var moves := MoveGenerator.generate(hand, 5, _config)
|
||||
var singles := 0
|
||||
for m in moves:
|
||||
if m.type == _C.TYPE_SINGLE:
|
||||
singles += 1
|
||||
assert_gt(singles, 0)
|
||||
|
||||
func test_generate_pair():
|
||||
var hand: Array[Card] = [_card(0, 0), _card(0, 1), _card(4, 0)]
|
||||
var moves := MoveGenerator.generate(hand, 5, _config)
|
||||
var has_pair := false
|
||||
for m in moves:
|
||||
if m.type == _C.TYPE_PAIR:
|
||||
has_pair = true
|
||||
assert_true(has_pair)
|
||||
|
||||
func test_empty_hand_returns_pass_only():
|
||||
var hand: Array[Card] = []
|
||||
var moves := MoveGenerator.generate(hand, 5, _config)
|
||||
assert_eq(moves.size(), 1)
|
||||
assert_eq(moves[0].type, -1)
|
||||
|
||||
func test_rule_config_standard():
|
||||
var rc := RuleConfig.standard()
|
||||
assert_eq(rc.double_down_levels, 3)
|
||||
assert_false(rc.can_tribute_wild)
|
||||
|
||||
func test_rule_config_huaian():
|
||||
var rc := RuleConfig.huaian()
|
||||
assert_false(rc.same_suit_straight_beats_bomb)
|
||||
|
||||
func test_rule_config_hash():
|
||||
var rc := RuleConfig.standard()
|
||||
var h := rc.to_hash_data()
|
||||
assert_eq(h.double_down, 3)
|
||||
71
tests/test_hand_evaluator.gd
Normal file
71
tests/test_hand_evaluator.gd
Normal file
@@ -0,0 +1,71 @@
|
||||
extends GutTest
|
||||
|
||||
var _config: RuleConfig
|
||||
const _C = preload("res://src/core/constants.gd")
|
||||
|
||||
func before_each():
|
||||
_config = RuleConfig.standard()
|
||||
|
||||
func _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 _cards(ids: Array) -> Array[Card]:
|
||||
var result: Array[Card] = []
|
||||
for spec in ids:
|
||||
if spec is int:
|
||||
result.append(_card(spec as int))
|
||||
elif spec is Array:
|
||||
result.append(_card(spec[0] as int, spec[1] as int))
|
||||
return result
|
||||
|
||||
func test_single():
|
||||
var cards := _cards([0])
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_eq(result.type, _C.TYPE_SINGLE)
|
||||
|
||||
func test_pair():
|
||||
# Two cards of same rank from different decks
|
||||
var cards: Array[Card] = [_card(0, 0), _card(0, 1)]
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_eq(result.type, _C.TYPE_PAIR)
|
||||
|
||||
func test_straight():
|
||||
# 3,4,5,6,7: orig_ids 4,8,12,16,20 (all from deck 0)
|
||||
var cards := _cards([4, 8, 12, 16, 20])
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_eq(result.type, _C.TYPE_STRAIGHT)
|
||||
assert_eq(result.primary_rank, 7)
|
||||
|
||||
func test_not_a_combo():
|
||||
var cards := _cards([0, 1, 2])
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_eq(result.type, HandEvaluator.INVALID)
|
||||
|
||||
func test_bomb():
|
||||
# Four 2s: same orig_id from both decks
|
||||
var cards: Array[Card] = [_card(0, 0), _card(0, 1), _card(1, 0), _card(1, 1)]
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_eq(result.type, _C.TYPE_BOMB)
|
||||
assert_eq(result.primary_rank, 2)
|
||||
assert_true(result.is_pure_bomb)
|
||||
|
||||
func test_rocket():
|
||||
var cards: Array[Card] = [_card(52, 0), _card(52, 1), _card(53, 0), _card(53, 1)]
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_eq(result.type, _C.TYPE_ROCKET)
|
||||
|
||||
func test_evaluator_null_on_empty():
|
||||
var cards: Array[Card] = []
|
||||
var result := HandEvaluator.evaluate(cards, 5, _config)
|
||||
assert_null(result)
|
||||
77
tests/test_rule_engine.gd
Normal file
77
tests/test_rule_engine.gd
Normal file
@@ -0,0 +1,77 @@
|
||||
extends GutTest
|
||||
|
||||
var _config: RuleConfig
|
||||
const _C = preload("res://src/core/constants.gd")
|
||||
|
||||
func before_each():
|
||||
_config = RuleConfig.standard()
|
||||
|
||||
func _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 _cards(ids: Array) -> Array[Card]:
|
||||
var result: Array[Card] = []
|
||||
for spec in ids:
|
||||
if spec is int:
|
||||
result.append(_card(spec as int))
|
||||
elif spec is Array:
|
||||
result.append(_card(spec[0] as int, spec[1] as int))
|
||||
return result
|
||||
|
||||
func test_can_play_fresh_round():
|
||||
var hand: Array[Card] = [_card(0), _card(4)]
|
||||
var table: Array = []
|
||||
var play := HandEvaluator.evaluate(_cards([0]), 5, _config)
|
||||
var result := RuleEngine.can_play(hand, play, table, -1, 5, _config)
|
||||
assert_true(result.ok)
|
||||
|
||||
func test_pure_bomb_beats_mixed():
|
||||
var pure := HandEvaluator.EvaluatedPlay.new()
|
||||
pure.type = _C.TYPE_BOMB
|
||||
pure.primary_rank = 10
|
||||
pure.is_pure_bomb = true
|
||||
var mixed := HandEvaluator.EvaluatedPlay.new()
|
||||
mixed.type = _C.TYPE_BOMB
|
||||
mixed.primary_rank = 10
|
||||
mixed.is_pure_bomb = false
|
||||
assert_eq(RuleEngine.compare_bombs(pure, mixed, _config), 1)
|
||||
assert_eq(RuleEngine.compare_bombs(mixed, pure, _config), -1)
|
||||
|
||||
func test_rocket_beats_bomb():
|
||||
var rocket := HandEvaluator.EvaluatedPlay.new()
|
||||
rocket.type = _C.TYPE_ROCKET
|
||||
rocket.primary_rank = 999
|
||||
var bomb := HandEvaluator.EvaluatedPlay.new()
|
||||
bomb.type = _C.TYPE_BOMB
|
||||
bomb.primary_rank = 14
|
||||
assert_eq(RuleEngine.compare(rocket, bomb, _config), 1)
|
||||
|
||||
func test_same_type_higher_beats():
|
||||
var low := HandEvaluator.EvaluatedPlay.new()
|
||||
low.type = _C.TYPE_SINGLE; low.primary_rank = 3
|
||||
var high := HandEvaluator.EvaluatedPlay.new()
|
||||
high.type = _C.TYPE_SINGLE; high.primary_rank = 14
|
||||
assert_eq(RuleEngine.compare(high, low, _config), 1)
|
||||
|
||||
func test_cannot_play_card_not_in_hand():
|
||||
var hand: Array[Card] = [_card(4)]
|
||||
var play := HandEvaluator.evaluate(_cards([0]), 5, _config) # card 0 not in hand
|
||||
var result := RuleEngine.can_play(hand, play, [], -1, 5, _config)
|
||||
assert_false(result.ok)
|
||||
|
||||
func test_team_partner():
|
||||
var t := Actions.Team.create_team(0, 0, 2)
|
||||
assert_eq(t.teammate_of(0), 2)
|
||||
assert_eq(t.teammate_of(2), 0)
|
||||
assert_true(t.contains(0))
|
||||
Reference in New Issue
Block a user