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 шт):
Примерно так. Переименовываем main() в, скажем, game(), а main пишем так:
int main()
{
for(char с = 'Y'; с == 'Y';)
{
game();
cout << "Еще? (Y/N) ";
cin >> c;
}
}
Что-то мне подсказывает, что саму игру писали не вы :)
Неплохое название для игры.
Добавить реплей можно перенеся весь код игры в отдельную функцию.
Главная проблема в том что в C++ используется C.
Доску В C++ надо делать отдельным классом(
Board), а не размазывать по всему коду.Следует разделять представление доски(
CellState) и её вывод на экран('X').Следует разделять содержимое доски(
CellState) и результат матча(WinState).В С++ массивы делаются через
std::array, а сишные же массивы ведут к множеству багов.В С++ есть генераторы случайных чисел(
std::mt19937_64) и не нужно использоватьrand() % x.Аи нужно выбрать случайную клетку, так пусть напрямую задаёт col и row.
Аи должен быть отдельным модулем с состоянием. Так легче сменить сторону аи или сделать несколько разных аи.
Если побеждает аи, то игроку всё же надо вывести финальное значение доски.
Для перечисления контейнеров следует использовать 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;
}