go_games_collection/poker_tui_test.go
2026-07-10 09:12:06 +03:00

215 lines
6.7 KiB
Go

package main
import (
"fmt"
"testing"
)
func TestPokerModelBotsGetRandomDifficulties(t *testing.T) {
m := NewPokerModel(4)
if len(m.bots) != 3 {
t.Fatalf("ожидалось 3 бота (4 игрока минус человек), получено %d", len(m.bots))
}
for seat, bot := range m.bots {
if bot.Difficulty < PokerDifficultyEasy || bot.Difficulty > PokerDifficultyHard {
t.Errorf("бот на месте %d получил недопустимый уровень сложности %v", seat, bot.Difficulty)
}
}
}
func TestPokerModelHumanIsSeatZero(t *testing.T) {
m := NewPokerModel(3)
if m.humanSeat != 0 {
t.Errorf("человек должен всегда занимать место 0, получено %d", m.humanSeat)
}
if _, isBot := m.bots[m.humanSeat]; isBot {
t.Errorf("на месте человека не должно быть бота")
}
}
func TestPokerModelFoldViaKey(t *testing.T) {
m := NewPokerModel(3)
m.game.CurrentPlayerIdx = m.humanSeat
next, _ := m.Update(key("1"))
m = next.(PokerModel)
if !m.game.Players[m.humanSeat].Folded {
t.Errorf("после выбора действия 'сбросить' игрок должен был сбросить карты")
}
}
func TestPokerModelRejectsActionWhenNotHumanTurn(t *testing.T) {
m := NewPokerModel(3)
m.game.CurrentPlayerIdx = (m.humanSeat + 1) % 3
next, _ := m.Update(key("1"))
m = next.(PokerModel)
if m.game.Players[m.humanSeat].Folded {
t.Errorf("действие не должно было приняться — сейчас не ход человека")
}
}
func TestPokerModelBetAmountSubmode(t *testing.T) {
m := NewPokerModel(2)
m.game.CurrentPlayerIdx = m.humanSeat
actions := m.pokerHumanActions()
betIdx := -1
for i, a := range actions {
if a == "bet" {
betIdx = i
}
}
if betIdx < 0 {
t.Fatalf("ожидалось действие 'bet' среди доступных: %v", actions)
}
next, _ := m.Update(key(fmt.Sprintf("%d", betIdx+1)))
m = next.(PokerModel)
if !m.choosingBetAmount {
t.Fatalf("ожидался переход в подрежим выбора суммы ставки")
}
before := m.betAmount
next, _ = m.Update(key("up"))
m = next.(PokerModel)
if m.betAmount != before+m.game.BigBlind {
t.Errorf("ожидалось увеличение ставки на большой блайнд, было %d, стало %d", before, m.betAmount)
}
next, _ = m.Update(key("enter"))
m = next.(PokerModel)
if m.choosingBetAmount {
t.Errorf("подрежим должен был закрыться после подтверждения")
}
human := m.game.Players[m.humanSeat]
if human.CurrentBet != before+m.game.BigBlind {
t.Errorf("ставка должна была реально примениться, ожидалось %d, получено %d", before+m.game.BigBlind, human.CurrentBet)
}
}
func TestPokerModelBetAmountCancelDoesNotApply(t *testing.T) {
m := NewPokerModel(2)
m.game.CurrentPlayerIdx = m.humanSeat
betBefore := m.game.Players[m.humanSeat].CurrentBet
actions := m.pokerHumanActions()
betIdx := 0
for i, a := range actions {
if a == "bet" {
betIdx = i
}
}
next, _ := m.Update(key(fmt.Sprintf("%d", betIdx+1)))
m = next.(PokerModel)
next, _ = m.Update(key("esc"))
m = next.(PokerModel)
if m.choosingBetAmount {
t.Errorf("esc должен был закрыть подрежим без применения ставки")
}
if m.game.Players[m.humanSeat].CurrentBet != betBefore {
t.Errorf("отменённая ставка не должна была примениться")
}
}
func TestPokerModelBotPlaysWhenNotHumanTurn(t *testing.T) {
m := NewPokerModel(3)
for m.game.CurrentPlayerIdx == m.humanSeat {
m.game.CurrentPlayerIdx = (m.game.CurrentPlayerIdx + 1) % 3
}
next, _ := m.Update(botMoveMsg{})
m = next.(PokerModel)
if m.isError {
t.Errorf("неожиданная ошибка хода бота: %s", m.message)
}
}
func TestPokerModelIgnoresBotMoveMsgOnHumanTurn(t *testing.T) {
m := NewPokerModel(3)
m.game.CurrentPlayerIdx = m.humanSeat
before := *m.game
next, _ := m.Update(botMoveMsg{})
m = next.(PokerModel)
if m.game.CurrentPlayerIdx != before.CurrentPlayerIdx {
t.Errorf("botMoveMsg не должно было ничего менять, пока сейчас ход человека")
}
}
func TestPokerModelQReturnsToMenuNotQuitsApp(t *testing.T) {
m := NewPokerModel(3)
next, _ := m.Update(key("q"))
m = next.(PokerModel)
if !m.backToMenu {
t.Errorf("ожидался флаг backToMenu после 'q'")
}
if m.quitting {
t.Errorf("'q' не должно завершать приложение целиком")
}
}
func TestPokerModelNextHandViaKey(t *testing.T) {
m := NewPokerModel(2)
m.game.Players[0].HoleCards = [2]Card{c(Ace, Spades), c(Ace, Hearts)}
m.game.Players[1].HoleCards = [2]Card{c(Two, Clubs), c(Seven, Diamonds)}
m.game.Board = []Card{c(Ace, Clubs), c(King, Spades), c(Queen, Hearts), c(Four, Diamonds), c(Nine, Clubs)}
m.game.Players[0].TotalContributed = 100
m.game.Players[1].TotalContributed = 100
m.game.resolveShowdown()
next, _ := m.Update(key("n"))
m = next.(PokerModel)
if m.game.Phase == PokerPhaseShowdown {
t.Errorf("после 'n' должна была начаться новая раздача")
}
}
func TestPokerModelFullTournamentAutoPlaythrough(t *testing.T) {
m := NewPokerModel(4)
humanBot := PokerBot{Difficulty: PokerDifficultyMedium}
steps := 0
for m.game.Phase != PokerPhaseTournamentOver && steps < 50000 {
steps++
g := m.game
if g.Phase == PokerPhaseShowdown {
next, _ := m.Update(key("n"))
m = next.(PokerModel)
continue
}
if g.CurrentPlayerIdx != m.humanSeat {
next, _ := m.Update(botMoveMsg{})
m = next.(PokerModel)
continue
}
action := humanBot.DecideAction(g, m.humanSeat, m.rng)
actions := m.pokerHumanActions()
labelToKey := map[string]int{}
for i, a := range actions {
labelToKey[a] = i + 1
}
var keyStr string
switch action.Kind {
case "fold":
keyStr = fmt.Sprintf("%d", labelToKey["fold"])
case "check":
if idx, ok := labelToKey["check"]; ok {
keyStr = fmt.Sprintf("%d", idx)
} else {
keyStr = fmt.Sprintf("%d", labelToKey["call"])
}
case "call":
keyStr = fmt.Sprintf("%d", labelToKey["call"])
case "bet":
keyStr = fmt.Sprintf("%d", labelToKey["bet"])
}
next, _ := m.Update(key(keyStr))
m = next.(PokerModel)
if m.choosingBetAmount {
next, _ = m.Update(key("enter"))
m = next.(PokerModel)
}
if m.isError {
t.Fatalf("неожиданная ошибка при автопрогоне: %s", m.message)
}
}
if m.game.Phase != PokerPhaseTournamentOver {
t.Fatalf("турнир не завершился за %d шагов", steps)
}
}