go_games_collection/oneohone_game_test.go
2026-07-04 13:51:04 +03:00

352 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import "testing"
func TestOneOhOneDealsCorrectly(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B", "C"}, -1)
for i, p := range g.Players {
want := 5
if i == g.DealerIdx {
want = 4
}
if len(p.Hand) != want {
t.Errorf("игрок %d: ожидалось %d карт, получено %d", i, want, len(p.Hand))
}
}
if len(g.Discard) != 1 {
t.Fatalf("ожидалась 1 карта в сбросе (стартовая), получено %d", len(g.Discard))
}
if len(g.Stock) != 36-14-1 {
t.Fatalf("ожидалось %d карт в колоде добора, получено %d", 36-14-1, len(g.Stock))
}
if g.CurrentPlayerIdx != g.nextActive(g.DealerIdx) {
t.Errorf("первым должен ходить игрок слева от сдающего")
}
}
func TestPlayCardMatchingSuit(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Clubs)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.topDiscard() != (c(Ten, Clubs)) {
t.Errorf("верхняя карта сброса должна была смениться")
}
}
func TestPlayCardMatchingRank(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Nine, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Nine, Hearts)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
}
func TestPlayCardRejectsMismatch(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Hearts)); err != ErrOOOInvalidPlay {
t.Errorf("ожидалась ошибка ErrOOOInvalidPlay, получено: %v", err)
}
}
func TestDrawRejectedWhenPlayableCardExists(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Nine, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.Draw(); err != ErrOOOMustPlay {
t.Errorf("ожидалась ошибка ErrOOOMustPlay, получено: %v", err)
}
}
func TestDrawAutoPlaysWhenDrawnCardMatches(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Hearts)} // не подходит
g.Stock = []Card{c(Nine, Diamonds), c(Two, Clubs)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.Draw(); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.topDiscard() != (c(Nine, Diamonds)) {
t.Errorf("взятая подходящая карта должна была сразу сыграться")
}
if hasCard(g.Players[0].Hand, c(Nine, Diamonds)) {
t.Errorf("сыгранной карты не должно остаться в руке")
}
}
func TestAceSkipsNextPlayer(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B", "C"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ace, Clubs), c(Two, Diamonds)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.Players[2].Hand = []Card{c(Jack, Spades)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ace, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.CurrentPlayerIdx != 2 {
t.Errorf("туз должен был пропустить игрока 1, ожидался ход игрока 2, получено %d", g.CurrentPlayerIdx)
}
if len(g.Players[1].Hand) != 1 {
t.Errorf("пропущенный игрок не должен был брать штрафные карты")
}
}
func TestSevenGivesTwoPenaltyCardsAndSkips(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B", "C"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Seven, Clubs), c(Two, Diamonds)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.Players[2].Hand = []Card{c(Jack, Spades)}
g.Stock = []Card{c(Three, Hearts), c(Four, Hearts), c(Five, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Seven, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if len(g.Players[1].Hand) != 3 {
t.Errorf("игрок 1 должен был взять 2 штрафные карты (1+2=3), получено %d карт", len(g.Players[1].Hand))
}
if g.CurrentPlayerIdx != 2 {
t.Errorf("ход должен был перейти к игроку 2, получено %d", g.CurrentPlayerIdx)
}
}
func TestKingOfSpadesGivesFourPenaltyCards(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Spades)}
g.Players[0].Hand = []Card{c(King, Spades), c(Two, Diamonds)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.Stock = make([]Card, 0, 4)
for i := 0; i < 4; i++ {
g.Stock = append(g.Stock, c(Rank(2+i), Hearts))
}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(King, Spades)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if len(g.Players[1].Hand) != 5 {
t.Errorf("игрок 1 должен был взять 4 штрафные карты (1+4=5), получено %d", len(g.Players[1].Hand))
}
}
func TestOtherKingsHaveNoSpecialEffect(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B", "C"}, -1)
g.Discard = []Card{c(Nine, Hearts)}
g.Players[0].Hand = []Card{c(King, Hearts), c(Two, Diamonds)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.Players[2].Hand = []Card{c(Jack, Spades)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(King, Hearts)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.CurrentPlayerIdx != 1 {
t.Errorf("некозырной король не должен ничего пропускать, ожидался ход игрока 1, получено %d", g.CurrentPlayerIdx)
}
if len(g.Players[1].Hand) != 1 {
t.Errorf("игрок 1 не должен был брать штрафные карты")
}
}
func TestRoundEndScoresRemainingCards(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Clubs)}
g.Players[1].Hand = []Card{c(King, Hearts), c(Ace, Diamonds)} // 4+11=15
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Phase != OneOhOnePhaseRoundOver {
t.Fatalf("раунд должен был завершиться победой игрока 0")
}
if g.Result.Winner != 0 {
t.Fatalf("ожидался победитель 0, получен %d", g.Result.Winner)
}
if g.Result.ScoreDeltas[0] != 0 {
t.Errorf("победитель без дамы должен получить 0 очков за раунд, получено %d", g.Result.ScoreDeltas[0])
}
if g.Result.ScoreDeltas[1] != 15 {
t.Errorf("ожидалось 15 штрафных очков у проигравшего, получено %d", g.Result.ScoreDeltas[1])
}
if g.Players[1].Score != 15 {
t.Errorf("общий счёт игрока 1 должен был стать 15, получено %d", g.Players[1].Score)
}
}
func TestQueenBonusOnWin(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Queen, Clubs)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Queen, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if !g.Result.QueenBonus {
t.Fatalf("ожидался бонус за даму")
}
if g.Result.ScoreDeltas[0] != -20 {
t.Errorf("ожидался бонус -20 за некозырную даму, получено %d", g.Result.ScoreDeltas[0])
}
}
func TestQueenOfHeartsGivesDoubleBonus(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Hearts)}
g.Players[0].Hand = []Card{c(Queen, Hearts)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Queen, Hearts)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Result.ScoreDeltas[0] != -40 {
t.Errorf("ожидался бонус -40 за червовую даму, получено %d", g.Result.ScoreDeltas[0])
}
}
func TestExactly101ResetsToZero(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Players[1].Score = 90 // 90+11(туз)=101
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Clubs)}
g.Players[1].Hand = []Card{c(Ace, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Players[1].Score != 0 {
t.Errorf("ровно 101 должно было обнулить счёт, получено %d", g.Players[1].Score)
}
if len(g.Result.Reset) != 1 || g.Result.Reset[0] != 1 {
t.Errorf("ожидался Reset=[1], получено %v", g.Result.Reset)
}
if g.Players[1].Out {
t.Errorf("игрок с обнулённым счётом не должен выбывать")
}
}
func TestOver101Eliminates(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B", "C"}, -1)
g.Players[1].Score = 95 // 95+11=106 > 101
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Clubs)}
g.Players[1].Hand = []Card{c(Ace, Hearts)}
g.Players[2].Hand = []Card{c(Jack, Spades)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if !g.Players[1].Out {
t.Errorf("игрок с 106 очками должен был выбыть")
}
if g.Result.MatchOver {
t.Errorf("партия не должна была завершиться (остались 2 активных игрока)")
}
}
func TestMatchEndsWhenOneActivePlayerRemains(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Players[1].Score = 95
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Clubs)}
g.Players[1].Hand = []Card{c(Ace, Hearts)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if !g.Result.MatchOver {
t.Fatalf("партия должна была завершиться (остался 1 активный игрок)")
}
if g.Result.MatchWinner != 0 {
t.Errorf("ожидался победитель партии 0, получено %d", g.Result.MatchWinner)
}
}
func TestNextRoundDealerIsRoundWinner(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B", "C"}, -1)
g.Discard = []Card{c(Nine, Clubs)}
g.Players[0].Hand = []Card{c(Ten, Clubs)}
g.Players[1].Hand = []Card{c(Ten, Hearts)}
g.Players[2].Hand = []Card{c(Jack, Spades)}
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if err := g.PlayCard(c(Ten, Clubs)); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.DealerIdx != 0 {
t.Fatalf("сдающим следующего раунда должен стать победитель (0), получено %d", g.DealerIdx)
}
g.NextRound()
if g.Phase != OneOhOnePhasePlay {
t.Fatalf("новый раунд должен был начаться")
}
for i, p := range g.Players {
want := 5
if i == g.DealerIdx {
want = 4
}
if len(p.Hand) != want {
t.Errorf("игрок %d: ожидалось %d карт в новом раунде, получено %d", i, want, len(p.Hand))
}
}
}
func TestStockReshufflesFromDiscardWhenEmpty(t *testing.T) {
g := NewOneOhOneGame([]string{"A", "B"}, -1)
g.Discard = []Card{c(Nine, Clubs), c(Ten, Diamonds), c(Jack, Diamonds)}
g.Stock = nil
g.Players[0].Hand = []Card{c(Eight, Clubs)} // не подходит ни по масти (clubs vs diamonds), ни по рангу (8 vs J)
g.CurrentPlayerIdx = 0
g.Phase = OneOhOnePhasePlay
if g.stockExhausted() {
t.Fatalf("колода не должна считаться исчерпанной — под верхней картой сброса есть ещё карты")
}
if err := g.Draw(); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if len(g.Discard) > 2 {
t.Errorf("после перетасовки в сбросе не должно накопиться старых карт, получено %d карт", len(g.Discard))
}
}