101 lines
2.5 KiB
Go
101 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"math/rand"
|
|
"testing"
|
|
)
|
|
|
|
// klondikeAutoStep пытается совершить один разумный ход: сначала
|
|
// проверяет, можно ли отправить видимую карту (из отбоя или любой
|
|
// колонны) на фундамент; если нет — пробует случайный допустимый ход
|
|
// между колоннами или из отбоя в колонну; если и это невозможно —
|
|
// добирает из колоды. Возвращает false, если вообще ничего сделать
|
|
// нельзя (колода и отбой пусты, и нет легальных ходов) — сигнал
|
|
// закончить прогон.
|
|
func klondikeAutoStep(g *KlondikeGameState, rnd *rand.Rand) bool {
|
|
for col := 0; col < 7; col++ {
|
|
if g.MoveTableauToFoundation(col) == nil {
|
|
return true
|
|
}
|
|
}
|
|
if g.MoveWasteToFoundation() == nil {
|
|
return true
|
|
}
|
|
|
|
order := rnd.Perm(7)
|
|
for _, from := range order {
|
|
destOrder := rnd.Perm(7)
|
|
for _, to := range destOrder {
|
|
if from == to {
|
|
continue
|
|
}
|
|
if g.MoveTableauToTableau(from, to) == nil {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
for _, to := range rnd.Perm(7) {
|
|
if g.MoveWasteToTableau(to) == nil {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if g.DrawStock() == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestKlondikeAutoPlayManyGamesNoCorruption(t *testing.T) {
|
|
rnd := rand.New(rand.NewSource(1))
|
|
|
|
for game := 0; game < 200; game++ {
|
|
g := NewKlondikeGame()
|
|
|
|
const maxSteps = 3000
|
|
stalledStreak := 0
|
|
for step := 0; step < maxSteps; step++ {
|
|
progressed := klondikeAutoStep(g, rnd)
|
|
if !progressed {
|
|
stalledStreak++
|
|
if stalledStreak > 3 {
|
|
break
|
|
}
|
|
} else {
|
|
stalledStreak = 0
|
|
}
|
|
if g.Won {
|
|
break
|
|
}
|
|
|
|
seen := map[Card]bool{}
|
|
total := 0
|
|
checkAdd := func(c Card) {
|
|
if seen[c] {
|
|
t.Fatalf("игра %d, шаг %d: карта %v встретилась дважды", game, step, c)
|
|
}
|
|
seen[c] = true
|
|
total++
|
|
}
|
|
for col := 0; col < 7; col++ {
|
|
for _, kc := range g.Tableau[col] {
|
|
checkAdd(kc.Card)
|
|
}
|
|
}
|
|
for _, c := range g.Stock {
|
|
checkAdd(c)
|
|
}
|
|
for _, c := range g.Waste {
|
|
checkAdd(c)
|
|
}
|
|
for s, r := range g.Foundations {
|
|
for rank := 1; rank <= r; rank++ {
|
|
checkAdd(Card{Suit: Suit(s), Rank: Rank(rank)})
|
|
}
|
|
}
|
|
if total != 52 {
|
|
t.Fatalf("игра %d, шаг %d: ожидалось 52 карты в игре, получено %d", game, step, total)
|
|
}
|
|
}
|
|
}
|
|
}
|