C#(或任何其他语言)如何读取代码?我是一个非常新手

How does C# (or any other language) read the code? I'm a very newie

提问人:Frank 提问时间:8/20/2023 更新时间:8/20/2023 访问量:66

问:

似乎我仍然不明白编程是如何工作的(也许是因为我已经学习了大约 7 天左右的 C#),而且我仍然不知道它如何读取您的代码。

看,我必须使用 c# 和控制台创建井字游戏。当我最终完成任务时,我看了一下正确的方法,当他们使用 200 行时,我使用了 500 行。

关键是他们做了一个“Do While”循环,在循环中,他们使用一个“if”调用一个方法,该方法使用玩家的数量和输入作为参数。当“if”关闭时,他们输入“Console.WriteLine”,要求提供他们之前需要使用的输入。当你运行它时,他们会在做任何事情之前要求你这样做。为什么会这样?

我自己也想不通为什么会这样工作。当他们在关闭时要求您输入时,这个“如果”怎么可能起作用。这是他代码的一部分,我的意思是:

 int player = 2;
 int input = 0;
 bool correctInput = true;

CreateBoard();

do
{
    if(player == 2)
    {
        player = 1;
        PutXorO(player, input);
    }else if(player == 1)
    {
        player = 2;
        PutXorO(player, input);
    }

    do
    {
        Console.WriteLine("\nPlayer{0}, please, select a number...", player);
        input = Convert.ToInt32(Console.ReadLine());

    } while (!correctInput);

} while (true);

编辑:在写这篇文章时,我认为这并不像我想象的那么难,但希望得到一些帮助。太好了!

在看到答案之前,我所做的是把所有这些东西放在你问的地方,在我的代码的第一行,这就是我的大脑理解这个逻辑的方式,但我需要改变它,这样我才能开始作为一个优秀的程序员思考。

我真的很想进步,相信我。我为此对自己感到不安。

C# 性能 循环 方法 逻辑

评论

2赞 Tim Roberts 8/20/2023
东西从上到动。在本例中,他们将值初始化为 0 和 2。第一次通过这个循环时,玩家 1 将选择值 0,因为这些变量已经有值了。只有这样,他们才会要求玩家采取行动。这不是一个特别好的写法。inputplayer
1赞 Michael Schönbauer 8/20/2023
同意最后一句话。好的编程不是写短而晦涩的行(在低级或高性能编程中也有例外)。它是关于在 90% 的时间内编写可维护的可读代码。所以这就是你应该学习的。我只能重复一遍:这不是一个特别好的写法
2赞 Fildor 8/20/2023
不要把这个作为一个好的学习例子。事实并非如此。不仅控制流...让我们说令人惊讶。这也很大程度上取决于方法的副作用。它绝对不会通过我的公关审查。
1赞 BJ Myers 8/20/2023
呼应其他人的评论 - 在编程中,很少有一种“正确”的方法来解决问题。解决问题的方法有很多种,其中一些根据高度主观的标准被衡量为更“正确”。IMO,帖子中的代码绝对不是编写此代码的好方法。我会因为和你一样的原因而感到困惑。

答:

0赞 Özgür Güzeldereli 8/20/2023 #1

首先,如果不查看代码的其余部分,就不可能再说什么,但我会尝试通过提供一些见解来尽可能多地解释代码。

作为一般经验法则,代码是从上到下和从左到右读取的,外部结构在其嵌套或内部组件之前读取。

守则

正如我之前所说,我不知道代码的其余部分。因此,我将做出一些假设。

循环具有特定用途。当您遇到以下情况时,最常使用它:do-while

为了简单起见,想象一个程序不断提示用户输入一个数字,直到该数字是“有效”数字,即该数字介于 1 和 10 之间。

对于正常循环,这将按如下方式完成:while

Console.WriteLine("Enter a number between 1 and 10: ");
int number = int.Parse(Console.ReadLine());

while (number < 1 || number > 10)
{
    Console.WriteLine("Invalid number. Enter a number between 1 and 10: ");
    number = int.Parse(Console.ReadLine());
}

Console.WriteLine("You entered a valid number: " + number);

在这个例子中,我们需要在进入循环之前向用户询问号码。或者,这可以使用循环来实现。do-while

int number;
        
do
{
    Console.WriteLine("Enter a number between 1 and 10:");
    number = int.Parse(Console.ReadLine());
} 
while (number < 1 || number > 10);

Console.WriteLine("You entered a valid number: " + number);

在本例中,循环初始化一次,使用单个 . Console.ReadLine()

在代码中,程序的这一部分

do
{
    if(player == 2)
    {
        player = 1;
        PutXorO(player, input);
    }else if(player == 1)
    {
        player = 2;
        PutXorO(player, input);
    }

    do
    {
        Console.WriteLine("\nPlayer{0}, please, select a number...", player);
        input = Convert.ToInt32(Console.ReadLine());

    } while (!correctInput);

} while (true);

运行在一个无用的循环上,它与循环没有什么不同。因此,它可以写成:do-whilewhile(true)

while (true)
{
    if(player == 2)
    {
        player = 1;
        PutXorO(player, input);
    }else if(player == 1)
    {
        player = 2;
        PutXorO(player, input);
    }

    do
    {
        Console.WriteLine("\nPlayer{0}, please, select a number...", player);
        input = Convert.ToInt32(Console.ReadLine());

    } while (!correctInput);
}

当程序启动时,被初始化为 。因此,它工作正常。但考虑到两个玩家都还没有采取行动,可以假设该方法可以像这样:input0PutXorO(player, input)PutXorO()

public void PutXorO(int player, int input)
{
    if(input == 0)
    {
        return;
    }

    // The rest of the code
}

接下来,第二个循环开始发挥作用的部分。现在,这部分对我来说不是很清楚,仅仅是因为这一行:do-while

while (!correctInput);

在代码的开头,设置为 。因此,这个 while 循环永远不会起作用,因为变量在循环本身中永远不会改变,这意味着循环条件总是解析为 。我相信方法发生了变化,这不仅令人困惑,而且毫无用处。correctInputtruecorrectInputfalsecorrectInputPutXorO()

如何替代它?

在这一部分中,我将尝试从头开始编写一个井字游戏。请注意,可能存在一些错误。我相信评论中的人会纠正这些错误。

首先,我将构造一个简单的板,它将是一个一维字符串数组。在形式中:

/* The tic tac toe board looks like this, with locations from 1 to 9:
* -------------------------
* |      |       |        |
* |  1   |   2   |   3    |
* |      |       |        |
* -------------------------
* |      |       |        |
* |  4   |   5   |   6    |
* |      |       |        |
* -------------------------
* |      |       |        |
* |  7   |   8   |   9    |
* |      |       |        |
* -------------------------
*/

string[] TicTacToeBoard = new string[9];

// Initialize board
for (int i = 0; i < TicTacToeBoard.Length; i++)
{
    TicTacToeBoard[i] = string.Empty; 
}

现在,我想初始化将在控制流中使用的几个变量:

int currentPlayer = 1; // The current player
bool gameOver = false; // Whether or not the game is going on
int moves = 0; // Keeping track of the moves to see if the board is filled

我们需要一个简单的循环来跟踪变量:gameOver

while (!gameOver)
{
    
}

我们将要求当前玩家提供位置信息

while (!gameOver)
{
    Console.WriteLine($"Player {currentPlayer}, it's your turn. Enter a location (1-9):"); 
    int input = Convert.ToInt32(Console.ReadLine());
}

在这一点上,我们将有一种方法将 X 或 O 放入电路板中,如果输入有效,则返回 true,如果输入无效,则返回 false。我想出了这个:

// Returns false if the location is invalid or already taken
static bool PutXorO(string[] board, int player, int location)
{
    if (location < 1 || location > 9)
    {
        return false;
    }

    if (player == 1 && board[location - 1] == string.Empty)
    {
        board[location - 1] = "X";
    }
    else if (player == 2 && board[location - 1] == string.Empty)
    {
        board[location - 1] = "O";
    }
    else
    {
        return false;
    }

    return true;
}

因此,我们现在可以将输入放入板中,或者如果输入无效,则重复移动

while (!gameOver)
{
    Console.WriteLine($"Player {currentPlayer}, it's your turn. Enter a location (1-9):"); 
    int input = Convert.ToInt32(Console.ReadLine());

    if(PutXorO(TicTacToeBoard, currentPlayer, input))
    {
        // Valid Input, proceed with the code
    }
    // Invalid Input, loop back and ask the same player for an input
}

现在,如果输入无效,代码将循环回并要求同一玩家提供新的输入。您可能会添加错误消息,但我选择不这样做。另一方面,如果输入有效,我们需要增加移动次数,检查获胜或平局,并交换当前玩家。

while (!gameOver)
{
    Console.WriteLine($"Player {currentPlayer}, it's your turn. Enter a location (1-9):"); 
    int input = Convert.ToInt32(Console.ReadLine());

    if(PutXorO(TicTacToeBoard, currentPlayer, input))
    {
        moves++;

        // TODO: Check for wins
        ...
        //

        if(moves == 9)
        {
            Console.WriteLine("It's a tie!");
            gameOver = true;
        }

        currentPlayer = currentPlayer == 1 ? 2 : 1;
    }
}

在这种情况下,如果移动数为 9 并且没有玩家获胜,则为平局。最后,我们所要做的就是检查所有获胜条件的方法。为此,我编写了以下方法:

static bool CheckPlayerWin(string[] board, int player)
{
    string playerToken = player == 1 ? "X" : "O";

    int[][] winCombos = new int[][]
    {
        new int[] { 0, 1, 2 },
        new int[] { 3, 4, 5 },
        new int[] { 6, 7, 8 },
        new int[] { 0, 3, 6 },
        new int[] { 1, 4, 7 },
        new int[] { 2, 5, 8 },
        new int[] { 0, 4, 8 },
        new int[] { 2, 4, 6 }
    };

    foreach (var combo in winCombos)
    {
        if (board[combo[0]] == playerToken && board[combo[1]] == playerToken && board[combo[2]] == playerToken)
        {
            return true;
        }
    }

    return false;
}

此方法检查棋盘上的所有获胜组合。此方法可以进一步包装在一个方法中,该方法将返回具有一个的播放器(如果有或其他)。CheckWin()0

static int CheckWin(string[] board)
{
    if (CheckPlayerWin(board, 1)) return 1;
    if (CheckPlayerWin(board, 2)) return 2;
    return 0;
}

最后,主循环可以按以下方式完成:

while (!gameOver)
{
    Console.WriteLine($"Player {currentPlayer}, it's your turn. Enter a location (1-9):"); 
    int input = Convert.ToInt32(Console.ReadLine());

    if(PutXorO(TicTacToeBoard, currentPlayer, input))
    {
        moves++;

        int playerWon = CheckWin(TicTacToeBoard);

        if(playerWon != 0)
        {
            Console.WriteLine($"Player {playerWon} has won the game!");
            gameOver = true;
        }
        else if(moves == 9)
        {
            Console.WriteLine("It's a tie!");
            gameOver = true;
        }

        currentPlayer = currentPlayer == 1 ? 2 : 1;
    }
}

请注意,只有当第九步没有赢家时,此代码才会检查平局。

整个代码变为:

internal class Program
{
    /* The tic tac toe board looks like this, with locations from 1 to 9:
     * -------------------------
     * |      |       |        |
     * |  1   |   2   |   3    |
     * |      |       |        |
     * -------------------------
     * |      |       |        |
     * |  4   |   5   |   6    |
     * |      |       |        |
     * -------------------------
     * |      |       |        |
     * |  7   |   8   |   9    |
     * |      |       |        |
     * -------------------------
     */


    // Returns false if the location is invalid or already taken
    static bool PutXorO(string[] board, int player, int location)
    {
        if (location < 1 || location > 9)
        {
            return false;
        }

        if (player == 1 && board[location - 1] == string.Empty)
        {
            board[location - 1] = "X";
        }
        else if (player == 2 && board[location - 1] == string.Empty)
        {
            board[location - 1] = "O";
        }
        else
        {
            return false;
        }

        return true;
    }

    static bool CheckPlayerWin(string[] board, int player)
    {
        string playerToken = player == 1 ? "X" : "O";

        int[][] winCombos = new int[][]
        {
            new int[] { 0, 1, 2 },
            new int[] { 3, 4, 5 },
            new int[] { 6, 7, 8 },
            new int[] { 0, 3, 6 },
            new int[] { 1, 4, 7 },
            new int[] { 2, 5, 8 },
            new int[] { 0, 4, 8 },
            new int[] { 2, 4, 6 }
        };

        foreach (var combo in winCombos)
        {
            if (board[combo[0]] == playerToken && board[combo[1]] == playerToken && board[combo[2]] == playerToken)
            {
                return true;
            }
        }

        return false;
    }

    static int CheckWin(string[] board)
    {
        if (CheckPlayerWin(board, 1)) return 1;
        if (CheckPlayerWin(board, 2)) return 2;
        return 0;
    }

    static void Main(string[] args)
    {
        string[] TicTacToeBoard = new string[9];

        // Initialize board
        for (int i = 0; i < TicTacToeBoard.Length; i++)
        {
            TicTacToeBoard[i] = string.Empty; 
        }

        int currentPlayer = 1;
        bool gameOver = false;
        int moves = 0;

        while (!gameOver)
        {
            Console.WriteLine($"Player {currentPlayer}, it's your turn. Enter a location (1-9):"); 
            int input = Convert.ToInt32(Console.ReadLine());

            if(PutXorO(TicTacToeBoard, currentPlayer, input))
            {
                moves++;

                int playerWon = CheckWin(TicTacToeBoard);

                if(playerWon != 0)
                {
                    Console.WriteLine($"Player {playerWon} has won the game!");
                    gameOver = true;
                }
                else if(moves == 9)
                {
                    Console.WriteLine("It's a tie!");
                    gameOver = true;
                }

                currentPlayer = currentPlayer == 1 ? 2 : 1;
            }
        }
    }
}

我的初步测试表明,这个程序有点有效。可能有几个更新可以改进此程序。例如,您可以添加一种在每次移动时显示棋盘的方式等。

不要犹豫,提出任何问题或指出我犯的任何错误。