go_games_collection/blackjack_tui_test.go
2026-07-12 13:49:34 +03:00

211 lines
7.4 KiB
Go
Raw Permalink 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 (
"strings"
"testing"
)
func TestBlackjackModelDealsCorrectPlayerCount(t *testing.T) {
for n := 1; n <= 6; n++ {
m := NewBlackjackModel(n, 100, 10)
if len(m.game.Players) != n {
t.Errorf("ожидалось %d игроков, получено %d", n, len(m.game.Players))
}
}
}
func TestBlackjackModelClampsPlayerCount(t *testing.T) {
m := NewBlackjackModel(0, 100, 10)
if len(m.game.Players) != 1 {
t.Errorf("ожидалось ограничение снизу до 1 игрока, получено %d", len(m.game.Players))
}
m = NewBlackjackModel(10, 100, 10)
if len(m.game.Players) != 6 {
t.Errorf("ожидалось ограничение сверху до 6 игроков, получено %d", len(m.game.Players))
}
}
func TestBlackjackModelHitViaKeys(t *testing.T) {
m := NewBlackjackModel(1, 100, 10)
resetForManualBout(m.game)
m.game.Players[0].Hand = []Card{c(King, Clubs), c(Queen, Hearts)}
m.game.CurrentPlayerIdx = 0
m.game.Phase = BJPhasePlayerTurn
m.game.Deck = &Deck{cards: []Card{c(Five, Diamonds)}}
next, _ := m.Update(key("h"))
m = next.(BlackjackModel)
if m.isError {
t.Fatalf("неожиданная ошибка: %s", m.message)
}
if m.game.Players[0].Status != BJStatusBusted {
t.Fatalf("ожидался перебор, статус: %v", m.game.Players[0].Status)
}
}
func TestBlackjackModelStandViaKeys(t *testing.T) {
m := NewBlackjackModel(1, 100, 10)
resetForManualBout(m.game)
m.game.CurrentPlayerIdx = 0
m.game.Phase = BJPhasePlayerTurn
next, _ := m.Update(key("s"))
m = next.(BlackjackModel)
if m.isError {
t.Fatalf("неожиданная ошибка: %s", m.message)
}
if m.game.Phase != BJPhaseRoundOver {
t.Fatalf("с одним игроком после stand раунд должен был сразу завершиться")
}
}
func TestBlackjackModelDoubleViaKeys(t *testing.T) {
m := NewBlackjackModel(1, 100, 10)
resetForManualBout(m.game)
m.game.Players[0].Hand = []Card{c(Five, Clubs), c(Six, Hearts)}
m.game.CurrentPlayerIdx = 0
m.game.Phase = BJPhasePlayerTurn
m.game.Deck = &Deck{cards: []Card{c(King, Diamonds)}}
next, _ := m.Update(key("d"))
m = next.(BlackjackModel)
if m.isError {
t.Fatalf("неожиданная ошибка: %s", m.message)
}
if m.game.Players[0].Bet != 20 {
t.Fatalf("ставка должна была удвоиться, получено %d", m.game.Players[0].Bet)
}
}
func TestBlackjackModelQReturnsToMenu(t *testing.T) {
m := NewBlackjackModel(2, 100, 10)
next, _ := m.Update(key("q"))
m = next.(BlackjackModel)
if !m.backToMenu {
t.Fatalf("ожидался флаг backToMenu после 'q'")
}
if m.quitting {
t.Fatalf("'q' не должен закрывать всё приложение")
}
}
func TestBlackjackModelNewRoundCarriesBalance(t *testing.T) {
m := NewBlackjackModel(1, 100, 10)
resetForManualBout(m.game)
m.game.CurrentPlayerIdx = 0
m.game.Phase = BJPhasePlayerTurn
next, _ := m.Update(key("s"))
m = next.(BlackjackModel)
if m.game.Phase != BJPhaseRoundOver {
t.Fatalf("раунд должен был завершиться")
}
balanceAfterRound := m.game.Players[0].Balance
next, _ = m.Update(key("n"))
m = next.(BlackjackModel)
// с одним игроком новый раунд может мгновенно разрешиться сам,
// если случайно выпадет блэкджек с раздачи — это ожидаемое
// поведение, а не баг, поэтому проверяем именно перенесённый
// капитал (preRoundBalance), а не итоговый Balance
if m.game.preRoundBalance[0] != balanceAfterRound {
t.Fatalf("капитал должен был перенестись в новый раунд: ожидалось %d, получено %d",
balanceAfterRound, m.game.preRoundBalance[0])
}
}
// TestBlackjackModelFullAutoPlaythrough прогоняет TUI-модель от
// начала до конца для человека, отыгрывающего по той же логике, что
// и бот, но через реальные нажатия клавиш — для нескольких
// вариантов числа игроков и нескольких раундов подряд.
func TestBlackjackModelFullAutoPlaythrough(t *testing.T) {
bot := BlackjackBot{}
for n := 1; n <= 6; n++ {
m := NewBlackjackModel(n, 1000, 10)
for round := 0; round < 20; round++ {
const maxSteps = 200
steps := 0
for m.game.Phase != BJPhaseRoundOver && steps < maxSteps {
steps++
if m.game.CurrentPlayerIdx != m.humanIdx {
next, _ := m.Update(botMoveMsg{})
m = next.(BlackjackModel)
continue
}
action := bot.DecideAction(m.game)
var k string
switch action {
case "double":
k = "d"
case "hit":
k = "h"
default:
k = "s"
}
next, _ := m.Update(key(k))
m = next.(BlackjackModel)
}
if steps >= maxSteps {
t.Fatalf("игроков %d, раунд %d: раунд через TUI не завершился за %d шагов", n, round, maxSteps)
}
if m.game.Result == nil {
t.Fatalf("игроков %d, раунд %d: раунд завершился без результата", n, round)
}
next, _ := m.Update(key("n"))
m = next.(BlackjackModel)
}
}
}
func TestBlackjackModelShowsMessageWhenBetReduced(t *testing.T) {
m := NewBlackjackModel(1, 100, 10)
for m.game.Phase == BJPhasePlayerTurn {
if err := m.game.Stand(); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
}
m.game.Players[m.humanIdx].Balance = 4 // меньше полной ставки 10, но больше нуля
next, _ := m.Update(key("n"))
m = next.(BlackjackModel)
if !m.game.RoundBetReduced[m.humanIdx] {
t.Fatalf("ожидался флаг урезанной ставки")
}
if m.game.Players[m.humanIdx].Bet != 4 {
t.Errorf("ставка должна была урезаться до 4, получено %d", m.game.Players[m.humanIdx].Bet)
}
if m.message == "" {
t.Errorf("ожидалось информационное сообщение об урезанной ставке")
}
if m.isError {
t.Errorf("сообщение об урезанной ставке не должно быть ошибкой")
}
}
func TestBlackjackModelBankruptBlocksNextRoundAndShowsMessage(t *testing.T) {
m := NewBlackjackModel(1, 100, 10)
for m.game.Phase == BJPhasePlayerTurn {
if err := m.game.Stand(); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
}
m.game.Players[m.humanIdx].Balance = 0
handsBefore := len(m.game.Players[m.humanIdx].Hand)
if !m.humanIsBankrupt() {
t.Fatalf("ожидалось, что humanIsBankrupt() вернёт true при нулевом балансе")
}
view := m.View()
if !strings.Contains(view, T("blackjack.bankrupt")) {
t.Errorf("экран должен был показать сообщение о банкротстве, получено:\n%s", view)
}
next, _ := m.Update(key("n"))
m = next.(BlackjackModel)
if m.game.Phase != BJPhaseRoundOver {
t.Errorf("нажатие 'n' при банкротстве не должно было начинать новый раунд")
}
if len(m.game.Players[m.humanIdx].Hand) != handsBefore {
t.Errorf("рука не должна была измениться — новый раунд не должен был начаться")
}
}