151 lines
4.3 KiB
Go
151 lines
4.3 KiB
Go
package main
|
||
|
||
import "testing"
|
||
|
||
func TestThousandModelDealsThreePlayers(t *testing.T) {
|
||
m := NewThousandModel()
|
||
if len(m.game.Players) != 3 {
|
||
t.Errorf("ожидалось 3 игрока, получено %d", len(m.game.Players))
|
||
}
|
||
}
|
||
|
||
func TestThousandModelBidViaKeys(t *testing.T) {
|
||
m := NewThousandModel()
|
||
m.game.CurrentBidderIdx = m.humanIdx
|
||
m.bidAmount = 100
|
||
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(ThousandModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка: %s", m.message)
|
||
}
|
||
if m.game.HighBid != 100 {
|
||
t.Fatalf("ожидалась ставка 100, получено %d", m.game.HighBid)
|
||
}
|
||
}
|
||
|
||
func TestThousandModelPassRejectedForFirstBidder(t *testing.T) {
|
||
m := NewThousandModel()
|
||
m.game.CurrentBidderIdx = m.humanIdx
|
||
m.game.firstBidderIdx = m.humanIdx
|
||
m.game.bidsMade = 0
|
||
|
||
next, _ := m.Update(key("p"))
|
||
m = next.(ThousandModel)
|
||
if !m.isError {
|
||
t.Fatalf("первый ходящий не может пасовать, ошибка ожидалась")
|
||
}
|
||
}
|
||
|
||
func TestThousandModelDiscardViaKeys(t *testing.T) {
|
||
m := NewThousandModel()
|
||
m.game.CurrentBidderIdx = m.humanIdx
|
||
m.bidAmount = 100
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(ThousandModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка ставки: %s", m.message)
|
||
}
|
||
if err := m.game.Pass(); err != nil {
|
||
t.Fatalf("ошибка подготовки: %v", err)
|
||
}
|
||
if err := m.game.Pass(); err != nil {
|
||
t.Fatalf("ошибка подготовки: %v", err)
|
||
}
|
||
if m.game.Phase != ThousandPhaseDiscard {
|
||
t.Fatalf("ожидалась фаза снoса")
|
||
}
|
||
if m.game.DeclarerIdx != m.humanIdx {
|
||
t.Fatalf("человек должен был стать declarer")
|
||
}
|
||
m.cursor = 0
|
||
|
||
next, _ = m.Update(key("enter"))
|
||
m = next.(ThousandModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка при первом сносе: %s", m.message)
|
||
}
|
||
if len(m.game.Players[m.humanIdx].Hand) != 9 {
|
||
t.Fatalf("после первого сноса должно остаться 9 карт, получено %d", len(m.game.Players[m.humanIdx].Hand))
|
||
}
|
||
}
|
||
|
||
func TestThousandModelQReturnsToMenu(t *testing.T) {
|
||
m := NewThousandModel()
|
||
next, _ := m.Update(key("q"))
|
||
m = next.(ThousandModel)
|
||
if !m.backToMenu {
|
||
t.Fatalf("ожидался флаг backToMenu после 'q'")
|
||
}
|
||
if m.quitting {
|
||
t.Fatalf("'q' не должен закрывать всё приложение")
|
||
}
|
||
}
|
||
|
||
// TestThousandModelFullAutoPlaythrough прогоняет TUI-модель от
|
||
// начала до конца нескольких раздач, где "человек" отыгрывает свои
|
||
// решения через реальные нажатия клавиш (используя ту же логику,
|
||
// что и бот).
|
||
func TestThousandModelFullAutoPlaythrough(t *testing.T) {
|
||
m := NewThousandModel()
|
||
bot := ThousandBot{}
|
||
|
||
const maxHands = 30
|
||
for hand := 0; hand < maxHands; hand++ {
|
||
const maxSteps = 3000
|
||
steps := 0
|
||
for m.game.Phase != ThousandPhaseHandOver && steps < maxSteps {
|
||
steps++
|
||
if m.actorForPhase() != m.humanIdx {
|
||
next, _ := m.Update(botMoveMsg{})
|
||
m = next.(ThousandModel)
|
||
continue
|
||
}
|
||
|
||
switch m.game.Phase {
|
||
case ThousandPhaseBidding:
|
||
if amount, ok := bot.DecideBid(m.game); ok {
|
||
m.bidAmount = amount
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(ThousandModel)
|
||
} else {
|
||
next, _ := m.Update(key("p"))
|
||
m = next.(ThousandModel)
|
||
}
|
||
case ThousandPhaseDiscard:
|
||
discards := bot.DecideDiscards(m.game.Players[m.humanIdx].Hand)
|
||
for _, card := range discards {
|
||
for i, hc := range m.currentHand() {
|
||
if hc == card {
|
||
m.cursor = i
|
||
break
|
||
}
|
||
}
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(ThousandModel)
|
||
}
|
||
case ThousandPhaseTrick:
|
||
card := bot.DecidePlay(m.game)
|
||
for i, hc := range m.currentHand() {
|
||
if hc == card {
|
||
m.cursor = i
|
||
break
|
||
}
|
||
}
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(ThousandModel)
|
||
}
|
||
}
|
||
if steps >= maxSteps {
|
||
t.Fatalf("раздача %d: не завершилась через TUI за %d шагов (фаза %v)", hand, maxSteps, m.game.Phase)
|
||
}
|
||
if m.game.Result == nil {
|
||
t.Fatalf("раздача %d: завершилась без результата", hand)
|
||
}
|
||
if m.game.Result.MatchOver {
|
||
return
|
||
}
|
||
next, _ := m.Update(key("n"))
|
||
m = next.(ThousandModel)
|
||
}
|
||
}
|