我做了这个井字游戏,我想知道你的意见以及可以改进的地方。我还希望获得有关检查是否有人获胜的建议。我是10年级学生,所以我仍然需要学习很多。

评论

欢迎来到华润。由于我们所有人都希望使我们的代码更高效或以一种或另一种方式进行改进,因此请尝试编写一个标题,以概括您的代码的工作,而不是您希望从审查中得到的内容。有关编写好的问题标题的指导,请参阅如何从“代码审查-提问”中获取最大价值。

#1 楼

避免使用平台特定的代码

如果有替代方法,则应尝试避免使用平台特定的代码。
这需要避免#include <windows.h>并使用依赖操作系统理解的命令的system("pause");之类的东西。 >
上有关于如何在命令行上等待用户输入以及如何在SO上的多个平台上清除屏幕的指南。

我必须调整您的代码以使其运行在我的机器上。

将数组用于重复数据

您绝对应该使用数组而不是单个变量。

char x1,x2,x3,x4,x5,x6,x7,x8,x9;

< br会变成

char fields[9];


甚至是二维

char fields[3][3];


您将不得不更改为零基于索引的索引,因此x4在二维情况下将变为fields[3]fields[1][0]。例如,您可能想循环打印而不是一行打印。对于2D阵列,它是:

    for(int row = numberOfRows - 1; row >= 0; --row) {
            for(int col = 0; col < numberOfColumns; ++col) {
                    cout << fields[row][col];
                    if(col < 2) {
                            cout  << "I";
                    } else {
                            cout << "\n";
                    }
            }
    }


在很多情况下,您可能会以某种方式印刷电路板。
这需要一个函数执行打印:

void print(Board fields, std::string columnSeparator = "I", std::string rowSeparator = "\n") {
        for(int row = 2; row >= 0; --row) {
                for(int col = 0; col < 3; ++col) {
                        cout << fields[row][col];
                        if(col < 2) {
                                cout  << columnSeparator;
                        } else {
                                cout << rowSeparator;
                        }
                }
        }
}


此功能可用于:

print(fields);
cout << "Tic Tac Toe\n";


或类似

Board numbers{{'1', '2', '3'},
                {'4', '5', '6'},
                {'7', '8', '9'}};
print(numbers);


不要滥用逗号运算符

在您的代码中,您可以看到许多行,例如

if(x==1 && x1!='X' && x1!='O')if(a%2==0)x1='X', a++, i++;
                                      else x1='O', a++, i++;


您不应滥用逗号运算符将多个命令链接到一个if中。相反,您应该像往常一样用;分隔命令,并将它们分组到一个通用范围内,如下所示:

if(x==1 && x1!='X' && x1!='O') {
    if(a%2==0) {
        x1='X'; 
        a++; 
        i++;
    } else {
        x1='O'; 
        a++; 
        i++;
    }
}


确定通用代码上面的代码在if的不同分支中有很多相似之处。如果相同if的两个分支的尾码相同,则可以将其提取出if:
通常,应始终在控制流结构(如{}ifwhile)之后使用for,以避免在添加一行未将其添加到正确的循环/ if中的错误。提高可读性

上面的代码中的嵌套if可以由三元运算符替换:

if(x==1 && x1!='X' && x1!='O') {
    if(a%2==0) {
        x1='X'; 
    } else {
        x1='O'; 
    }
    a++; 
    i++;
}


成为

if(a%2==0) {
    x1='X'; 
} else {
    x1='O'; 
}


将有效性检查与操作分开

最后,您有了应该设置适当字段的if列表。
首先,我们需要调整它到二维数组为此,我们必须从位置x计算行和列:

x1= (a%2==0) ? 'X' : 'O'; 


复制变量
ia完全相同,因此具有相同的值。那为什么要两个呢?
我还注意到a没有初始化,这导致行为未定义。我们从用户那里读取值,我们必须考虑当用户输入无效值时会发生什么情况。
尽管由于许多if导致您的代码没有错,但最好在代码并告诉用户出了什么问题:

    const int row = (x - 1) / numberOfColumns;
    const int column = (x - 1) % numberOfColumns;

    if(fields[row][column] == '+') {
            fields[row][column] = (a%2==0) ? 'X' : 'O';
            a++;
            i++;
    }


类似地:

    do{
            cout << "Type coordinate of square(number) ";
            cin >> x;
            if(x < 1 || x > 9) {
                    cout << "Invalid value: Expected integer between 1 and 9!\n";
            }
    } while(!(0 < x && x < 10));



名称对于理解变量的含义很重要。
通常,名称离声明越远,使用的名称就越需要描述。
在代码中我将重命名为以下内​​容:



/>

x1-> x2


fields->作为我删除t个重复项x


其他

您不需要从C ++中的fieldIndex中获得i。如果roundIndex在遇到a之前结束,则默认情况下会执行此操作。

不要i会给您带来比在键入时节省的麻烦。

使用正确的格式来提高代码的可读性。通常,每个嵌套级别(return 0;)都会加深缩进。

代码

这是我想到的代码。我仍然需要纠正一些问题,但是这篇文章已经足够长了。 (请注意,我已经删除了屏幕清理功能,但是应该可以以多平台的方式重新插入它。)


评论


\ $ \ begingroup \ $
+1好评价。如果您还提到了有关使用命名空间std的警告,提倡使用一些空白并建议使用对象来表示游戏,那么您将涵盖我将编写的大部分内容。
\ $ \ endgroup \ $
–爱德华
16 Mar 18 '16 at 12:59

\ $ \ begingroup \ $
好的答案,只是想指出char字段[9] = {'+'}等效于x1 ='+'; x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 =' \ 0';而不是原始代码x1 = x2 = x3 = x4 = x5 = x6 = x7 = x8 = x9 ='+';:根据初始化程序为数组分配所有剩余元素的默认初始化。
\ $ \ endgroup \ $
–计算机芯片
16-3-18在13:48



\ $ \ begingroup \ $
@CompuChip:没错。我在最终代码中将其删除,但在本节中将其忘记。另请注意,在2D数组初始化中也会发生类似的问题。
\ $ \ endgroup \ $
–没人远离SE
16 Mar 18 '16 at 13:50

\ $ \ begingroup \ $
我倾向于用C ++的方式而不是print(),并覆盖<<操作符,ostream&操作符<<(ostream&os,const Board&board){...}。然后,您可以只计算<< << board`,这是更多的C ++风格。除此之外,衷心同意这个答案。
\ $ \ endgroup \ $
– Dewi Morgan
16 Mar 18 '16在15:17

\ $ \ begingroup \ $
@MatthieuM:纯粹的监督。有太多需要审查的内容,即使是现在,与其他“问题”相比,我也只看到了一点点细节。
\ $ \ endgroup \ $
–没人远离SE
16 Mar 18 '16在15:20

#2 楼

我添加了@Nobody说的内容。在类中管理游戏状态是个好主意,因为您已经标记了C ++问题。尽管此版本中的代码行更多,但它有助于大大组织代码

main.cpp

#include "game.h"

using std::cout;
using std::cin;
using std::cerr;

void getrc(size_t *row, size_t *col)
{
    for (;;) {
        cout << "Enter the row and column position\n";
        if (cin >> *row >> *col)
            break;
        else {
            cin.clear();
            cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
    }
}

int main()
{
    game g;
    size_t row, col;
    winstate win;
    try {
        cout << "You are " << square2char(g.gethuman()) << '\n';
        do {
            getrc(&row, &col);
            g.set(row - 1, col - 1);
            g.pcset();
            cout << g;
            getchar();
        } while (((win = g.getwinner()) == winstate::NOWIN) && 
                  (win != winstate::TIE));
        cout<<((win==square2win(g.gethuman()))?"you" : "PC")<<" won"<< '\n';
    }
    catch (const char *msg) {
        cerr << msg << '\n';
        getchar();
    }
    getchar();
}



#ifndef BOARD_H
#define BOARD_H
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iostream>
const size_t NROW = 3;
const size_t NCOL = 3;

enum class square { EMPTY, X, O };
char square2char(const square &);
square char2square(char);

class board {
    square arr[NROW][NCOL];
public:
    board();
    board(const board &);
    board &operator=(const board &);
    friend std::ostream &operator<<(std::ostream &, const board &);
    void set(size_t, size_t, square);
    void set(size_t, size_t, char);
    square get(size_t, size_t) const;
};
#endif



#include "board.h"

char square2char(const square &s)
{
    switch (s) {
        case square::EMPTY:
            return ' ';
        case square::O:
            return 'O';
        case square::X:
            return 'X';
    }
}
square char2square(char c)
{
    switch (c) {
        case ' ':
        case '
#ifndef GAME_H
#define GAME_H
#include "board.h"
enum class winstate { NOWIN, TIE, X, O };
winstate square2win(const square &);
square win2square(const winstate &);
class game {
    board *b;
    square human;
    square pc;
public:
    game();
    game(const game &);
    game &operator=(const game &);
    ~game();

    square gethuman() const;
    square getpc() const;

    void set(size_t, size_t);
    void pcset();
    friend std::ostream &operator<<(std::ostream &, const game &);
    winstate getwinner() const;
};
#endif
': return square::EMPTY; case 'X': case 'x': return square::X; case 'O': case 'o': return square::O; } } board::board() { memset(this->arr, 0, sizeof arr); } board::board(const board &that) { memcpy(this->arr, that.arr, sizeof(square) * NROW * NCOL); } board &board::operator=(const board &that) { if (this != &that) memcpy(this->arr, that.arr, sizeof(square) * NROW * NCOL); return *this; } std::ostream &operator<<(std::ostream &os, const board &b) { size_t i, j; for (i = 0; i < NCOL; ++i) { for (j = 0; j < NROW; ++j) os << '[' << square2char(b.arr[i][j]) << ']'; putchar('\n'); } return os; } void board::set(size_t zbrow, size_t zbcol, square s) { if (zbrow >= NROW || zbcol >= NCOL || zbrow < 0 || zbcol < 0) throw "Out of range"; if (this->arr[zbrow][zbcol] != square::EMPTY) throw "Square is full"; this->arr[zbrow][zbcol] = s; } void board::set(size_t zbrow, size_t zbcol, char c) { this->set(zbrow, zbcol, char2square(c)); } square board::get(size_t zbrow, size_t zbcol) const { return this->arr[zbrow][zbcol]; }


game.h

#include "game.h"
winstate square2win(const square &s)
{
    switch (s) {
        case square::EMPTY:
            return winstate::NOWIN;
        case square::O:
            return winstate::O;
        case square::X:
            return winstate::X;
    }
    return winstate::TIE;
}
square win2square(const winstate &w)
{
    switch (w) {
        case winstate::O:
            return square::O;
        case winstate::X:
            return square::X;
    }
    return square::EMPTY;
}
game::game()
{
    srand(time(NULL));

    this->b = new board;
    this->human = (square) (1 + rand() % 2);
    this->pc = (this->human == square::O) ? square::X : square::O;
}
game::game(const game &that)
{
    this->b = new board(*that.b);
    this->human = that.human;
    this->pc = that.pc;
}
game &game::operator=(const game &that)
{
    if (this != &that) {
        delete this->b;

        this->b = new board(*that.b);
        this->human = that.human;
        this->pc = that.pc;
    }
    return *this;
}
game::~game()
{
    delete this->b;
}
square game::gethuman() const
{
    return this->human;
}
square game::getpc() const
{
    return this->pc;
}
void game::set(size_t row, size_t col)
{
    this->b->set(row, col, this->human);
}
void game::pcset()
{
    size_t row, col;

    do {
        row = rand() % 3;
        col = rand() % 3;
    } while (this->b->get(row, col) != square::EMPTY);
    this->b->set(row, col, this->pc);
}
winstate game::getwinner() const
{
    size_t i, j;

    for (i = 0; i < NROW; ++i) {
        if (this->b->get(i, 0) != square::EMPTY
            && this->b->get(i, 0) == this->b->get(i, 1)
            && this->b->get(i, 0) == this->b->get(i, 2))
                return square2win(this->b->get(i, 0));
        if (this->b->get(0, i) != square::EMPTY
            && this->b->get(0, i) == this->b->get(1, i)
            && this->b->get(0, i) == this->b->get(2, i))
                return square2win(this->b->get(0, i));
    }
    if (this->b->get(0, 0) != square::EMPTY && 
              this->b->get(0, 0) == this->b->get(1, 1) && 
              this->b->get(0, 0) == this->b->get(2, 2))
        return square2win(this->b->get(0, 0));
    if (this->b->get(2, 0) != square::EMPTY 
              && this->b->get(2, 0) == this->b->get(1, 1) && 
              this->b->get(2, 0) == this->b->get(0, 2))
        return square2win(this->b->get(2, 0));

    for (i = 0; i < NROW; ++i)
        for (j = 0; j < NCOL; ++j)
            if (this->b->get(i, j) == square::EMPTY)
                return winstate::NOWIN;

    return winstate::TIE;
}
std::ostream &operator<<(std::ostream &os, const game &g)
{
    os << *(g.b);
    return os;
}


game.cpp

q4312078q