Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "pch.h"
- // функция minmax
- #include <algorithm>
- // функции srand и rand
- #include <cstdlib>
- // функция time (используется в srand)
- #include <ctime>
- // функция setw
- #include <iomanip>
- // объекты cin, cout
- #include <iostream>
- // структура pair
- #include <utility>
- class Game
- {
- public:
- // ширина поля
- static const unsigned int FieldWidth = 10;
- // состояние клетки на поле
- enum CellState
- {
- Empty = 0,
- Occupied = 1
- };
- // валидность хода
- enum MoveValidity
- {
- Invalid = 0,
- Miss = 1,
- Hit = 2,
- Kill = 3
- };
- // была ли клетка проверена
- enum MoveState
- {
- Unchecked = 0,
- Checked = 1
- };
- // второе имя для пары координат
- using Coordinates = std::pair<unsigned int, unsigned int>;
- // второе имя для массива с информацией о кораблях
- using Field = CellState[FieldWidth][FieldWidth];
- // второе имя для массива с информацией о ходах
- using MoveField = MoveState[FieldWidth][FieldWidth];
- // конструктор класса Game
- Game();
- // запускает игру и возвращает код ошибки 0
- int start();
- // печатает поле игрока
- void printPlayerField() const;
- // печатает поле бота
- void printBotField() const;
- protected:
- // получает координаты корабла выбранной длины от игрока, возвращает true
- // и сохраняет координаты на поле игрока, если введённые координаты
- // представляют собой верное расположение корабля на поле игрока
- bool getPlayerCoords(unsigned int);
- // генерирует случайные верные координаты корабла выбранной длины, возвращает
- // true и сохраняет координаты на поле бота
- bool getBotCoords(unsigned int);
- // получает координаты (необязательно валидные) хода от игрока
- Coordinates getPlayerMove() const;
- // генерирует случайные валидные координаты хода бота
- Coordinates getBotMove() const;
- // проверяет, представляют ли координаты верное расположение корабля указанной
- // длины на указанном поле, дополнительно можно включить вывод ошибок
- static bool checkCoords(const Field &, unsigned int, Coordinates, Coordinates, bool = true);
- // проверяет ход, представленный указанными координатами на указанном поле,
- // дополнительно можно включить вывод ошибок
- static MoveValidity checkMove(const Field &, const MoveField &, Coordinates, bool = true);
- // на поле установить прямоугольник с углами в указанных координатах в
- // указанное состояние; при переходе от состояния Empty к состоянию Occupied
- // увеличить счётчик
- static void writeField(Field &, unsigned int &, CellState, Coordinates, Coordinates);
- // записать ход в массив ходов
- static void writeMove(MoveField &, MoveState, Coordinates);
- // печатает текущее состояние поля
- static void print(const Field &);
- private:
- // количество клеток, занятых кораблями игрока
- unsigned int playerCount;
- // количество клеток, занятых кораблами бота
- unsigned int botCount;
- // поле игрока
- Field playerField;
- // поле бота
- Field botField;
- // ходы игрока
- MoveField playerMoves;
- // ходы бота
- MoveField botMoves;
- };
- Game::Game()
- {
- // функция make_pair создаёт объект структуры pair, содержащей пару из двух
- // значений
- writeField(playerField, playerCount, Empty, std::make_pair(0, 0), std::make_pair(FieldWidth - 1, FieldWidth - 1));
- writeField(botField, botCount, Empty, std::make_pair(0, 0), std::make_pair(FieldWidth - 1, FieldWidth - 1));
- for (unsigned int i = 0; i < FieldWidth; ++i)
- {
- for (unsigned int j = 0; j < FieldWidth; ++j)
- {
- writeMove(playerMoves, Unchecked, std::make_pair(i, j));
- writeMove(botMoves, Unchecked, std::make_pair(i, j));
- }
- }
- playerCount = 0;
- botCount = 0;
- }
- int Game::start()
- {
- std::cout << "The game begins!\n";
- for (unsigned int shipLength = 1; shipLength <= 4; ++shipLength)
- {
- std::cout << "Enter coordinates for ships of length " << shipLength << "\n";
- unsigned int shipCount = 4 - shipLength + 1;
- for (unsigned int shipNumber = 1; shipNumber <= shipCount; ++shipNumber)
- {
- std::cout << "Ship " << shipNumber << " of " << shipCount << "\n";
- // !(выражение) значит выражение != true
- while (!getPlayerCoords(shipLength))
- {
- std::cout << "You specified invalid coordinates, please try again\n";
- }
- getBotCoords(shipLength);
- }
- }
- printPlayerField();
- //printBotField();
- while (playerCount > 0 && botCount > 0)
- {
- bool playerTurn = true;
- while (playerTurn && botCount > 0)
- {
- playerTurn = false;
- Coordinates playerMove;
- MoveValidity moveValidity;
- do
- {
- playerMove = getPlayerMove();
- moveValidity = checkMove(botField, playerMoves, playerMove);
- } while (moveValidity == Invalid);
- writeMove(playerMoves, Checked, playerMove);
- if (moveValidity == Hit || moveValidity == Kill)
- {
- --botCount;
- playerTurn = true;
- if (moveValidity == Hit)
- {
- std::cout << "You hit bot's ship\n";
- }
- else
- {
- std::cout << "You killed bot's ship\n";
- }
- }
- else
- {
- std::cout << "You missed\n";
- }
- std::cout << "Bot still have " << botCount << " units\n\n";
- }
- if (botCount == 0)
- {
- break;
- }
- bool botTurn = true;
- while (botTurn && playerCount > 0)
- {
- botTurn = false;
- Coordinates botMove = getBotMove();
- MoveValidity moveValidity = checkMove(playerField, botMoves, botMove);
- writeMove(botMoves, Checked, botMove);
- std::cout << "Bot fired at (" << botMove.first + 1 << "; " << botMove.second + 1 << ")\n";
- if (moveValidity == Hit || moveValidity == Kill)
- {
- --playerCount;
- botTurn = true;
- if (moveValidity == Hit)
- {
- std::cout << "Bot hit your ship\n";
- }
- else
- {
- std::cout << "Bot killed your ship\n";
- }
- }
- else
- {
- std::cout << "Bot missed\n";
- }
- std::cout << "You still have " << playerCount << " units\n\n";
- }
- }
- std::cout << "Game finished\n\n";
- printBotField();
- std::cout << "\n";
- if (botCount == 0)
- {
- std::cout << "You win\n";
- }
- else
- {
- std::cout << "You lose\n";
- }
- return 0;
- }
- void Game::printPlayerField() const
- {
- std::cout << "Your field\n";
- print(playerField);
- }
- void Game::printBotField() const
- {
- std::cout << "Bot's field\n";
- print(botField);
- }
- bool Game::getPlayerCoords(unsigned int shipLength)
- {
- if (shipLength == 1)
- {
- std::cout << "Enter coordinates of a ship: ";
- Coordinates coords;
- std::cin >> coords.first >> coords.second;
- --coords.first;
- --coords.second;
- if (checkCoords(playerField, shipLength, coords, coords))
- {
- writeField(playerField, playerCount, Occupied, coords, coords);
- return true;
- }
- else
- {
- return false;
- }
- }
- else
- {
- std::cout << "Enter coordinates of the start of the ship: ";
- Coordinates coords1;
- std::cin >> coords1.first >> coords1.second;
- --coords1.first;
- --coords1.second;
- std::cout << "Enter coordinates of the end of the ship: ";
- Coordinates coords2;
- std::cin >> coords2.first >> coords2.second;
- --coords2.first;
- --coords2.second;
- if (checkCoords(playerField, shipLength, coords1, coords2))
- {
- writeField(playerField, playerCount, Occupied, coords1, coords2);
- return true;
- }
- else
- {
- return false;
- }
- }
- }
- bool Game::getBotCoords(unsigned int shipLength)
- {
- bool foundCorrectCoords = false;
- while (!foundCorrectCoords)
- {
- bool horizontal = std::rand() % 2;
- Coordinates coords1(unsigned(std::rand()) % FieldWidth, unsigned(std::rand()) % FieldWidth);
- Coordinates coords2(coords1);
- if (horizontal)
- {
- coords2.second += shipLength - 1;
- }
- else
- {
- coords2.first += shipLength - 1;
- }
- if (checkCoords(botField, shipLength, coords1, coords2, false))
- {
- writeField(botField, botCount, Occupied, coords1, coords2);
- foundCorrectCoords = true;
- }
- }
- return foundCorrectCoords;
- }
- Game::Coordinates Game::getPlayerMove() const
- {
- std::cout << "Enter coordinates of your move: ";
- Coordinates coords;
- std::cin >> coords.first >> coords.second;
- --coords.first;
- --coords.second;
- return coords;
- }
- Game::Coordinates Game::getBotMove() const
- {
- Coordinates coords;
- do
- {
- coords = std::make_pair(unsigned(std::rand()) % FieldWidth, unsigned(std::rand()) % FieldWidth);
- } while (checkMove(playerField, botMoves, coords, false) == Invalid);
- return coords;
- }
- bool Game::checkCoords(const Field &field, unsigned int shipLength, Coordinates coords1, Coordinates coords2,
- bool printErrors)
- {
- // функция minmax возващает пару значений, где первый элемент - меньший, а
- // второй - больший
- Coordinates checkI = std::minmax(coords1.first, coords2.first);
- Coordinates checkJ = std::minmax(coords1.second, coords2.second);
- if (checkI.second >= FieldWidth)
- {
- if (printErrors)
- {
- std::cout << "Range error\n";
- }
- return false;
- }
- else if (checkJ.second >= FieldWidth)
- {
- if (printErrors)
- {
- std::cout << "Range error\n";
- }
- return false;
- }
- if (checkI.first == checkI.second)
- {
- if (checkJ.second - checkJ.first != shipLength - 1)
- {
- if (printErrors)
- {
- std::cout << "Invalid length of horizontal ship\n";
- }
- return false;
- }
- }
- else if (checkJ.first == checkJ.second)
- {
- if (checkI.second - checkI.first != shipLength - 1)
- {
- if (printErrors)
- {
- std::cout << "Invalid length of vertical ship\n";
- }
- return false;
- }
- }
- else
- {
- if (printErrors)
- {
- std::cout << "Ship is not on one line\n";
- }
- return false;
- }
- if (checkI.first > 0)
- {
- --checkI.first;
- }
- if (checkJ.first > 0)
- {
- --checkJ.first;
- }
- if (checkI.second < FieldWidth - 1)
- {
- ++checkI.second;
- }
- if (checkJ.second < FieldWidth - 1)
- {
- ++checkJ.second;
- }
- for (unsigned int i = checkI.first; i <= checkI.second; ++i)
- {
- for (unsigned int j = checkJ.first; j <= checkJ.second; ++j)
- {
- if (field[i][j] != Empty)
- {
- if (printErrors)
- {
- std::cout << "Cell at (" << i + 1 << "; " << j + 1 << ") is not empty\n";
- }
- return false;
- }
- }
- }
- return true;
- }
- Game::MoveValidity Game::checkMove(const Field &field, const MoveField &moves, Coordinates coords, bool printErrors)
- {
- if (coords.first >= FieldWidth)
- {
- if (printErrors)
- {
- std::cout << "Move is out of field\n";
- }
- return Invalid;
- }
- if (coords.second >= FieldWidth)
- {
- if (printErrors)
- {
- std::cout << "Move is out of field\n";
- }
- return Invalid;
- }
- if (moves[coords.first][coords.second] == Checked)
- {
- if (printErrors)
- {
- std::cout << "This cell has already been checked\n";
- }
- return Invalid;
- }
- if (field[coords.first][coords.second] == Empty)
- {
- return Miss;
- }
- unsigned int i = coords.first;
- unsigned int j = coords.second;
- while (true)
- {
- if (i > 0 && field[i - 1][j] != Empty)
- {
- --i;
- }
- else if (j > 0 && field[i][j - 1] != Empty)
- {
- --j;
- }
- else
- {
- break;
- }
- }
- unsigned int hit = 0;
- unsigned int occupied = 0;
- while (true)
- {
- hit += moves[i][j] == Checked || (i == coords.first && j == coords.second);
- occupied += 1;
- if (i < FieldWidth - 1 && field[i + 1][j] != Empty)
- {
- ++i;
- }
- else if (j < FieldWidth - 1 && field[i][j + 1] != Empty)
- {
- ++j;
- }
- else
- {
- break;
- }
- }
- if (hit == occupied)
- {
- return Kill;
- }
- else
- {
- return Hit;
- }
- }
- void Game::writeField(Field &field, unsigned int &counter, CellState state, Coordinates coords1, Coordinates coords2)
- {
- Coordinates setI = std::minmax(coords1.first, coords2.first);
- Coordinates setJ = std::minmax(coords1.second, coords2.second);
- for (unsigned int i = setI.first; i <= setI.second; ++i)
- {
- for (unsigned int j = setJ.first; j <= setJ.second; ++j)
- {
- if (field[i][j] == Empty && state == Occupied)
- {
- ++counter;
- }
- field[i][j] = state;
- }
- }
- }
- void Game::writeMove(MoveField &moves, MoveState state, Coordinates coords)
- {
- moves[coords.first][coords.second] = state;
- }
- void Game::print(const Field &field)
- {
- for (unsigned int i = 0; i <= FieldWidth; ++i)
- {
- if (i == 0)
- {
- for (unsigned int j = 0; j <= FieldWidth; ++j)
- {
- std::cout << std::setw(2) << j << " ";
- }
- }
- else
- {
- for (unsigned int j = 0; j <= FieldWidth; ++j)
- {
- if (j == 0)
- {
- std::cout << std::setw(2) << i << " ";
- }
- else
- {
- switch (field[i - 1][j - 1])
- {
- case Empty:
- std::cout << " ";
- break;
- case Occupied:
- std::cout << "## ";
- break;
- }
- }
- }
- }
- std::cout << "\n";
- }
- }
- int main()
- {
- // инициализирует генератор псевдослучайных чисел текущим временем
- std::srand(unsigned(time(nullptr)));
- Game game;
- return game.start();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement