首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++中的Tic脚趾游戏

C++中的Tic脚趾游戏
EN

Code Review用户
提问于 2020-12-05 06:00:01
回答 2查看 4.7K关注 0票数 9

我是一个初级程序员,并在c++中做了一个toe程序。我需要一些帮助,我的记忆管理可以任何人审查和帮助我。这是我的密码

代码语言:javascript
复制
// This is a trial for tic tac toe in C++
#include <iostream>
using namespace std;

char *squares = new char[9]{'1', '2', '3', '4', '5', '6', '7', '8', '9'};

void play();
void getBoard();
int checkWin();

int main()
{
    char *playAgain = new char;
    do
    {
        play();
        cout << "Do you want to play again(y/n): ";
        cin >> *playAgain;

    } while (tolower(*playAgain) == 'y');
    delete squares, playAgain;
    cin.get();
    return 0;
}

//Play the game
void play()
{
    int *i = new int;
    int player = 1;
    int *choice = new int;
    char *mark = new char;

    do
    {
        getBoard();
        player = (player%2) ? 1 : 2;
        cout << "Enter your choice: ";
        cin >> *choice;
        *mark = (player == 1) ? 'X' : 'O';

        if (squares[0] == '1' && *choice == 1)
        {
            squares[0] = *mark;
        }
        else if (squares[1] == '2' && *choice == 2)
        {
            squares[1] = *mark;
        }
        else if (squares[2] == '3' && *choice == 3)
        {
            squares[2] = *mark;
        }
        else if (squares[3] == '4' && *choice == 4)
        {
            squares[3] = *mark;
        }
        else if (squares[4] == '5' && *choice == 5)
        {
            squares[4] = *mark;
        }
        else if (squares[5] == '6' && *choice == 6)
        {
            squares[5] = *mark;
        }
        else if (squares[6] == '7' && *choice == 7)
        {
            squares[6] = *mark;
        }
        else if (squares[7] == '8' && *choice == 8)
        {
            squares[7] = *mark;
        }
        else if (squares[8] == '9' && *choice == 9)
        {
            squares[8] = *mark;
        }
        else
        {
            cout << "Invalid move ";

            player--;
            cin.ignore();
            cin.get();
        }

        *i = checkWin();
        player++;

    } while (*i == -1);
    getBoard();
    if (*i == 1)
    {
        cout << "\aPlayer " << --player << " Wins" << endl;
        delete mark, choice, i;
    }
    else
    {
        cout << "\aGame Draw" << endl;
        delete mark, choice, i;
    }
}

// Print the board
void getBoard()
{
    cout << "\n\n\tTic Tac Toe\n\n";

    cout << "Player 1 (X)  -  Player 2 (O)" << endl
         << endl;
    cout << endl;

    cout << "     |     |     " << endl;
    cout << "  " << squares[0] << "  |  " << squares[1] << "  |  " << squares[2] << endl;

    cout << "_____|_____|_____" << endl;
    cout << "     |     |     " << endl;

    cout << "  " << squares[3] << "  |  " << squares[4] << "  |  " << squares[5] << endl;

    cout << "_____|_____|_____" << endl;
    cout << "     |     |     " << endl;

    cout << "  " << squares[6] << "  |  " << squares[7] << "  |  " << squares[8] << endl;

    cout << "     |     |     " << endl
         << endl;
}

/**********************************************************************************************************
Return 1 if some one wins
Return 0 if draw
Return -1 if the game is not over
***********************************************************************************************************/

int checkWin()
{
    if (squares[0] == squares[1] && squares[1] == squares[2])
    {
        return 1;
    }
    else if (squares[0] == squares[3] && squares[3] == squares[6])
    {
        return 1;
    }
    else if (squares[0] == squares[4] && squares[4] == squares[8])
    {
        return 1;
    }
    else if (squares[3] == squares[4] && squares[4] == squares[5])
    {
        return 1;
    }
    else if (squares[1] == squares[4] && squares[4] == squares[7])
    {
        return 1;
    }
    else if (squares[6] == squares[4] && squares[4] == squares[2])
    {
        return 1;
    }
    else if (squares[6] == squares[7] && squares[7] == squares[8])
    {
        return 1;
    }
    else if (squares[2] == squares[5] && squares[5] == squares[8])
    {
        return 1;
    }
    else if (squares[0] != '1' && squares[1] != '2' && squares[2] != '3' && squares[3] != '4' && squares[4] != '5' && squares[5] != '6' && squares[6] != '7' && squares[7] != '8' && squares[8] != '9')
    {
        return 0;
    }
    else
    {
        return -1;
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2020-12-05 08:21:53

尽量不要触发命名空间std

这被认为是一种糟糕的做法。这是因为引入名称空间是为了避免名称冲突,即具有相同名称的多个对象。std命名空间很大,有数百个公共标识符可以干扰您的命名空间。

通过执行using namespace std,您现在不知道哪些函数是std库的一部分,哪些不是。

new

的使用

我认为您误解了new运算符在C++中的使用。动态内存分配在这里有什么用?我看不出有什么好的理由不能简单地在堆栈上创建变量。这里的问题是,您不能忘记delete。如果你这样做了,你就把你的程序暴露在内存泄漏中--可怕。

您可以在以下情况下使用new

  • 在调用delete之前,不希望销毁对象
  • 当您的对象非常大时

简而言之,这里不需要使用new操作符。这只会带来并发症。

何时使用“新”运算符

使用enum

代码语言:javascript
复制
/**********************************************************************************************************
Return 1 if some one wins
Return 0 if draw
Return -1 if the game is not over
***********************************************************************************************************/

数字1,0,-1被称为幻数幻数,您的评论很有帮助,但是要知道它们的含义,就必须不断地引用。在这里使用enum将清除很多

代码语言:javascript
复制
enum Result { Win , Draw , OnGoing };

return Result::Win;

这样您就可以将您的函数声明为

代码语言:javascript
复制
Result checkWin();

而不是

代码语言:javascript
复制
return (number which means nothing)

你做了

代码语言:javascript
复制
return Result::Win;

值0、1和2分别自动分配给WinDrawOngoing。现在我们有了这些数字的名称,我们使用它们而不是原始数字。这意味着

代码语言:javascript
复制
return Result::Win;

是相同的

代码语言:javascript
复制
return 0;

checkWin()

代码语言:javascript
复制
int checkWin()
{
    if (squares[0] == squares[1] && squares[1] == squares[2])
    {
        return 1;
    }
    else if (squares[0] == squares[3] && squares[3] == squares[6])
    {
        return 1;
    }
    else if (squares[0] == squares[4] && squares[4] == squares[8])
    {
        return 1;
    }
    else if (squares[3] == squares[4] && squares[4] == squares[5])
    {
        return 1;
    }
    else if (squares[1] == squares[4] && squares[4] == squares[7])
    {
        return 1;
    }
    else if (squares[6] == squares[4] && squares[4] == squares[2])
    {
        return 1;
    }
    else if (squares[6] == squares[7] && squares[7] == squares[8])
    {
        return 1;
    }
    else if (squares[2] == squares[5] && squares[5] == squares[8])
    {
        return 1;
    }
    
}

要做到这一点,一个更容易读的方法是将模式存储在数组中,并且每次您想要检查是否有赢家时,您都会遍历该数组并使用它们。

代码语言:javascript
复制
constexpr int winPatterns[8][3]{
    {0, 1, 2}, // first row
    {3, 4, 5},  // second row

    //...
};

现在,当你想找一个赢家的时候

代码语言:javascript
复制
for (int i = 0;i < 8;i++){
    auto const& line = winPatterns[i];
    const int a = squares[line[0]];
    const int b = squares[line[1]];
    const int c = squares[line[2]];
    if ( a == b and b == c ) return a;
}

绘制

的检查

不要检查每个单元格是否被占用,而是使用最初设置为int0。每次转弯后继续递增。这样你就能得到这样的结果

代码语言:javascript
复制
bool checkDraw(){
    return (checkWin() != Result::Win and counter == 9 );
}

这很简单。如果计数器达到9,但还没有人赢,那就是平局,因为所有的单元格都被占据,而没有赢家。

打印板

代码语言:javascript
复制
cout << "\n\n\tTic Tac Toe\n\n";

    cout << "Player 1 (X)  -  Player 2 (O)" << endl
         << endl;
    cout << endl;

    cout << "     |     |     " << endl;
    cout << "  " << squares[0] << "  |  " << squares[1] << "  |  " << squares[2] << endl;

    cout << "_____|_____|_____" << endl;
    cout << "     |     |     " << endl;

    cout << "  " << squares[3] << "  |  " << squares[4] << "  |  " << squares[5] << endl;

    cout << "_____|_____|_____" << endl;
    cout << "     |     |     " << endl;

    cout << "  " << squares[6] << "  |  " << squares[7] << "  |  " << squares[8] << endl;

    cout << "     |     |     " << endl
         << endl;

您可以在这里使用字符串文字

代码语言:javascript
复制
const char* Heading = R"(
        Tic Tac Toe

Player 1 (X)  -  Player 2 (O)

)";

std::cout << Heading;
票数 12
EN

Code Review用户

发布于 2020-12-05 08:35:38

避免using namespace std

我再怎么强调也不为过,现在就避免。您现在可能感觉不到它的效果,但是当代码库的大小增加时,它就会令人头痛。想象一下,您有一个定义方法cout的类,这看起来很琐碎,谁会用cout定义一个方法呢?但是您应该知道,有许多好的、合理的名称已经被标准的libary所采用,您可以为sincostan找到什么好名称。这样做会使编译器混淆并导致未定义的行为。不仅如此,你还会用不相关的东西污染任何地方。首选

代码语言:javascript
复制
std::cout << "Hello World\n"

代码语言:javascript
复制
using std::cout
cout << "Hello World\n"

在后一种情况下,效果只局限于std::cout

避免全球!

对于programmer来说,只有一个全局可能是合理的,但它会导致许多微妙的错误,并使代码依赖于这些全局。

代码语言:javascript
复制
char *squares = new char[9]{'1', '2', '3', '4', '5', '6', '7', '8', '9'};

可以在main()中定义并作为参数传递给其他函数。

内置程序从不在本地作用域

中隐式定义

像整数类型和浮点类型这样的内置程序从不隐式初始化,

代码语言:javascript
复制
int a;

上面的代码段是未初始化的,是很难跟踪bug的来源,如果有疑问,请避免显式地初始化变量。这意味着,上面的代码段将变成

代码语言:javascript
复制
int a{}

这里,a被初始化为0。

坏删除

代码语言:javascript
复制
delete mark, choice, i;

好的,您想要释放分配给这些变量的内存,但是oops,您做了什么,您成功地删除了markchoicei从未被删除过。一个好的编译器应该警告不要,这强调你应该让编译器成为你的朋友,不要让警告隐式传递,照顾它们,否则它们以后可能会伤害你。

代码语言:javascript
复制
delete mark;
delete choice;
delete i;

现在,更好的是,我们明确了我们想要做的事情。

Detour

  1. getBoard()是一个误导性的名称,变量命名是编程的核心,必须尽可能明确地表达它的意图,乍一看getBoard(),我会期待一个面板数组作为返回值,但它会惊讶地打印一个板到stdout,一个更好的名字将是printBoarddisplayBoard,这一点对我这样的读者来说是如此的清楚。
  2. 现在,在游戏结束后,用户可能想再玩一次,但是您的游戏并没有重新设置自己,您可能想修复这个微妙的错误。
  3. 您代码的某些部分非常混乱,我只想强调其中的几个部分。现在,你有大量的支票只是为了在一个地点放置一个标记。您可以通过拥有这样一个isValid()函数来简化事情
代码语言:javascript
复制
bool isValid(int loc)
    return loc > 0 && loc <= MAX_SIZE;

然后打个记号就像说

代码语言:javascript
复制
squares[*choice-1] = *mark;

编辑 isValid()检查用户的输入是否在squares的范围内,例如,1是有效的,因为它大于0,小于9(方格的大小)。-1无效,因为它不大于0,尽管它小于9。如果isValid()返回true,我们可以用1来抵消它在squares中的位置,因此1的值意味着squares.0的索引。

这大大减少了代码大小,您可能需要考虑checkwin()的相同方法。

边注

当您有多个if- you语句时,请考虑重构代码。

票数 8
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/253068

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档