go_games_collection/senet_game_test.go

279 lines
9.9 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 "testing"
func TestSenetStartingSetupAlternates(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
for sq := 1; sq <= 10; sq++ {
owner, ok := senetCellOwner(g.Board[sq])
if !ok {
t.Fatalf("клетка %d должна быть занята при старте", sq)
}
wantA := sq%2 == 1
if wantA && owner != SenetPlayerA {
t.Errorf("клетка %d (нечётная) должна принадлежать игроку A", sq)
}
if !wantA && owner != SenetPlayerB {
t.Errorf("клетка %d (чётная) должна принадлежать игроку B", sq)
}
}
for sq := 11; sq <= 30; sq++ {
if g.Board[sq] != 0 {
t.Errorf("клетка %d должна быть пуста при старте, получено %d", sq, g.Board[sq])
}
}
}
func TestSenetSwapOnLoneOpponentBlot(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[5] = senetCellValue(SenetPlayerA)
g.Board[8] = senetCellValue(SenetPlayerB)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 3
if err := g.Move(5); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
ownerAt8, _ := senetCellOwner(g.Board[8])
ownerAt5, _ := senetCellOwner(g.Board[5])
if ownerAt8 != SenetPlayerA {
t.Errorf("клетка 8 должна была стать занятой игроком A, получено %v", g.Board[8])
}
if ownerAt5 != SenetPlayerB {
t.Errorf("шашка соперника должна была обменяться местами и оказаться на клетке 5, получено %v", g.Board[5])
}
}
func TestSenetTwoInRowAreProtected(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[5] = senetCellValue(SenetPlayerA)
g.Board[20] = senetCellValue(SenetPlayerA) // вторая шашка — есть куда сходить вперёд, так что откат "хода назад" не включится
g.Board[8] = senetCellValue(SenetPlayerB)
g.Board[9] = senetCellValue(SenetPlayerB)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 3
moves := g.LegalMoves()
for _, mv := range moves {
if mv.From == 5 && !mv.Backward {
t.Errorf("нельзя вставать вперёд на защищённую (2+ подряд) шашку соперника")
}
}
}
func TestSenetThreeInRowBlockPassage(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[5] = senetCellValue(SenetPlayerA)
g.Board[20] = senetCellValue(SenetPlayerA) // вторая шашка с явно легальным ходом — проверка не вакуумна
g.Board[6] = senetCellValue(SenetPlayerB)
g.Board[7] = senetCellValue(SenetPlayerB)
g.Board[8] = senetCellValue(SenetPlayerB)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 5 // 5+5=10, должно пройти НАСКВОЗЬ через 6-7-8
moves := g.LegalMoves()
if len(moves) == 0 {
t.Fatalf("ожидался хотя бы один ход (вторая шашка с 20 точно может сходить)")
}
for _, mv := range moves {
if mv.From == 5 {
t.Errorf("заграждение из трёх подряд должно полностью блокировать проход, даже если конечная клетка сама по себе свободна")
}
}
}
func TestSenetBlockadeDoesNotTurnCorner(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[9] = senetCellValue(SenetPlayerB)
g.Board[10] = senetCellValue(SenetPlayerB)
g.Board[11] = senetCellValue(SenetPlayerB)
if g.senetRunLength(10) >= 3 {
t.Errorf("цепочка не должна пересекать угол 10-11, получено %d", g.senetRunLength(10))
}
}
func TestSenetTrapSquareSendsBackToRebirth(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[24] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 3 // 24+3=27 — ловушка
if err := g.Move(24); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Board[27] != 0 {
t.Errorf("клетка 27 должна была остаться пустой после отскока")
}
owner, ok := senetCellOwner(g.Board[SenetHouseRebirth])
if !ok || owner != SenetPlayerA {
t.Errorf("шашка должна была оказаться на клетке 15 (дом возрождения)")
}
}
func TestSenetTrapBouncesToNearestEmptyBeforeRebirth(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[24] = senetCellValue(SenetPlayerA)
g.Board[SenetHouseRebirth] = senetCellValue(SenetPlayerB)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 3
if err := g.Move(24); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
owner, ok := senetCellOwner(g.Board[SenetHouseRebirth-1])
if !ok || owner != SenetPlayerA {
t.Errorf("при занятой клетке 15 шашка должна была отскочить на ближайшую свободную клетку перед ней (14)")
}
}
func TestSenetExactRollRequiredToBearOffFromHouseThree(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[SenetHouseThree] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 2 // House 28 требует ровно 3
if err := g.Move(SenetHouseThree); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.BorneOff[SenetPlayerA] != 0 {
t.Errorf("бросок 2 не должен был снять шашку с клетки 28 (нужен именно 3)")
}
owner, ok := senetCellOwner(g.Board[30])
if !ok || owner != SenetPlayerA {
t.Errorf("шашка должна была просто переместиться на клетку 30")
}
}
func TestSenetBearsOffWithExactRoll(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[SenetHouseThree] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 3
if err := g.Move(SenetHouseThree); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.BorneOff[SenetPlayerA] != 1 {
t.Errorf("ожидалось снятие шашки с клетки 28 точным броском 3, BorneOff=%d", g.BorneOff[SenetPlayerA])
}
}
func TestSenetCannotBearOffWithoutReachingExitSquare(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[20] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 5
moves := g.LegalMoves()
if len(moves) != 1 || moves[0].From != 20 {
t.Fatalf("ожидался один обычный ход на клетку 25, получено %+v", moves)
}
g.Move(20)
if g.BorneOff[SenetPlayerA] != 0 {
t.Errorf("снятие не должно было произойти вдалеке от выходных клеток")
}
}
func TestSenetWinWhenAllFiveBorneOff(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.BorneOff[SenetPlayerA] = SenetPieceCount - 1
g.Board[SenetBoardSize] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 1
if err := g.Move(SenetBoardSize); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Result == nil || g.Result.Winner != SenetPlayerA {
t.Fatalf("ожидалась победа игрока A, получено Result=%+v", g.Result)
}
}
func TestSenetExtraTurnOnRolls1And4And5(t *testing.T) {
for _, roll := range []int{1, 4, 5} {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[1] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = roll
g.Move(1)
if g.CurrentPlayer != SenetPlayerA {
t.Errorf("бросок %d должен был дать дополнительный ход тому же игроку", roll)
}
}
}
func TestSenetNoExtraTurnOnRolls2And3(t *testing.T) {
for _, roll := range []int{2, 3} {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[1] = senetCellValue(SenetPlayerA)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = roll
g.Move(1)
if g.CurrentPlayer != SenetPlayerB {
t.Errorf("бросок %d не должен был давать дополнительный ход", roll)
}
}
}
func TestSenetMustMoveBackwardWhenNoForwardMovePossible(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[10] = senetCellValue(SenetPlayerA)
g.Board[13] = senetCellValue(SenetPlayerB)
g.Board[14] = senetCellValue(SenetPlayerB)
g.CurrentPlayer = SenetPlayerA
g.Rolled = true
g.DiceValue = 3
moves := g.LegalMoves()
if len(moves) == 0 {
t.Fatalf("ожидался ход назад (10-3=7), так как вперёд ходов нет")
}
for _, mv := range moves {
if !mv.Backward {
t.Errorf("при отсутствии хода вперёд все предложенные ходы должны быть назад")
}
}
}
func TestSenetNoMoveAtAllEndsTurnAfterRoll(t *testing.T) {
g := NewSenetGame(SenetPlayerA)
g.Board = [SenetBoardSize + 1]int{}
g.Board[10] = senetCellValue(SenetPlayerA)
g.Board[12] = senetCellValue(SenetPlayerB)
g.Board[13] = senetCellValue(SenetPlayerB)
g.Board[14] = senetCellValue(SenetPlayerB)
g.Board[6] = senetCellValue(SenetPlayerB)
g.Board[7] = senetCellValue(SenetPlayerB)
g.CurrentPlayer = SenetPlayerA
if err := g.Roll(3); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.CurrentPlayer != SenetPlayerB {
t.Errorf("при полном отсутствии ходов (ни вперёд, ни назад) очередь должна была перейти сопернику")
}
}