4х4 крестики-нолики


Я только начала кодирования 10 дней назад. Это моя первая программа с использованием классов на языке C++. Это два игрока игра 4х4 крестики-нолики. Он отлично работает. Если есть какие-то улучшения, я могу сделать, пожалуйста, дайте мне знать.

#include <iostream>

using namespace std;



class Board
{
char board[4][4];
public:
Board()
{
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++)
        {
            board[i][j]='_';
        }
    }
}


void printBoard()
{

    for(int i=0;i<4;i++)
    {
        for(int j=0;j<4;j++)
        {
            cout<<board[i][j]<<'|';
        }
    cout<<endl;
    }
cout<<endl;
}
int setPosition(char choice)
{
while(1)
{
int row,column;
cout<<"Please enter the row and column"<<endl;
cin>>row>>column;
if(board[row][column]=='_')
{
board[row][column]=choice;
break;

}
else
{
cout<<"Position is taken"<<endl;

}
}
}
char getPosition()
{
for(int i=0;i<4;i++)
{
    for(int j=0;j<4;j++)
    {
        return board[i][j];
    }
    }

}
int checkHorizontal(char choice)
{


int count;
for(int i=0;i<4;i++)
{
    count=0;
    for(int j=0;j<4;j++)
    {
        if(board[i][j]==choice)
        {
            count++;
        }
        if(count==4)
        {

            return 1;
        }
    }

}
return 0;
}
int checkVertical(char choice)
{
int count;
for(int i=0;i<4;i++)
{
    count=0;
    for(int j=0;j<4;j++)
    {
        if(board[j][i]==choice)
        {
            count++;
        }
        if(count==4)
        {

            return 1;
        }
    }

}
return 0;
}
int checkprincipalDiagonal(char choice)
{
int count=0;
for(int i=0;i<4;i++)
{
    if (board[i][i]==choice)
    {
        count++;
    }

    }
if(count==4)
{

  return 1;
}


return 0;
}
int checkotherDiagonal(char choice)
{
int count=0;
for(int i=0;i<4;i++)
{

    for(int j=0;j<4;j++)
    {

        if((i+j)%3==0 && i!=j)
        {
         if (board[i][j]==choice)
        {
           count++;
        }
        }


    }
}

if(count==4)
{

    return 1;
}
    else
    {
        return 0;
    }
}
int checkDraw()
{
for(int i=0;i<4;i++)
{
    for(int j=0;j<4;j++)
    {
        if(board[i][j]=='_')
        {
            return 1;
            break;
        }

    }
 }
return 0;
}

};
class Player
{
string Name;
char choice;
public:
void setName(string NameIn)
{
 Name=NameIn;
}
void setChoice(char choiceIn)
{
    choice=choiceIn;
}
char getChoice()
{
    return choice;
    cout<<endl<<endl;
}

string getName()
{
    return Name;
    cout<<endl<<endl;
}
};
int main()
{
char choice1,choice2;
string Name1,Name2;
Board b1;
cout<<"The board is printed below:"<<endl;
b1.printBoard();
cout<<endl;
Player p1,p2;
cout<<"Player 1,Please Enter your name"<<endl<<endl;
cin>>Name1;
cout<<endl<<endl<<"Player 2,Please Enter your name"<<endl<<endl;
cin>>Name2;
p1.setName(Name1);
p2.setName(Name2);
cout<<endl<<endl<<"Player 1,Please Enter a character to use"<<endl<<endl;
cin>>choice1;
cout<<endl<<endl<<"Player 2,Please Enter a character to use"<<endl<<endl;
cin>>choice2;
p1.setChoice(choice1);
p2.setChoice(choice2);
while(1)
{
cout<<endl<<endl<<"Your turn,"<<Name1<<endl<<endl;
b1.setPosition(choice1);
b1.getPosition();
b1.printBoard();
if(b1.checkHorizontal(choice1)==1 || b1.checkVertical(choice1)==1|| 
b1.checkprincipalDiagonal(choice1)==1 || b1.checkotherDiagonal(choice1)==1)
{
    cout<<endl<<endl<<"Congrats"<<"\t"<<Name1<<"\t"<<"You have won"<<endl;
    break;
}
cout<<endl<<endl<<"Your turn,"<<Name2<<endl<<endl;
b1.setPosition(choice2);
b1.getPosition();
b1.printBoard();

if(b1.checkHorizontal(choice2)==1 || b1.checkVertical(choice2)==1|| 
b1.checkprincipalDiagonal(choice2)==1 || b1.checkotherDiagonal(choice2)==1)
{
    cout<<endl<<endl<<"Congrats "<<Name2<<" You Have won!"<<endl;
    break;
}
if(b1.checkDraw()==0)
{
    cout<<endl<<endl<<"The match is a draw!"<<endl;
    break;
}


}



}


3238
18
задан 25 марта 2018 в 08:03 Источник Поделиться
Комментарии
2 ответа

Ваш код вполне годится для того, кто начал 10 дней назад. Вот несколько улучшений, которые вы должны рассмотреть:


  1. Ваш код трудно читать из-за недостаточного отступы и интервалы. Взгляните здесь на рекомендуемый отступ стиль для C++.

  2. using namespace std; отлично для простой программы, как это, но это хорошая идея, чтобы сломать эту привычку, используя его. Это считается плохой практикой.

  3. Это всегда целесообразно иметь в виду возможность функционал программы будет расширен в будущем, даже если это кажется маловероятным в настоящее время. Например, ваш текущий код для 4х4 крестики-нолики. Если вы должны были изменить это в будущем 3х3 или 5х5 крестики-нолики, это повлечет за собой значительный объем работ (замена всех 4 на код в новое измерение). Вы в настоящее время используете магическое число , и это будет хорошая идея, чтобы объявить const int С размер доски.

  4. В setPosition(), у вас есть while(1) петли, которые вы завершить с break. Используя break таким образом, считается плохой практикой. Попробуйте использовать делать-цикл while, где тест проводится после каждой итерации, для того, чтобы решить проблему.

  5. У вас есть несколько способов типа int что возвращает 0 или 1 в зависимости от того, является ли условие истинным или ложным. Я предлагаю использовать логический тип данных в качестве типа возвращаемого из этих методов, так это то, что это было сделано для.

  6. Имея 4 методов (checkHorizontal(), checkVertical(), checkprincipalDiagonal() и checkotherDiagonal()) является ненужным, так как их функциональные возможности могут быть объединены в 1 способ. Попробуйте подумать о том, как можно использовать 1 способ с 1 вложенного цикла для проведения всех проверок.

  7. Также обратите внимание, что имена checkprincipalDiagonal() и checkotherDiagonal() не используйте надлежащее верблюжьего.

Это должно установить Вас в правильном направлении, удачи в кодировании!

23
ответ дан 25 марта 2018 в 09:03 Источник Поделиться

Для кодирования с 10 дней, эта программа действительно впечатляет. Если бы мне пришлось узнать что-то новое и сложное, как C++, я бы, вероятно, сделать намного больше ошибок.

Единственное, что всегда сложно-это вход и выход. Особенно входной, поскольку вам придется иметь дело с забавными пользователей, которые заходят 1,4 для координат, так как они не знают, что вы ожидаете 1 4.

Я попытался это случайно, и ваша программа ответила:

Please enter the row and column
1,4
_|_|_|_|
a|_|_|_|
_|_|_|_|
_|_|_|_|

Your turn,Roland

Please enter the row and column
Position is taken
Please enter the row and column
Position is taken
Please enter the row and column
Position is taken
Please enter the row and column
Position is taken
...

Так что там явно что-то не так. Я решил это с помощью следующего кода, который заботится о многих возможных исхода:

void play(Board &board, Player &player) {
while (true) {
std::cout << "Please enter the row and column (1..4): ";

// If a simple string cannot be read, something is seriously
// broken. Stop the whole program.
std::string line;
if (!std::getline(std::cin, line)) {
std::exit(std::cin.eof() ? 0 : 1);
}

// Let the user type the coordinates either as "1 4"
// or as "1,4" or "1, 4" or any other variant.
std::replace(line.begin(), line.end(), ',', ' ');

int row, column;
if (!(std::stringstream(line) >> row >> column)) {
std::cout << "Please enter two numbers\n";
continue;
}

// Make sure that the coordinates are correct.
// Otherwise the program may crash or do something entirely different.
// This is called "undefined behavior" and it should frighten you.
row--;
column--;
if (!(0 <= row && row < 4 && 0 <= column && column < 4)) {
std::cout << "Please enter only numbers in the range 1..4\n";
continue;
}

if (board.at(row, column) != '_') {
std::cout << "Position is already taken\n";
continue;
}

board.playAt(row, column, player);
return;
}
}

Как вы можете видеть, я также сделал некоторые другие изменения в коде. Но самая важная идея заключается в том чтобы читать ввод построчно и затем извлечь координаты из строки. В ваш текущий код, когда я нажмите Enter много раз, прежде чем вводить любое число, то программа не даст мне любую обратную связь.

В аналогичном случае при вводе данных игрока. Я переписал ваш код:

Player input(const std::string &defaultName, char defaultSymbol) {
std::string line;
Player player;

std::cout << defaultName << ", please enter your name: ";
if (std::getline(std::cin, line) && !line.empty()) {
player.name = line;
} else {
player.name = defaultName;
}

std::cout << player.name << ", please enter a character to use: ";
if (std::getline(std::cin, line) && !line.empty()) {
player.symbol = line[0];
} else {
player.symbol = defaultSymbol;
}

return player;
}

Вот, я тоже менял с помощью >> оператор с использованием std::getline так что делает поведение программы гораздо более предсказуемы. Оператор cin >> choice в коде не потреблять ввести ключ, например. Поэтому, когда я изменил программу, чтобы попросить name1, choice1, name2, choice2на ФИО2 была всегда вводится автоматически и пустую строку. Это не может произойти, когда вы последовательно читали все входные линии.

Я также дал полезные значения по умолчанию, так что пользователь может просто нажать Enter четыре раза подряд. Выше функция вызывается так:

int main() {
Player p1 = input("Player 1", 'x');
Player p2 = input("Player 2", 'o');
Board board;

...
}

Это выглядит очень коротким и кратким, подобно тому, как программы в main следует.

Для всех вышеперечисленных изменений, вы, очевидно, должны знать, что такие функции, как std::getline или std::replace существуют и что нужно включить еще один заголовок #include <algorithm> в верхней части вашей программы. Этого нельзя ожидать от новичка, поэтому это всегда хорошо, чтобы спросить, как вы сделали здесь.

Для справки, вот полная программа, которую я написал на основе своего действительно хорошего код.

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <sstream>

class Player {
public:
std::string name;
char symbol{};
};

class Board {
static constexpr int boardSize = 4;
static constexpr int winLength = 4;
char board[boardSize][boardSize]{};
public:
Board() {
for (auto &row : board) {
for (char &cell : row) {
cell = '_';
}
}
}

bool isWin(const Player &player) const {
auto symbol = player.symbol;
return checkHorizontal(symbol)
|| checkVertical(symbol)
|| checkPrincipalDiagonal(symbol)
|| checkOtherDiagonal(symbol);
}

bool isDraw() const {
for (auto &row : board) {
for (char cell : row) {
if (cell == '_') {
return false;
}
}
}
return true;
}

int size() const { return boardSize; }

char at(int row, int col) const { return board[row][col]; }

void playAt(int row, int col, const Player &player) {
board[row][col] = player.symbol;
}

private:
bool checkHorizontal(char symbol) const {
for (auto &row : board) {
int count = 0;
for (char cell : row) {
if (cell == symbol) {
count++;
}
}
if (count == winLength) {
return true;
}
}
return false;
}

bool checkVertical(char symbol) const {
for (int i = 0; i < boardSize; i++) {
int count = 0;
for (auto &row : board) {
if (row[i] == symbol) {
count++;
if (count == winLength) {
return true;
}
} else {
count = 0;
}
}
}
return false;
}

bool checkPrincipalDiagonal(char symbol) const {
int count = 0;
for (int i = 0; i < boardSize; i++) {
if (board[i][i] == symbol) {
count++;
if (count == winLength) {
return true;
}
} else {
count = 0;
}
}
return false;
}

bool checkOtherDiagonal(char symbol) const {
int count = 0;
for (int i = 0; i < boardSize; i++) {
if (board[i][boardSize - 1 - i] == symbol) {
count++;
if (count == winLength) {
return true;
}
} else {
count = 0;
}
}
return false;
}
};

void print(const Board &board) {
for (int row = 0; row < board.size(); ++row) {
for (int col = 0; col < board.size(); ++col) {
std::cout << '|' << board.at(row, col);
}
std::cout << "|\n";
}
}

void play(Board &board, Player &player) {
while (true) {
std::cout << "Please enter the row and column (1.." << board.size() << "): ";

std::string line;
if (!std::getline(std::cin, line)) {
std::exit(std::cin.eof() ? 0 : 1);
}
std::replace(line.begin(), line.end(), ',', ' ');

int row, column;
if (!(std::stringstream(line) >> row >> column)) {
std::cout << "Please enter two numbers\n";
continue;
}

row--;
column--;
if (!(0 <= row && row < board.size() && 0 <= column && column < board.size())) {
std::cout << "Please enter only numbers in the range 1.." << board.size() << "\n";
continue;
}

if (board.at(row, column) != '_') {
std::cout << "Position is already taken\n";
continue;
}

board.playAt(row, column, player);
return;
}
}

Player input(const std::string &defaultName, char defaultSymbol) {
std::string line;
Player player;

std::cout << defaultName << ", please enter your name: ";
player.name = std::getline(std::cin, line) && !line.empty() ? line : defaultName;

std::cout << player.name << ", please enter a character to use: ";
player.symbol = std::getline(std::cin, line) && !line.empty() ? line[0] : defaultSymbol;

return player;
}

int main() {
Player player1 = input("Player 1", 'x');
Player player2 = input("Player 2", 'o');
Board board;
print(board);

Player *turn = &player1;
while (true) {
std::cout << "Your turn, " << turn->name << ".\n";
play(board, *turn);
print(board);

if (board.isWin(*turn)) {
std::cout << "Congrats, " << turn->name << ", you have won.\n";
break;
}

if (board.isDraw()) {
std::cout << "The match is a draw!\n";
break;
}

turn = turn == &player1 ? &player2 : &player1;
}
}

Не стесняйтесь менять две константы boardSize и winLength. Доска размером 19 и выиграть длина 5 сделать интересную игру. Поскольку эти два значения 4 не так легко различить в ваш код, это хороший стиль, чтобы давать такие цифры значащие имена.

11
ответ дан 25 марта 2018 в 11:03 Источник Поделиться