go_games_collection/checkers_game_test.go
2026-07-04 13:51:04 +03:00

235 lines
8.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 TestNewCheckersGameSetup(t *testing.T) {
g := NewCheckersGame()
whiteCount, blackCount := 0, 0
for r := 0; r < 8; r++ {
for c := 0; c < 8; c++ {
p := g.Board[r][c]
if p == nil {
continue
}
if (r+c)%2 == 0 {
t.Errorf("шашка на светлой клетке (%d,%d)", r, c)
}
if p.Color == CheckersWhite {
whiteCount++
} else {
blackCount++
}
}
}
if whiteCount != 12 || blackCount != 12 {
t.Errorf("ожидалось по 12 шашек, получено white=%d black=%d", whiteCount, blackCount)
}
if g.Turn != CheckersWhite {
t.Errorf("первый ход должен быть за белыми")
}
}
func TestSimpleManMoveForwardOnly(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[5][2] = &CheckersPiece{Color: CheckersWhite}
if err := g.Move(CheckersPos{5, 2}, CheckersPos{4, 1}); err != nil {
t.Fatalf("неожиданная ошибка хода вперёд: %v", err)
}
}
func TestSimpleManCannotMoveBackward(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[5][2] = &CheckersPiece{Color: CheckersWhite}
if err := g.Move(CheckersPos{5, 2}, CheckersPos{6, 1}); err != ErrCheckersInvalidMove {
t.Errorf("простая шашка не должна ходить назад, ожидалась ErrCheckersInvalidMove, получено: %v", err)
}
}
func TestManCapturesBackward(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[4][3] = &CheckersPiece{Color: CheckersWhite}
g.Board[5][4] = &CheckersPiece{Color: CheckersBlack}
if err := g.Move(CheckersPos{4, 3}, CheckersPos{6, 5}); err != nil {
t.Fatalf("шашка должна была взять назад: %v", err)
}
if g.Board[5][4] != nil {
t.Errorf("взятая шашка должна была исчезнуть с доски")
}
if g.Board[6][5] == nil {
t.Errorf("шашка должна была оказаться на клетке приземления")
}
}
func TestCaptureIsMandatory(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[5][2] = &CheckersPiece{Color: CheckersWhite}
g.Board[4][3] = &CheckersPiece{Color: CheckersBlack}
g.Board[5][6] = &CheckersPiece{Color: CheckersWhite}
if err := g.Move(CheckersPos{5, 6}, CheckersPos{4, 5}); err != ErrCheckersMustCapture {
t.Errorf("простой ход должен быть запрещён при доступном взятии, получено: %v", err)
}
if err := g.Move(CheckersPos{5, 2}, CheckersPos{3, 4}); err != nil {
t.Fatalf("взятие должно было пройти: %v", err)
}
}
func TestMultiJumpMustContinueWithSamePiece(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[6][1] = &CheckersPiece{Color: CheckersWhite}
g.Board[5][2] = &CheckersPiece{Color: CheckersBlack}
g.Board[3][4] = &CheckersPiece{Color: CheckersBlack}
g.Board[6][7] = &CheckersPiece{Color: CheckersWhite} // другая белая шашка — ходить ею сейчас нельзя
if err := g.Move(CheckersPos{6, 1}, CheckersPos{4, 3}); err != nil {
t.Fatalf("первое взятие должно было пройти: %v", err)
}
if g.ChainPos == nil {
t.Fatalf("ожидалось продолжение серии взятий той же шашкой")
}
if g.Turn != CheckersWhite {
t.Errorf("ход не должен был перейти сопернику в середине серии")
}
if err := g.Move(CheckersPos{6, 7}, CheckersPos{5, 6}); err != ErrCheckersMustContinue {
t.Errorf("ход другой шашкой в середине серии взятий должен быть запрещён, получено: %v", err)
}
if err := g.Move(CheckersPos{4, 3}, CheckersPos{2, 5}); err != nil {
t.Fatalf("второе взятие в серии должно было пройти: %v", err)
}
if g.ChainPos != nil {
t.Errorf("серия должна была завершиться — больше взятий нет")
}
if g.Turn != CheckersBlack {
t.Errorf("ход должен был перейти чёрным после завершения серии")
}
if g.Board[5][2] != nil || g.Board[3][4] != nil {
t.Errorf("обе взятые шашки должны были исчезнуть с доски")
}
}
func TestPromotionToKing(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[1][2] = &CheckersPiece{Color: CheckersWhite}
if err := g.Move(CheckersPos{1, 2}, CheckersPos{0, 1}); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if !g.Board[0][1].King {
t.Errorf("шашка должна была стать дамкой на последней горизонтали")
}
}
func TestPromotionDuringCaptureContinuesAsKing(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[2][1] = &CheckersPiece{Color: CheckersWhite}
g.Board[1][2] = &CheckersPiece{Color: CheckersBlack}
g.Board[2][5] = &CheckersPiece{Color: CheckersBlack}
if err := g.Move(CheckersPos{2, 1}, CheckersPos{0, 3}); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if !g.Board[0][3].King {
t.Fatalf("шашка должна была стать дамкой при взятии на последней горизонтали")
}
if g.ChainPos == nil {
t.Fatalf("если дальнейшее взятие возможно дамкой — серия должна продолжиться")
}
}
func TestKingFlyingMove(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[4][4] = &CheckersPiece{Color: CheckersWhite, King: true}
if err := g.Move(CheckersPos{4, 4}, CheckersPos{1, 1}); err != nil {
t.Fatalf("дамка должна летать на любое расстояние по диагонали: %v", err)
}
}
func TestKingFlyingCaptureWithChoiceOfLanding(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[7][0] = &CheckersPiece{Color: CheckersWhite, King: true}
g.Board[4][3] = &CheckersPiece{Color: CheckersBlack}
options := g.captureMoveOptions(CheckersPos{7, 0})
if len(options) == 0 {
t.Fatalf("ожидались варианты взятия дамкой")
}
landed := false
for _, opt := range options {
if opt.To == (CheckersPos{2, 5}) {
landed = true
}
}
if !landed {
t.Errorf("среди вариантов должна быть клетка (2,5)")
}
if err := g.Move(CheckersPos{7, 0}, CheckersPos{1, 6}); err != nil {
t.Fatalf("выбор дальней клетки приземления должен быть допустим: %v", err)
}
if g.Board[4][3] != nil {
t.Errorf("взятая шашка должна была исчезнуть")
}
}
func TestKingCannotJumpOverTwoPiecesInARow(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[7][0] = &CheckersPiece{Color: CheckersWhite, King: true}
g.Board[4][3] = &CheckersPiece{Color: CheckersBlack}
g.Board[3][4] = &CheckersPiece{Color: CheckersBlack}
options := g.captureMoveOptions(CheckersPos{7, 0})
for _, opt := range options {
if opt.To.Row <= 3 {
t.Errorf("нельзя приземлиться за второй подряд фигурой, но получен вариант %v", opt.To)
}
}
}
func TestGameOverWhenNoLegalMoves(t *testing.T) {
g := &CheckersGameState{Turn: CheckersBlack}
g.Board[0][7] = &CheckersPiece{Color: CheckersBlack}
g.Board[1][6] = &CheckersPiece{Color: CheckersWhite}
// (2,5) занята — иначе чёрная шашка могла бы взять белую назад
// через прыжок (0,7)->(2,5), ведь простые шашки в русских шашках
// бьют в любом направлении, включая назад
g.Board[2][5] = &CheckersPiece{Color: CheckersWhite}
g.Board[5][0] = &CheckersPiece{Color: CheckersWhite}
g.Turn = CheckersWhite
if err := g.Move(CheckersPos{5, 0}, CheckersPos{4, 1}); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Result == nil {
t.Fatalf("партия должна была завершиться — у чёрных нет ходов")
}
if g.Result.Winner != CheckersWhite {
t.Errorf("ожидался победитель — белые")
}
}
func TestDrawByNoCaptureLimit(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite, MovesSinceCapture: maxCheckersMovesWithoutCapture - 1}
g.Board[5][2] = &CheckersPiece{Color: CheckersWhite}
if err := g.Move(CheckersPos{5, 2}, CheckersPos{4, 1}); err != nil {
t.Fatalf("неожиданная ошибка: %v", err)
}
if g.Result == nil || !g.Result.Draw {
t.Fatalf("ожидалась ничья по лимиту ходов без взятия")
}
}
func TestCannotMoveOpponentPiece(t *testing.T) {
g := &CheckersGameState{Turn: CheckersWhite}
g.Board[2][1] = &CheckersPiece{Color: CheckersBlack}
if err := g.Move(CheckersPos{2, 1}, CheckersPos{3, 2}); err != ErrCheckersNoPiece {
t.Errorf("ожидалась ошибка хода чужой шашкой, получено: %v", err)
}
}