C++.Игра Хрестики-Нолики

Здрасте,написал консольную игру на С++,называеться "Хресити-Нолики". Теперь мне нужно Добавить функционал повторения игры (После того, как показали результат игры, спросить у пользователя, хочет ли он повторить игру. Если пользователь выбирает вариант "да" - игра начинается с начала, "нет" - оформить завершения игры). Можете показать как вывести вот эту статистику в конце,и если человек хочет играть снова,начать игру сначала.Буду очень благодарен,мне нужно модефицировать вот этот код(ну,зделать то,что я написал вверху,буду благодарен если покажете как вы зделали эту статистику и повторенее игры)вот код который нужно это зделать:

#include <iostream>
#include <ctime>
using namespace std;
int main()
{
    srand(time(NULL));
    const int SIZE = 3;
    char game[SIZE][SIZE];
    //values for array game
    for (int i = 0; i < SIZE; i++)
    {
        for (int j = 0; j < SIZE; j++) {
            game[i][j] = '_';
        }
    }
    
    int count = 0;
    char winner = '*';
    char player = 'X';
    while (count!=9) 
    {
        count++;
        if (player == 'X') 
        {
            int col=0, row=0;
            while (true)
            {
                //print game
                for (int i = 0; i < SIZE; i++)
                {
                    for (int j = 0; j < SIZE; j++) {
                        cout << game[i][j] << "|";
                    }
                    cout << endl;
                }
                cout << "__________________" << endl;
                cout << "Step X" << endl;
                cout << "__________________" << endl;
                int n = 1;
                for (int i = 0; i < SIZE; i++)
                {
                    for (int j = 0; j < SIZE; j++) {
                        cout << n + j << "|";
                    }
                    n += 3;
                    cout << endl;
                }
                int posotion;
                cout << "Select option: ";
                cin >> posotion;
                switch (posotion)
                {
                case 1: row = 0; col = 0; break;
                case 2: row = 0; col = 1; break;
                case 3: row = 0; col = 2; break;
                case 4: row = 1; col = 0; break;
                case 5: row = 1; col = 1; break;
                case 6: row = 1; col = 2; break;
                case 7: row = 2; col = 0; break;
                case 8: row = 2; col = 1; break;
                case 9: row = 2; col = 2; break;
                }
                if (posotion > 0 && posotion < 10 && game[row][col] == '_') { break; }
                else { cout << "Error!!! Select true option!!!!" << endl; }
            }
            game[row][col] = player;
            player = '0';
        }
        else if (player == '0') 
        {
            int col = 0, row = 0;
            while (true)
            {
                int posotion = 1 + rand() % 9;
                switch (posotion)
                {
                case 1: row = 0; col = 0; break;
                case 2: row = 0; col = 1; break;
                case 3: row = 0; col = 2; break;
                case 4: row = 1; col = 0; break;
                case 5: row = 1; col = 1; break;
                case 6: row = 1; col = 2; break;
                case 7: row = 2; col = 0; break;
                case 8: row = 2; col = 1; break;
                case 9: row = 2; col = 2; break;
                }
                if (game[row][col] == '_') { break; }
            }
            if (game[0][0] == '_' && (game[0][1] == 'X' && game[0][2] == 'X' || game[1][1] == 'X' && game[2][2] == 'X' || game[1][0] == 'X' && game[2][0] == 'X')) {
                row = 0; col = 0;
            }
            else if (game[0][1] == '_' && (game[0][0] == 'X' && game[0][2] == 'X' || game[1][1] == 'X' && game[2][1] == 'X')) {
                row = 0; col = 1;
            }
            game[row][col] = player;
            player = 'X';
        }

        if (game[0][0] != '_' && (game[0][0] == game[0][1] && game[0][1] == game[0][2] || game[0][0] == game[1][0] && game[1][0] == game[2][0] || game[0][0] == game[1][1] && game[1][1] == game[2][2])) {
            winner = game[0][0];
        }
        else if (game[0][1] != '_' && game[0][1] == game[1][1] && game[1][1] == game[2][1]) {
            winner = game[0][1];
        }
        else if (game[0][2] != '_' && (game[0][2] == game[1][2] && game[1][2] == game[2][2] || game[0][2] == game[1][1] && game[1][1] == game[2][0])) {
            winner = game[0][2];
        }
        else if (game[1][0] != '_' && game[1][0] == game[1][1] && game[1][1] == game[1][2]) {
            winner = game[1][0];
        }
        else if (game[2][0] != '_' && game[2][0] == game[2][1] && game[2][1] == game[2][2]) {
            winner = game[2][0];
        }

        if (winner != '*') {
            break;
        }
    }
    cout << "__________________" << endl;
    cout << "END GAME" << endl;
    cout << "__________________" << endl;
    if (winner == '*') {
        cout << "Draw!!!" << endl;
    }
    else {
        cout << winner << "'s win!" << endl;
    }





}

Ответы (2 шт):

Автор решения: Harry

Примерно так. Переименовываем main() в, скажем, game(), а main пишем так:

int main()
{
    for(char с = 'Y'; с == 'Y';)
    {
        game();
        cout << "Еще? (Y/N) ";
        cin >> c;
    }
}
    

Что-то мне подсказывает, что саму игру писали не вы :)

→ Ссылка
Автор решения: Никита Самоуков

Неплохое название для игры.

Добавить реплей можно перенеся весь код игры в отдельную функцию.

Главная проблема в том что в C++ используется C.

  1. Доску В C++ надо делать отдельным классом(Board), а не размазывать по всему коду.

  2. Следует разделять представление доски(CellState) и её вывод на экран('X').

  3. Следует разделять содержимое доски(CellState) и результат матча(WinState).

  4. В С++ массивы делаются через std::array, а сишные же массивы ведут к множеству багов.

  5. В С++ есть генераторы случайных чисел(std::mt19937_64) и не нужно использовать rand() % x.

  6. Аи нужно выбрать случайную клетку, так пусть напрямую задаёт col и row.

  7. Аи должен быть отдельным модулем с состоянием. Так легче сменить сторону аи или сделать несколько разных аи.

  8. Если побеждает аи, то игроку всё же надо вывести финальное значение доски.

  9. Для перечисления контейнеров следует использовать range for.

Я добавил простой ай умеющий завершать линию.

Вот как должно быть в C++:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <regex>
#include <iomanip>
#include <optional>
#include <list>
#include <forward_list>
#include <array>
#include <random>
#include <unordered_map>
#include <cassert>

using namespace std;

struct Board
{
    static constexpr size_t board_size = 3;
    enum class CellState
    {
        Empty,
        X,
        O,
    };

    enum class WinState
    {
        Nobody,
        Tie,
        X,
        O,
    };

    Board()
    {
        Clear();
    }

    void Clear()
    {
        for (auto& column : data)
            column.fill(CellState::Empty);
    }

    bool CanSetCell(size_t x, size_t y) const
    {
        return x >= 0 && x < board_size&& y >= 0 && y < board_size&& data[x][y] == CellState::Empty;
    }

    void SetCell(size_t x, size_t y, CellState state)
    {
        if (!CanSetCell(x, y))
        {
            cout << "Error set" << endl;
        }
        data[x][y] = state;
    }

    WinState CalcWin() const
    {
        auto CellStateToWinner = [](CellState state)
        {
            return state == CellState::X ? WinState::X : WinState::O;
        };

        if (data[0][0] != CellState::Empty && (data[0][0] == data[0][1] && data[0][1] == data[0][2] || data[0][0] == data[1][0] && data[1][0] == data[2][0] || data[0][0] == data[1][1] && data[1][1] == data[2][2]))
        {
            return CellStateToWinner(data[0][0]);
        }
        if (data[0][1] != CellState::Empty && data[0][1] == data[1][1] && data[1][1] == data[2][1])
        {
            return CellStateToWinner(data[0][1]);
        }
        if (data[0][2] != CellState::Empty && (data[0][2] == data[1][2] && data[1][2] == data[2][2] || data[0][2] == data[1][1] && data[1][1] == data[2][0]))
        {
            return CellStateToWinner(data[0][2]);
        }
        if (data[1][0] != CellState::Empty && data[1][0] == data[1][1] && data[1][1] == data[1][2])
        {
            return CellStateToWinner(data[1][0]);
        }
        if (data[2][0] != CellState::Empty && data[2][0] == data[2][1] && data[2][1] == data[2][2])
        {
            return CellStateToWinner(data[2][0]);
        }

        for (auto& column : data)
            for (auto& cell : column)
                if (cell == CellState::Empty)
                {
                    return WinState::Nobody;
                }

        return WinState::Tie;
    }

    static char ToChar(CellState state)
    {
        static const unordered_map<CellState, char> print_conversion_index{
            {CellState::Empty, '_'},
            {CellState::X, 'X'},
            {CellState::O, 'O'},
        };

        return print_conversion_index.at(state);
    }

    void Print() const
    {

        for (int y = 0; y < board_size; y++)
        {
            for (int x = 0; x < board_size; x++)
            {
                cout << ToChar(data[x][y]) << "|";
            }
            cout << endl;
        }
    }

    array<array<CellState, board_size>, board_size> data{};

};

template<class T>
T RandNumber(T min, T max)
{
    static std::mt19937_64 random_gen{ (uint64_t)std::time(nullptr) };

    return std::uniform_int_distribution<T>(min, max)(random_gen);
}

class Ai
{
public:
    virtual pair<size_t, size_t> CalculateTurn(Board const& board) = 0;

    Ai() = default;
    virtual ~Ai() = default;

    Ai(Ai const&) = delete;
    Ai& operator=(Ai const&) = delete;
    Ai(Ai&&) = delete;
    Ai& operator=(Ai&&) = delete;
};

class SimpleAi : public Ai
{
public:
    explicit SimpleAi(Board::CellState my_sym) :_my_sym(my_sym)
    {}

    pair<size_t, size_t> CalculateTurn(Board const& board) override
    {
        assert(board.CalcWin() != Board::WinState::Tie);

        size_t col = 0, row = 0;

        while (true)
        {
            col = RandNumber(0, 2);
            row = RandNumber(0, 2);

            if (board.CanSetCell(row, col)) { break; }
        }


        if (board.data[0][0] == Board::CellState::Empty && (
            board.data[0][1] == _my_sym && board.data[0][2] == _my_sym ||
            board.data[1][1] == _my_sym && board.data[2][2] == _my_sym ||
            board.data[1][0] == _my_sym && board.data[2][0] == _my_sym
            ))
        {
            row = 0; col = 0;
        }

        else if (board.data[0][1] == Board::CellState::Empty && (
            board.data[0][0] == _my_sym && board.data[0][2] == _my_sym ||
            board.data[1][1] == _my_sym && board.data[2][1] == _my_sym
            ))
        {
            row = 0; col = 1;
        }

        return { row, col };
    }

private:
    Board::CellState _my_sym;
};

class MediumAi : public Ai
{
public:
    explicit MediumAi(Board::CellState my_sym) :_my_sym(my_sym)
    {}

    pair<size_t, size_t> CalculateTurn(Board const& board) override
    {
        assert(board.CalcWin() != Board::WinState::Tie);


        for (size_t x = 0; x < Board::board_size; x++)
            for (size_t y = 0; y < Board::board_size; y++)
            {
                if (board.CanSetCell(x, y))
                {
                    Board board_simulation = board;
                    board_simulation.SetCell(x, y, _my_sym);
                    if (board_simulation.CalcWin() != Board::WinState::Nobody)
                    {
                        return { x, y };
                    }
                }

            }

        size_t col = 0, row = 0;

        while (true)
        {
            col = RandNumber(0, 2);
            row = RandNumber(0, 2);

            if (board.CanSetCell(row, col)) { break; }
        }

        return { row, col };
    }

private:
    Board::CellState _my_sym;
};

void PrintSelectHelp()
{
    int n = 1;
    for (int i = 0; i < Board::board_size; i++)
    {
        for (int j = 0; j < Board::board_size; j++)
        {
            cout << n + j << "|";
        }
        n += 3;
        cout << endl;
    }
}

void PlayGame()
{
    Board board;
    unique_ptr<Ai> ai;

    char choose = -1;
    while (choose != '0' && choose != '1')
    {
        cout << "0-Simple 1-Medium" << endl;
        cin >> choose;
    }

    switch (choose)
    {
    case '0':
        ai = make_unique<SimpleAi>(Board::CellState::O);
        break;
    case '1':
        ai = make_unique<MediumAi>(Board::CellState::O);
        break;
    }

    int count = 0;
    auto player = Board::CellState::X;
    Board::WinState game_result = Board::WinState::Nobody;

    while (count != 9)
    {
        count++;
        if (player == Board::CellState::X)
        {
            int col = 0, row = 0;
            while (true)
            {
                //print game
                board.Print();

                cout << "__________________" << endl;
                cout << "Step " << Board::ToChar(player) << endl;
                cout << "__________________" << endl;

                PrintSelectHelp();

                int posotion;
                cout << "Select option: ";
                cin >> posotion;
                switch (posotion)
                {
                case 1: row = 0; col = 0; break;
                case 2: row = 1; col = 0; break;
                case 3: row = 2; col = 0; break;
                case 4: row = 0; col = 1; break;
                case 5: row = 1; col = 1; break;
                case 6: row = 2; col = 1; break;
                case 7: row = 0; col = 2; break;
                case 8: row = 1; col = 2; break;
                case 9: row = 2; col = 2; break;
                }
                if (posotion > 0 && posotion < 10 && board.CanSetCell(row, col)) { break; }

                cout << "Error!!! Selected incorrect option!!!!" << endl;
            }
            board.SetCell(row, col, player);
            player = Board::CellState::O;
        }
        else if (player == Board::CellState::O)
        {
            auto [row, col] = ai->CalculateTurn(board);

            assert(board.CanSetCell(row, col));
            board.SetCell(row, col, player);
            player = Board::CellState::X;
        }

        game_result = board.CalcWin();

        if (game_result != Board::WinState::Nobody)
        {
            break;
        }
    }

    cout << "__________________" << endl;
    board.Print();
    cout << "END GAME" << endl;
    cout << "__________________" << endl;
    switch (game_result)
    {
    case Board::WinState::Tie:
        cout << "Draw!!!" << endl;
        break;
    case Board::WinState::X:
        cout << "X's win!" << endl;
        break;
    case Board::WinState::O:
        cout << "O's win!" << endl;
        break;
    }
}

int main()
{
    while (true)
    {
        PlayGame();
        char choose = 0;
        while (choose != 'Y' && choose != 'N')
        {
            cout << "Again? (Y/N) ";
            cin >> choose;
        }

        if (choose == 'N')
        {
            break;
        }
    }

    return 0;
}
→ Ссылка