我刚刚开始编码10天。这是我的第一个使用C ++类的程序。这是一个两个人的4x4井字游戏。完美运作。如果我可以进行任何改进,请告诉我。

#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;
}


}



}


评论

您可以正确缩进代码吗?

@Toto这不是评论的一部分吗?

@πάνταῥεῖ取决于它在复制到堆栈溢出之前是否已格式化,我想......

@Shadow很容易检查。由于TAB被保留但未呈现,因此您可以在编辑模式下看到它。

并不是真正的代码审查注释。但这不应该被称为“井字游戏”吗?

#1 楼

对于10天前开始的人来说,您的代码非常不错。您应该考虑以下几点改进:

由于缩进和间距不足,您的代码难以阅读。请查看此Wikipedia链接,以获取推荐的C ++缩进样式。这被认为是不好的做法。
最好记住将来可能会扩展程序功能的可能性,即使目前看来不太可能。例如,您当前的代码用于4x4井字游戏。如果将来要将其更改为3x3或5x5井字游戏,则需要将代码中的所有using namespace std;替换为新尺寸。您当前使用的是幻数,最好用板子尺寸声明一个4
const int中,您有一个setPosition()循环,以while(1)结尾。以这种方式使用break被认为是不好的做法。为了解决问题,请尝试使用do-while循环,该循环在每次迭代之后进行测试,以解决问题。
您有break类型的几种方法,它们根据条件是true还是false返回int0。我建议使用1数据类型作为这些方法的返回类型,因为这是它的用途。
由于它们的功能可以合并为1,因此不需要4种相似的方法(boolcheckHorizontal()checkVertical()checkprincipalDiagonal())方法。尝试考虑如何使用带有1个嵌套循环的1种方法来执行所有检查。
还要注意,名称checkotherDiagonal()checkprincipalDiagonal()没有使用正确的camelCase。

这应该设置您朝着正确的方向前进,编码愉快!

评论


\ $ \ begingroup \ $
好评!我想补充一点5.实际上是C中的常见惯例。OP可能犯了将C惯例应用于C ++的常见错误。
\ $ \ endgroup \ $
–翼翼
18 Mar 25 '18在10:36

\ $ \ begingroup \ $
非常感谢您提到#2 .....我发誓,仍然这样做的人使我烦恼。直到去年四月,Google的protobuf库都将其放在通用标头中,而我的团队无法理解我为什么如此沮丧。
\ $ \ endgroup \ $
– Mascoj
18 Mar 25 '18在23:36

\ $ \ begingroup \ $
关于#5,一个更大的问题是函数名称不能说明返回值的含义。 true和false对于名为checkDraw()的函数没有多大意义。另一方面,如果将一个函数命名为boardIsFull(),我将确切知道true和false(或1和0)的含义。
\ $ \ endgroup \ $
– Ben Voigt
18 Mar 26 '18在5:25

#2 楼

对于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
...


所以显然出了点问题。我使用以下代码解决了这个问题,该代码处理了许多可能的极端情况:如您所见,我还对代码进行了其他一些更改。但是,这里最重要的想法是逐行读取输入,然后从该行中提取坐标。在您当前的代码中,当我在输入任何数字之前多次按Enter键时,该程序不会给我任何反馈。我将您的代码重写为:

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;
    }
}


这里,我也从使用>>运算符更改为使用std::getline,因为这使程序的行为更加可预测。例如,您代码中的运算符cin >> choice不会使用Enter键。因此,当我将程序更改为name1, choice1, name2, choice2时,name2总是自动输入的,并且为空字符串。当您始终将所有输入读为行时,就不会发生这种情况。上面的函数是这样调用的:

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;
}


这看起来非常简短,就像main中的程序一样。

对于上述所有更改,您显然必须知道这些功能(例如std::getlinestd::replace)存在,并且需要在程序顶部包含另一个带有#include <algorithm>的标头。对于初学者来说,这是无法期望的,因此像您在这里一样询问总是很好。

作为参考,这是我根据您非常好的代码编写的完整程序。

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

    ...
}


随意更改两个常数boardSizewinLength。棋盘大小为19且胜利长度为5时,这很有趣。由于4的这两种含义在您的代码中不易区分,因此给此类数字赋予有意义的名称是一种很好的样式。

评论


\ $ \ begingroup \ $
糟糕,上面的代码不会检查所有对角线,只会检查最长的对角线。
\ $ \ endgroup \ $
–罗兰·伊利格(Roland Illig)
18年3月26日在18:55