package main import "testing" func TestNewTicTacToeGameXAlwaysStartsFirst(t *testing.T) { g := NewTicTacToeGame(TicTacToeO) if g.CurrentTurn != TicTacToeX { t.Errorf("крестики должны ходить первыми независимо от того, за кого играет человек") } } func TestNewTicTacToeGameDefaultsToXOnInvalidMark(t *testing.T) { g := NewTicTacToeGame(TicTacToeEmpty) if g.HumanMark != TicTacToeX { t.Errorf("недопустимый знак человека должен откатываться на крестики по умолчанию") } } func TestPlaceMarkRejectsOccupiedCell(t *testing.T) { g := NewTicTacToeGame(TicTacToeX) if err := g.PlaceMark(0); err != nil { t.Fatalf("неожиданная ошибка: %v", err) } if err := g.PlaceMark(0); err != ErrTicTacToeCellOccupied { t.Errorf("ожидалась ошибка занятой клетки, получено: %v", err) } } func TestPlaceMarkRejectsOutOfRange(t *testing.T) { g := NewTicTacToeGame(TicTacToeX) if err := g.PlaceMark(-1); err != ErrTicTacToeInvalidPosition { t.Errorf("ожидалась ошибка недопустимой позиции для -1, получено: %v", err) } if err := g.PlaceMark(9); err != ErrTicTacToeInvalidPosition { t.Errorf("ожидалась ошибка недопустимой позиции для 9, получено: %v", err) } } func TestPlaceMarkRejectsAfterGameOver(t *testing.T) { g := NewTicTacToeGame(TicTacToeX) // X быстро выигрывает верхнюю строку: 0,1,2 moves := []int{0, 3, 1, 4, 2} // X: 0,1,2 O: 3,4 for _, pos := range moves { if err := g.PlaceMark(pos); err != nil { t.Fatalf("неожиданная ошибка на ходу %d: %v", pos, err) } } if g.Phase != TicTacToePhaseOver || g.Result.Winner != TicTacToeX { t.Fatalf("ожидалась победа крестиков, получено Phase=%v Result=%+v", g.Phase, g.Result) } if err := g.PlaceMark(5); err != ErrTicTacToeGameOver { t.Errorf("ожидалась ошибка завершённой игры, получено: %v", err) } } func TestAllEightWinLinesDetected(t *testing.T) { lines := [8][3]int{ {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {0, 3, 6}, {1, 4, 7}, {2, 5, 8}, {0, 4, 8}, {2, 4, 6}, } for _, line := range lines { var board [9]TicTacToeMark board[line[0]] = TicTacToeX board[line[1]] = TicTacToeX board[line[2]] = TicTacToeX winner, gotLine, ok := ticTacToeCheckWin(board) if !ok || winner != TicTacToeX { t.Errorf("линия %v должна была засчитаться как победная", line) } if gotLine != line { t.Errorf("ожидалась выигрышная линия %v, получено %v", line, gotLine) } } } func TestDrawWhenBoardFullWithoutWinner(t *testing.T) { g := NewTicTacToeGame(TicTacToeX) // классическая ничейная раскладка: // X O X // X O O // O X X moves := []int{0, 1, 2, 4, 3, 5, 7, 6, 8} for i, pos := range moves { if err := g.PlaceMark(pos); err != nil { t.Fatalf("ход %d (клетка %d): неожиданная ошибка %v", i, pos, err) } if g.Phase == TicTacToePhaseOver && i != len(moves)-1 { t.Fatalf("партия завершилась преждевременно на ходу %d (клетка %d), доска: %v", i, pos, g.Board) } } if g.Phase != TicTacToePhaseOver || !g.Result.Draw { t.Fatalf("ожидалась ничья, получено Phase=%v Result=%+v", g.Phase, g.Result) } } func TestTurnAlternatesBetweenMoves(t *testing.T) { g := NewTicTacToeGame(TicTacToeX) if g.CurrentTurn != TicTacToeX { t.Fatalf("ожидался первый ход крестиков") } g.PlaceMark(0) if g.CurrentTurn != TicTacToeO { t.Errorf("после хода крестиков должна ходить нолики") } g.PlaceMark(1) if g.CurrentTurn != TicTacToeX { t.Errorf("после хода ноликов должны снова ходить крестики") } }