Advertisement
BERKYT

Sea fight

Feb 24th, 2021
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.04 KB | None | 0 0
  1. #include "pch.h"
  2. // функция minmax
  3. #include <algorithm>
  4.  
  5. // функции srand и rand
  6. #include <cstdlib>
  7.  
  8. // функция time (используется в srand)
  9. #include <ctime>
  10.  
  11. // функция setw
  12. #include <iomanip>
  13.  
  14. // объекты cin, cout
  15. #include <iostream>
  16.  
  17. // структура pair
  18. #include <utility>
  19.  
  20. class Game
  21. {
  22. public:
  23.     // ширина поля
  24.     static const unsigned int FieldWidth = 10;
  25.  
  26.     // состояние клетки на поле
  27.     enum CellState
  28.     {
  29.         Empty = 0,
  30.         Occupied = 1
  31.     };
  32.  
  33.     // валидность хода
  34.     enum MoveValidity
  35.     {
  36.         Invalid = 0,
  37.         Miss = 1,
  38.         Hit = 2,
  39.         Kill = 3
  40.     };
  41.  
  42.     // была ли клетка проверена
  43.     enum MoveState
  44.     {
  45.         Unchecked = 0,
  46.         Checked = 1
  47.     };
  48.  
  49.     // второе имя для пары координат
  50.     using Coordinates = std::pair<unsigned int, unsigned int>;
  51.  
  52.     // второе имя для массива с информацией о кораблях
  53.     using Field = CellState[FieldWidth][FieldWidth];
  54.  
  55.     // второе имя для массива с информацией о ходах
  56.     using MoveField = MoveState[FieldWidth][FieldWidth];
  57.  
  58.     // конструктор класса Game
  59.     Game();
  60.  
  61.     // запускает игру и возвращает код ошибки 0
  62.     int start();
  63.  
  64.     // печатает поле игрока
  65.     void printPlayerField() const;
  66.  
  67.     // печатает поле бота
  68.     void printBotField() const;
  69.  
  70. protected:
  71.     // получает координаты корабла выбранной длины от игрока, возвращает true
  72.     // и сохраняет координаты на поле игрока, если введённые координаты
  73.     // представляют собой верное расположение корабля на поле игрока
  74.     bool getPlayerCoords(unsigned int);
  75.  
  76.     // генерирует случайные верные координаты корабла выбранной длины, возвращает
  77.     // true и сохраняет координаты на поле бота
  78.     bool getBotCoords(unsigned int);
  79.  
  80.     // получает координаты (необязательно валидные) хода от игрока
  81.     Coordinates getPlayerMove() const;
  82.  
  83.     // генерирует случайные валидные координаты хода бота
  84.     Coordinates getBotMove() const;
  85.  
  86.     // проверяет, представляют ли координаты верное расположение корабля указанной
  87.     // длины на указанном поле, дополнительно можно включить вывод ошибок
  88.     static bool checkCoords(const Field &, unsigned int, Coordinates, Coordinates, bool = true);
  89.  
  90.     // проверяет ход, представленный указанными координатами на указанном поле,
  91.     // дополнительно можно включить вывод ошибок
  92.     static MoveValidity checkMove(const Field &, const MoveField &, Coordinates, bool = true);
  93.  
  94.     // на поле установить прямоугольник с углами в указанных координатах в
  95.     // указанное состояние; при переходе от состояния Empty к состоянию Occupied
  96.     // увеличить счётчик
  97.     static void writeField(Field &, unsigned int &, CellState, Coordinates, Coordinates);
  98.  
  99.     // записать ход в массив ходов
  100.     static void writeMove(MoveField &, MoveState, Coordinates);
  101.  
  102.     // печатает текущее состояние поля
  103.     static void print(const Field &);
  104.  
  105. private:
  106.     // количество клеток, занятых кораблями игрока
  107.     unsigned int playerCount;
  108.  
  109.     // количество клеток, занятых кораблами бота
  110.     unsigned int botCount;
  111.  
  112.     // поле игрока
  113.     Field playerField;
  114.  
  115.     // поле бота
  116.     Field botField;
  117.  
  118.     // ходы игрока
  119.     MoveField playerMoves;
  120.  
  121.     // ходы бота
  122.     MoveField botMoves;
  123. };
  124.  
  125. Game::Game()
  126. {
  127.     // функция make_pair создаёт объект структуры pair, содержащей пару из двух
  128.     // значений
  129.     writeField(playerField, playerCount, Empty, std::make_pair(0, 0), std::make_pair(FieldWidth - 1, FieldWidth - 1));
  130.     writeField(botField, botCount, Empty, std::make_pair(0, 0), std::make_pair(FieldWidth - 1, FieldWidth - 1));
  131.  
  132.     for (unsigned int i = 0; i < FieldWidth; ++i)
  133.     {
  134.         for (unsigned int j = 0; j < FieldWidth; ++j)
  135.         {
  136.             writeMove(playerMoves, Unchecked, std::make_pair(i, j));
  137.             writeMove(botMoves, Unchecked, std::make_pair(i, j));
  138.         }
  139.     }
  140.  
  141.     playerCount = 0;
  142.     botCount = 0;
  143. }
  144.  
  145. int Game::start()
  146. {
  147.     std::cout << "The game begins!\n";
  148.  
  149.     for (unsigned int shipLength = 1; shipLength <= 4; ++shipLength)
  150.     {
  151.         std::cout << "Enter coordinates for ships of length " << shipLength << "\n";
  152.         unsigned int shipCount = 4 - shipLength + 1;
  153.  
  154.         for (unsigned int shipNumber = 1; shipNumber <= shipCount; ++shipNumber)
  155.         {
  156.             std::cout << "Ship " << shipNumber << " of " << shipCount << "\n";
  157.  
  158.             // !(выражение) значит выражение != true
  159.             while (!getPlayerCoords(shipLength))
  160.             {
  161.                 std::cout << "You specified invalid coordinates, please try again\n";
  162.             }
  163.  
  164.             getBotCoords(shipLength);
  165.         }
  166.     }
  167.  
  168.     printPlayerField();
  169.     //printBotField();
  170.  
  171.     while (playerCount > 0 && botCount > 0)
  172.     {
  173.         bool playerTurn = true;
  174.  
  175.         while (playerTurn && botCount > 0)
  176.         {
  177.             playerTurn = false;
  178.  
  179.             Coordinates playerMove;
  180.             MoveValidity moveValidity;
  181.  
  182.             do
  183.             {
  184.                 playerMove = getPlayerMove();
  185.                 moveValidity = checkMove(botField, playerMoves, playerMove);
  186.             } while (moveValidity == Invalid);
  187.  
  188.             writeMove(playerMoves, Checked, playerMove);
  189.  
  190.             if (moveValidity == Hit || moveValidity == Kill)
  191.             {
  192.                 --botCount;
  193.                 playerTurn = true;
  194.  
  195.                 if (moveValidity == Hit)
  196.                 {
  197.                     std::cout << "You hit bot's ship\n";
  198.                 }
  199.                 else
  200.                 {
  201.                     std::cout << "You killed bot's ship\n";
  202.                 }
  203.             }
  204.             else
  205.             {
  206.                 std::cout << "You missed\n";
  207.             }
  208.  
  209.             std::cout << "Bot still have " << botCount << " units\n\n";
  210.         }
  211.  
  212.         if (botCount == 0)
  213.         {
  214.             break;
  215.         }
  216.  
  217.         bool botTurn = true;
  218.  
  219.         while (botTurn && playerCount > 0)
  220.         {
  221.             botTurn = false;
  222.  
  223.             Coordinates botMove = getBotMove();
  224.             MoveValidity moveValidity = checkMove(playerField, botMoves, botMove);
  225.  
  226.             writeMove(botMoves, Checked, botMove);
  227.  
  228.             std::cout << "Bot fired at (" << botMove.first + 1 << "; " << botMove.second + 1 << ")\n";
  229.  
  230.             if (moveValidity == Hit || moveValidity == Kill)
  231.             {
  232.                 --playerCount;
  233.                 botTurn = true;
  234.  
  235.                 if (moveValidity == Hit)
  236.                 {
  237.                     std::cout << "Bot hit your ship\n";
  238.                 }
  239.                 else
  240.                 {
  241.                     std::cout << "Bot killed your ship\n";
  242.                 }
  243.             }
  244.             else
  245.             {
  246.                 std::cout << "Bot missed\n";
  247.             }
  248.  
  249.             std::cout << "You still have " << playerCount << " units\n\n";
  250.         }
  251.     }
  252.  
  253.     std::cout << "Game finished\n\n";
  254.     printBotField();
  255.     std::cout << "\n";
  256.  
  257.     if (botCount == 0)
  258.     {
  259.         std::cout << "You win\n";
  260.     }
  261.     else
  262.     {
  263.         std::cout << "You lose\n";
  264.     }
  265.  
  266.     return 0;
  267. }
  268.  
  269. void Game::printPlayerField() const
  270. {
  271.     std::cout << "Your field\n";
  272.     print(playerField);
  273. }
  274.  
  275. void Game::printBotField() const
  276. {
  277.     std::cout << "Bot's field\n";
  278.     print(botField);
  279. }
  280.  
  281. bool Game::getPlayerCoords(unsigned int shipLength)
  282. {
  283.     if (shipLength == 1)
  284.     {
  285.         std::cout << "Enter coordinates of a ship: ";
  286.         Coordinates coords;
  287.         std::cin >> coords.first >> coords.second;
  288.         --coords.first;
  289.         --coords.second;
  290.  
  291.         if (checkCoords(playerField, shipLength, coords, coords))
  292.         {
  293.             writeField(playerField, playerCount, Occupied, coords, coords);
  294.             return true;
  295.         }
  296.         else
  297.         {
  298.             return false;
  299.         }
  300.     }
  301.     else
  302.     {
  303.         std::cout << "Enter coordinates of the start of the ship: ";
  304.         Coordinates coords1;
  305.         std::cin >> coords1.first >> coords1.second;
  306.         --coords1.first;
  307.         --coords1.second;
  308.  
  309.         std::cout << "Enter coordinates of the end of the ship: ";
  310.         Coordinates coords2;
  311.         std::cin >> coords2.first >> coords2.second;
  312.         --coords2.first;
  313.         --coords2.second;
  314.  
  315.         if (checkCoords(playerField, shipLength, coords1, coords2))
  316.         {
  317.             writeField(playerField, playerCount, Occupied, coords1, coords2);
  318.             return true;
  319.         }
  320.         else
  321.         {
  322.             return false;
  323.         }
  324.     }
  325. }
  326.  
  327. bool Game::getBotCoords(unsigned int shipLength)
  328. {
  329.     bool foundCorrectCoords = false;
  330.  
  331.     while (!foundCorrectCoords)
  332.     {
  333.         bool horizontal = std::rand() % 2;
  334.         Coordinates coords1(unsigned(std::rand()) % FieldWidth, unsigned(std::rand()) % FieldWidth);
  335.         Coordinates coords2(coords1);
  336.  
  337.         if (horizontal)
  338.         {
  339.             coords2.second += shipLength - 1;
  340.         }
  341.         else
  342.         {
  343.             coords2.first += shipLength - 1;
  344.         }
  345.  
  346.         if (checkCoords(botField, shipLength, coords1, coords2, false))
  347.         {
  348.             writeField(botField, botCount, Occupied, coords1, coords2);
  349.             foundCorrectCoords = true;
  350.         }
  351.     }
  352.  
  353.     return foundCorrectCoords;
  354. }
  355.  
  356. Game::Coordinates Game::getPlayerMove() const
  357. {
  358.     std::cout << "Enter coordinates of your move: ";
  359.     Coordinates coords;
  360.     std::cin >> coords.first >> coords.second;
  361.     --coords.first;
  362.     --coords.second;
  363.     return coords;
  364. }
  365.  
  366. Game::Coordinates Game::getBotMove() const
  367. {
  368.     Coordinates coords;
  369.  
  370.     do
  371.     {
  372.         coords = std::make_pair(unsigned(std::rand()) % FieldWidth, unsigned(std::rand()) % FieldWidth);
  373.     } while (checkMove(playerField, botMoves, coords, false) == Invalid);
  374.  
  375.     return coords;
  376. }
  377.  
  378. bool Game::checkCoords(const Field &field, unsigned int shipLength, Coordinates coords1, Coordinates coords2,
  379.     bool printErrors)
  380. {
  381.     // функция minmax возващает пару значений, где первый элемент - меньший, а
  382.     // второй - больший
  383.     Coordinates checkI = std::minmax(coords1.first, coords2.first);
  384.     Coordinates checkJ = std::minmax(coords1.second, coords2.second);
  385.  
  386.     if (checkI.second >= FieldWidth)
  387.     {
  388.         if (printErrors)
  389.         {
  390.             std::cout << "Range error\n";
  391.         }
  392.  
  393.         return false;
  394.     }
  395.     else if (checkJ.second >= FieldWidth)
  396.     {
  397.         if (printErrors)
  398.         {
  399.             std::cout << "Range error\n";
  400.         }
  401.  
  402.         return false;
  403.     }
  404.  
  405.     if (checkI.first == checkI.second)
  406.     {
  407.         if (checkJ.second - checkJ.first != shipLength - 1)
  408.         {
  409.             if (printErrors)
  410.             {
  411.                 std::cout << "Invalid length of horizontal ship\n";
  412.             }
  413.  
  414.             return false;
  415.         }
  416.     }
  417.     else if (checkJ.first == checkJ.second)
  418.     {
  419.         if (checkI.second - checkI.first != shipLength - 1)
  420.         {
  421.             if (printErrors)
  422.             {
  423.                 std::cout << "Invalid length of vertical ship\n";
  424.             }
  425.  
  426.             return false;
  427.         }
  428.     }
  429.     else
  430.     {
  431.         if (printErrors)
  432.         {
  433.             std::cout << "Ship is not on one line\n";
  434.         }
  435.  
  436.         return false;
  437.     }
  438.  
  439.     if (checkI.first > 0)
  440.     {
  441.         --checkI.first;
  442.     }
  443.  
  444.     if (checkJ.first > 0)
  445.     {
  446.         --checkJ.first;
  447.     }
  448.  
  449.     if (checkI.second < FieldWidth - 1)
  450.     {
  451.         ++checkI.second;
  452.     }
  453.  
  454.     if (checkJ.second < FieldWidth - 1)
  455.     {
  456.         ++checkJ.second;
  457.     }
  458.  
  459.     for (unsigned int i = checkI.first; i <= checkI.second; ++i)
  460.     {
  461.         for (unsigned int j = checkJ.first; j <= checkJ.second; ++j)
  462.         {
  463.             if (field[i][j] != Empty)
  464.             {
  465.                 if (printErrors)
  466.                 {
  467.                     std::cout << "Cell at (" << i + 1 << "; " << j + 1 << ") is not empty\n";
  468.                 }
  469.  
  470.                 return false;
  471.             }
  472.         }
  473.     }
  474.  
  475.     return true;
  476. }
  477.  
  478. Game::MoveValidity Game::checkMove(const Field &field, const MoveField &moves, Coordinates coords, bool printErrors)
  479. {
  480.     if (coords.first >= FieldWidth)
  481.     {
  482.         if (printErrors)
  483.         {
  484.             std::cout << "Move is out of field\n";
  485.         }
  486.  
  487.         return Invalid;
  488.     }
  489.  
  490.     if (coords.second >= FieldWidth)
  491.     {
  492.         if (printErrors)
  493.         {
  494.             std::cout << "Move is out of field\n";
  495.         }
  496.  
  497.         return Invalid;
  498.     }
  499.  
  500.     if (moves[coords.first][coords.second] == Checked)
  501.     {
  502.         if (printErrors)
  503.         {
  504.             std::cout << "This cell has already been checked\n";
  505.         }
  506.  
  507.         return Invalid;
  508.     }
  509.  
  510.     if (field[coords.first][coords.second] == Empty)
  511.     {
  512.         return Miss;
  513.     }
  514.  
  515.     unsigned int i = coords.first;
  516.     unsigned int j = coords.second;
  517.  
  518.     while (true)
  519.     {
  520.         if (i > 0 && field[i - 1][j] != Empty)
  521.         {
  522.             --i;
  523.         }
  524.         else if (j > 0 && field[i][j - 1] != Empty)
  525.         {
  526.             --j;
  527.         }
  528.         else
  529.         {
  530.             break;
  531.         }
  532.     }
  533.  
  534.     unsigned int hit = 0;
  535.     unsigned int occupied = 0;
  536.  
  537.     while (true)
  538.     {
  539.         hit += moves[i][j] == Checked || (i == coords.first && j == coords.second);
  540.         occupied += 1;
  541.  
  542.         if (i < FieldWidth - 1 && field[i + 1][j] != Empty)
  543.         {
  544.             ++i;
  545.         }
  546.         else if (j < FieldWidth - 1 && field[i][j + 1] != Empty)
  547.         {
  548.             ++j;
  549.         }
  550.         else
  551.         {
  552.             break;
  553.         }
  554.     }
  555.  
  556.     if (hit == occupied)
  557.     {
  558.         return Kill;
  559.     }
  560.     else
  561.     {
  562.         return Hit;
  563.     }
  564. }
  565.  
  566. void Game::writeField(Field &field, unsigned int &counter, CellState state, Coordinates coords1, Coordinates coords2)
  567. {
  568.     Coordinates setI = std::minmax(coords1.first, coords2.first);
  569.     Coordinates setJ = std::minmax(coords1.second, coords2.second);
  570.  
  571.     for (unsigned int i = setI.first; i <= setI.second; ++i)
  572.     {
  573.         for (unsigned int j = setJ.first; j <= setJ.second; ++j)
  574.         {
  575.             if (field[i][j] == Empty && state == Occupied)
  576.             {
  577.                 ++counter;
  578.             }
  579.  
  580.             field[i][j] = state;
  581.         }
  582.     }
  583. }
  584.  
  585. void Game::writeMove(MoveField &moves, MoveState state, Coordinates coords)
  586. {
  587.     moves[coords.first][coords.second] = state;
  588. }
  589.  
  590. void Game::print(const Field &field)
  591. {
  592.     for (unsigned int i = 0; i <= FieldWidth; ++i)
  593.     {
  594.         if (i == 0)
  595.         {
  596.             for (unsigned int j = 0; j <= FieldWidth; ++j)
  597.             {
  598.                 std::cout << std::setw(2) << j << " ";
  599.             }
  600.         }
  601.         else
  602.         {
  603.             for (unsigned int j = 0; j <= FieldWidth; ++j)
  604.             {
  605.                 if (j == 0)
  606.                 {
  607.                     std::cout << std::setw(2) << i << " ";
  608.                 }
  609.                 else
  610.                 {
  611.                     switch (field[i - 1][j - 1])
  612.                     {
  613.                     case Empty:
  614.                         std::cout << "   ";
  615.                         break;
  616.  
  617.                     case Occupied:
  618.                         std::cout << "## ";
  619.                         break;
  620.                     }
  621.                 }
  622.             }
  623.         }
  624.  
  625.         std::cout << "\n";
  626.     }
  627. }
  628.  
  629. int main()
  630. {
  631.     // инициализирует генератор псевдослучайных чисел текущим временем
  632.     std::srand(unsigned(time(nullptr)));
  633.     Game game;
  634.     return game.start();
  635. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement