129 lines
3.7 KiB
Go
129 lines
3.7 KiB
Go
package game
|
|
|
|
import "testing"
|
|
|
|
func TestParseCardRoundTrip(t *testing.T) {
|
|
cases := []string{"AS", "10H", "2C", "KD", "JS", "QH"}
|
|
for _, code := range cases {
|
|
c, err := ParseCard(code)
|
|
if err != nil {
|
|
t.Fatalf("parse %q: %v", code, err)
|
|
}
|
|
if c.String() != code {
|
|
t.Errorf("roundtrip %q -> %q", code, c.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBeats(t *testing.T) {
|
|
trump := Hearts
|
|
lead := Spades
|
|
// آتو بر غیرآتو میبرد
|
|
if !beats(Card{Hearts, 2}, Card{Spades, Ace}, trump, lead) {
|
|
t.Error("trump 2 should beat non-trump Ace")
|
|
}
|
|
// بالاترین خال زمینه بر کارت خال دیگر (غیرآتو) میبرد
|
|
if !beats(Card{Spades, 5}, Card{Diamonds, Ace}, trump, lead) {
|
|
t.Error("lead-suit card should beat off-suit card")
|
|
}
|
|
// کارت خارج از خال زمینه و غیرآتو نمیبرد
|
|
if beats(Card{Diamonds, Ace}, Card{Spades, 2}, trump, lead) {
|
|
t.Error("off-suit non-trump should not beat lead suit")
|
|
}
|
|
}
|
|
|
|
func TestMustFollowSuit(t *testing.T) {
|
|
g := NewGame(7, 0)
|
|
if err := g.ChooseTrump(0, Hearts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// حاکم (seat 0) شروع میکند. یک کارت از دستش بازی میکنیم.
|
|
lead := g.hands[0][0]
|
|
if err := g.PlayCard(0, lead); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// بازیکن بعدی اگر کارت همخال دارد باید همان را بزند.
|
|
next := g.Turn
|
|
if hasSuit(g.hands[next], lead.Suit) {
|
|
// یک کارت با خال متفاوت پیدا کن
|
|
for _, c := range g.hands[next] {
|
|
if c.Suit != lead.Suit {
|
|
if err := g.PlayCard(next, c); err != ErrMustFollow {
|
|
t.Fatalf("expected ErrMustFollow, got %v", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// playFullGame یک بازی کامل را با یک ربات ساده تا پایان پیش میبرد و
|
|
// تمام نامتغیرها (invariants) را بررسی میکند.
|
|
func TestFullGamePlaysOut(t *testing.T) {
|
|
g := NewGame(7, FirstHakem())
|
|
|
|
for hand := 0; g.Phase != PhaseGameOver; hand++ {
|
|
if hand > 100 {
|
|
t.Fatal("too many hands; engine likely stuck")
|
|
}
|
|
// انتخاب حکم توسط حاکم
|
|
if g.Phase != PhaseChooseTrump {
|
|
t.Fatalf("expected choose_trump, got %s", g.Phase)
|
|
}
|
|
if err := g.ChooseTrump(g.Hakem, g.hands[g.Hakem][0].Suit); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// بررسی: هر بازیکن دقیقاً ۱۳ کارت دارد
|
|
for s := 0; s < 4; s++ {
|
|
if g.HandCount(s) != 13 {
|
|
t.Fatalf("seat %d has %d cards after deal", s, g.HandCount(s))
|
|
}
|
|
}
|
|
|
|
// بازی دستها تا پایان هَند
|
|
for g.Phase == PhasePlaying {
|
|
if g.TrickDone {
|
|
if err := g.CollectTrick(); err != nil {
|
|
t.Fatalf("collect trick: %v", err)
|
|
}
|
|
continue
|
|
}
|
|
seat := g.Turn
|
|
card := legalMove(g, seat)
|
|
if err := g.PlayCard(seat, card); err != nil {
|
|
t.Fatalf("play %s by seat %d: %v", card, seat, err)
|
|
}
|
|
}
|
|
|
|
// بعد از هَند: یا تمام شده یا hand_over
|
|
if g.Phase == PhaseHandOver {
|
|
r := g.LastResult
|
|
if r.Points < 1 || r.Points > 2 {
|
|
t.Fatalf("unexpected points %d", r.Points)
|
|
}
|
|
if err := g.NextHand(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// در پایان یک تیم باید به امتیاز هدف رسیده باشد
|
|
if g.Scores[0] < g.TargetScore && g.Scores[1] < g.TargetScore {
|
|
t.Fatalf("game over but no team reached target: %v", g.Scores)
|
|
}
|
|
t.Logf("final scores: %v", g.Scores)
|
|
}
|
|
|
|
// legalMove یک حرکت مجاز برای ربات انتخاب میکند (follow suit در صورت امکان).
|
|
func legalMove(g *Game, seat int) Card {
|
|
hand := g.hands[seat]
|
|
if g.leadSet {
|
|
for _, c := range hand {
|
|
if c.Suit == g.LeadSuit {
|
|
return c
|
|
}
|
|
}
|
|
}
|
|
return hand[0]
|
|
}
|