Задание для добора (Елена Гасанова)

Задание

Разработать программу для игры в крестики-нолики.
Задание содержит заготовку, работа, выполненная без её использования, не зачитывается

Описание

Программа является консольной. Игровое поле отображается следующим образом:

+---+---+---+
|   |   |   |
+---+---+---+
| X | O |   |
+---+---+---+
|   | O | X |
+---+---+---+
где клетки пронумерованы следующим образом:
+-----------+
| 0 | 1 | 2 |
+-----------+
| 3 | 4 | 5 |
+-----------+
| 6 | 7 | 8 |
+-----------+

Реализация

Программа использует несколько классов. Каждый класс имеет свой заголовочный файл и свой файл *.cpp. Заголовочные файлы имеют стражи включения. Следует реализовать следующие классы:

Piece

Piece (фигура) – символ, который отображается на игровом поле. Реализуется в виде перечисления следующим образом:

enum class Piece {empty = ' ', first = 'X', second ='O'};
Определяется в заголовочном файле Board.h

Board

Класс Board – используется для отображения текущего состояния на игровом поле.

Он содержит следующие поля и методы:

class Board {
	Piece squares[9];          // одномерный массив клеток (9 элементов типа Piece)
	void clear();              // удаляет все фигуры с доски (все клетки становятся равны Piece::empty);
public:
	Board() { clear(); }       // конструктор по умолчанию, который вызывает метод clear();
	void display();            // отображает игровое поле на консоли;
	void makeMove(Piece, int); // помещает фигуру в клетку, номер которой задан вторым параметром;
	void undoMove(int);        // удаляет фигуру из заданной клетки;

	bool isLegal(Piece, int);   // метод проверяет, можно ли поместить фигуру в клетку, заданную вторым параметром;
	bool isWinner(Piece) const; // возвращает true, если текущая позиция является выигрышной для данной фигуры;
                                    // (Подсказка: существует всего 8 выигрышных комбинаций, которые можно описать во
                                    // внешнем массиве-константе) 
	bool isFull() const;        // Возвращает true, если поле заполнено
};

Player

Player – абстрактный класс, базовый как для игрока-человека, так и для компьютерного игрока. В нём хранится имя игрока и фигура, которой он играет (например Piece::first или Piece::second). Он обеспечивает следующие методы:
class Player
{
	string name; // имя игрока
	Piece piece; // его фигура
public:
	Player(const string&, Piece); // конструктор, который принимает имя игрока и его фигуру
	Piece getPiece() const;       // возвращает фигуру, которую использует игрок
	string getName() const;       // возвращает имя игрока;  
	virtual void makeMove(Board&) const = 0; // чисто виртуальная функция, которая заставляет игрока делать ход
	virtual ~Player();
};

HumanPlayer

Класс – наследник Player. Метод makeMove() запрашивает с пользователя номер клетки, в которую он хотел бы поместить клетку (подсказка: организовать цикл в котором программа запрашивает с пользователя номер клетки, до тех пор, пока он не введёт существующий номер клетки (от 0 до 9), не занятый фигурой)

class HumanPlayer :
	public Player
{
public:
	HumanPlayer(const string&, Piece);
	void makeMove(Board&) const override; // запрашивает с пользователя номер клетки, в которую он хотел бы поместить
                                              // фигуру 
                                              // (подсказка: организовать цикл в котором программа запрашивает с 
                                              // пользователя номер клетки, до тех пор, пока он не введёт существующий 
                                              // номер клетки (от 0 до 9), не занятой никакой фигурой).
	~HumanPlayer();
};

ComputerPlayer

Абстрактный класс наследник Player. Он не реализует метод makeMove(). Он только обеспечивает одно и то же имя для разных компьютерных игроков.

class ComputerPlayer :
	public Player
{
public:
	ComputerPlayer(Piece);	// вызывает конструктор базового класс в списке инициализации и 
                                // задаёт значение имени равным строке "Computer"
	~ComputerPlayer();
};

RandomPlayer

- наследник ComputerPlayer. Метод MakeMove() делает случайный ход на незанятую клетку.

class RandomPlayer :
	public ComputerPlayer
{
public:
	RandomPlayer(Piece);                 // обращается к конструктору базового класса;
	virtual void makeMove(Board&) const; // делает случайный ход на незанятую клетку;
	~RandomPlayer();
};

Game

- класс для контроля над ходом игры. Его поля – это объект класса Board, а также два указателя на объекты типа Player

class Game
{
	Board board;                 // игровое поле;
	Player *player1;             // указатели на игроков;
	Player *player2;
	int movesMade;               // количество сделанных ходов;
	Player *selectPlayer(Piece); // вспомогательный метод, запрашивающий у пользователя тип игрока и 
                                     // его имя (если выбран HumanPlayer). Затем функция возвращает динамически созданный объект - 
                                     // указатель на Player;
	Player* nextPlayer() const;  // метод возвращает указатель на игрока, ход которого – следующий;	
        bool isRunning() const;      // возвращает true, если игровое поле содержит свободные клетки;
public:
	Game();                      // конструктор по умолчанию, который создаёт пустое игровое поле и
                                     // устанавливает значения указателей на Player равными nullptr;
	void selectPlayers();        // метод содержит всего две команды - вызов selectPlayer для player1 и player2;	
	void play();                 // пока isRunning() возвращает true, метод отображает игровое поле и вызывает 
                                     // метод makeMove() для очередного игрока (указатель на который возвратил nextPlayer());  
	void announceWinner();       // Объявляет об окончании игры. Выводит имя победителя или сообщение о ничье;
	~Game();                     // удаляет динамически созданные объекты; 
};

Функция main()

Помещается в отдельном файле и имеет следующий вид:
int main() {
	srand((unsigned) time(0)); // инициализируется генератор случайных чисел;
	Game game;                 // создаётся объект типа Game;
	game.selectPlayers();      // запрашиваем тип и имя игроков у пользователя;
	game.play();               // играем игру;
	game.announceWinner();     // объявляем победителя;	
	system("PAUSE");
}

Дополнительное задание

Создать класс advancedComputerPlayer – наследник RandomPlayer. Его метод MakeMove() перед тем, как сделать ход, проверяет, существует ли ход, который приносит немедленный выигрыш. Если такой ход есть, он совершается, если его нет, совершается ход в случайную клетку.

Вывод программы выглядит примерно так:

select the type of the player #1 (X) (human - 0, computer - 1, advanced computer player -  2):0
 enter the name of the human player: Qwerty
select the type of the player #2 (O) (human - 0, computer - 1, advanced computer player -  2):2
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
Qwerty (player #1) makes a move.
enter the square number: 0
+---+---+---+
| X |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
Computer (player #2) makes a move.
+---+---+---+
| X |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
| O |   |   |
+---+---+---+
Qwerty (player #1) makes a move.
enter the square number: 1
+---+---+---+
| X | X |   |
+---+---+---+
|   |   |   |
+---+---+---+
| O |   |   |
+---+---+---+
Computer (player #2) makes a move.
+---+---+---+
| X | X |   |
+---+---+---+
|   | O |   |
+---+---+---+
| O |   |   |
+---+---+---+
Qwerty (player #1) makes a move.
enter the square number: 2
+---+---+---+
| X | X | X |
+---+---+---+
|   | O |   |
+---+---+---+
| O |   |   |
+---+---+---+
Player # 1 Qwerty wins!!!
Press any key to continue . . .