C#在Tic Tac Toe编码领带

时间:2017-06-04 13:17:29

标签: c# tic-tac-toe

我在C#中开发了一个tic tac toe游戏,并为胜利条件设置了一个函数,但我似乎无法弄清楚当网格已满时如何设置条件。当我尝试输入时,领带会随机出现。我想知道是不是我如何设置or和and运算符。这就是我尝试过的。

public bool Ended() 
{
    if (grid[0, 0] == 'X' && grid[0, 1] == 'X' && grid[0, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
    if (grid[1, 0] == 'X' && grid[1, 1] == 'X' && grid[1, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
    if (grid[2, 0] == 'X' && grid[2, 1] == 'X' && grid[2, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }

    if (grid[0, 0] == 'X' && grid[1, 0] == 'X' && grid[2, 0] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
    if (grid[0, 1] == 'X' && grid[1, 1] == 'X' && grid[2, 1] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
    if (grid[0, 2] == 'X' && grid[1, 2] == 'X' && grid[2, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }

    if (grid[0, 0] == 'X' && grid[1, 1] == 'X' && grid[2, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
    if (grid[0, 2] == 'X' && grid[1, 1] == 'X' && grid[2, 0] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }

    if (grid[0, 0] == 'O' && grid[0, 1] == 'O' && grid[0, 2] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }
    if (grid[1, 0] == 'O' && grid[1, 1] == 'O' && grid[1, 2] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }
    if (grid[2, 0] == 'O' && grid[2, 1] == 'O' && grid[2, 2] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }

    if (grid[0, 0] == 'O' && grid[1, 0] == 'O' && grid[2, 0] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }
    if (grid[0, 1] == 'O' && grid[1, 1] == 'O' && grid[2, 1] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }
    if (grid[0, 2] == 'O' && grid[1, 2] == 'O' && grid[2, 2] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }

    if (grid[0, 0] == 'O' && grid[1, 1] == 'O' && grid[2, 2] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }
    if (grid[0, 2] == 'O' && grid[1, 1] == 'O' && grid[2, 0] == 'O') { Console.WriteLine("PLayer 2 Wins"); return true; }

    else if (grid[0, 0] == 'X' || grid[0, 0] == 'O' &&
        grid[0, 1] == 'X' || grid[0, 1] == 'O' &&
        grid[0, 2] == 'X' || grid[0, 2] == 'O' &&
        grid[1, 0] == 'X' || grid[1, 0] == 'O' &&
        grid[1, 1] == 'X' || grid[1, 1] == 'O' &&
        grid[1, 2] == 'X' || grid[1, 2] == 'O' &&
        grid[2, 0] == 'X' || grid[2, 0] == 'O' &&
        grid[2, 1] == 'X' || grid[2, 1] == 'O' &&
        grid[2, 2] == 'X' || grid[2, 2] == 'O')
    { Console.WriteLine("It's a Tie"); return true; }
    return false;
}

5 个答案:

答案 0 :(得分:14)

根本问题在于程序的缩进与其含义不符。假设我写道:

int x =
   2 + 3 * 
   4 + 5;

很容易认为这应该是5 x 9 = 45,但事实上它是2 + 12 + 5 = 19.

你做了同样的事情。

if (a || b &&
    c || d)

装置

if (a || (b && c) || d)

不是你想要它的意思,这是

if ((a || b) && (c || d))

您可能已经记住乘法优先于加法。您可以记住AND高于OR,因为AND与乘法相同!

true && true == true
true && false == false
false && true == false
false && false == false

如果我们将true替换为1,将false替换为0,将&&替换为*,那么我们会得到真实的陈述:

1 * 1 == 1
1 * 0 == 0
0 * 1 == 0
0 * 0 == 0

现在让我们解决你的程序中的其他问题。

每当您发现两次或两次以上编写相同代码时,请考虑制作方法。切割和粘贴是代码中的坏习惯。

if (grid[0, 0] == 'X' && grid[0, 1] == 'X' && grid[0, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
if (grid[1, 0] == 'X' && grid[1, 1] == 'X' && grid[1, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }
if (grid[2, 0] == 'X' && grid[2, 1] == 'X' && grid[2, 2] == 'X') {  Console.WriteLine("PLayer 1 Wins"); return true; }

我注意到你剪了一个错字!只是另外一个原因让它如此糟糕。

如果您发现正在切割和粘贴,请制定方法:

private bool HasRow(char player, int row)
{
  return grid[row,0] == player && grid[row,1] == player && grid[row,2] == player;
}

现在您可以用

替换该代码
if (HasRow('X', 0) || HasRow('X', 1) || HasRow('X', 2)) { ... }
...
if (HasRow('O', 0) || HasRow('O', 1) || HasRow('O', 2)) { ... }

但是等等,我们再次剪切并粘贴代码。所以解决这个问题!

private bool HasAnyRow(char player) 
{
  return HasRow(player, 0) || HasRow(player, 1) || HasRow(player, 2);
}

现在您的代码变为

if (HasAnyRow('X')) { ... }
...
if (HasAnyRow('O')) { ... }
...

对列和对角线执行相同操作,最后使用

if (HasAnyRow('X') || HasAnyColumn('X') || HasAnyDiagonal('X')) { ... }
if (HasAnyRow('O') || HasAnyColumn('O') || HasAnyDiagonal('O')) { ... }

但是等等,这是仍然过多的复制粘贴。

private bool HasWin(char player) 
{
  return HasAnyRow(player) || HasAnyColumn(player) || HasAnyDiagonal(player);
}

现在代码是:

if (HasWin('X')) { ... }
if (HasWin('O')) { ... }

您的代码将变得更好 - 更容易编写,更易于阅读,更易于调试,更有可能在第一次更正 - 如果您:

  • 通过制作辅助方法而不是复制粘贴代码来消除冗余代码。
  • 制作程序的业务域中的帮助程序方法。什么是井字游戏的胜利?有行,列或对角线。 因此,制作名为HasRow,HasColumn和HasDiagonal 的方法,现在您可以更轻松地推理您的程序因为它是使用程序业务领域中的概念编写的。。 / LI>

答案 1 :(得分:1)

||运算符的优先级低于&&。将or条件放在括号中:

if ((grid[0, 0] == 'X' || grid[0, 0] == 'O') &&
    (grid[0, 1] == 'X' || grid[0, 1] == 'O') &&
    (grid[0, 2] == 'X' || grid[0, 2] == 'O') &&
    (grid[1, 0] == 'X' || grid[1, 0] == 'O') &&
    (grid[1, 1] == 'X' || grid[1, 1] == 'O') &&
    (grid[1, 2] == 'X' || grid[1, 2] == 'O') &&
    (grid[2, 0] == 'X' || grid[2, 0] == 'O') &&
    (grid[2, 1] == 'X' || grid[2, 1] == 'O') &&
    (grid[2, 2] == 'X' || grid[2, 2] == 'O'))
{ 
    Console.WriteLine("It's a Tie"); 
    return true; 
}

答案 2 :(得分:0)

检查网格变量是否不包含''(或用于空的任何内容)

答案 3 :(得分:0)

此解决方案通过完全删除错误代码并使用基于for循环的方法进行最后检查来解决您的问题。它还避免了冗余并使(imho)更容易遵循:

private const char PLAYER_1_SYMBOL = 'X';
private const char PLAYER_2_SYMBOL = 'O';

private readonly char[,] grid = new char[3,3];

public bool Ended()
{
    if (CheckPlayerVictory(PLAYER_1_SYMBOL))
    {
        Console.WriteLine("Player 1 Wins");
        return true;
    }
    else if (CheckPlayerVictory(PLAYER_2_SYMBOL))
    {
        Console.WriteLine("Player 2 Wins");
        return true;
    }
    else if (CheckNoMovesRemaining())
    {
        Console.WriteLine("It's a Tie");
        return true;
    }
    return false;
}

// true, if a match for the given symbol was found in the grid.
private bool CheckPlayerVictory(char symbol)
{
    return (grid[0, 0] == symbol && grid[0, 1] == symbol && grid[0, 2] == symbol)
        || (grid[1, 0] == symbol && grid[1, 1] == symbol && grid[1, 2] == symbol)
        || (grid[2, 0] == symbol && grid[2, 1] == symbol && grid[2, 2] == symbol)
        || (grid[0, 0] == symbol && grid[1, 0] == symbol && grid[2, 0] == symbol)
        || (grid[0, 1] == symbol && grid[1, 1] == symbol && grid[2, 1] == symbol)
        || (grid[0, 2] == symbol && grid[1, 2] == symbol && grid[2, 2] == symbol)
        || (grid[0, 0] == symbol && grid[1, 1] == symbol && grid[2, 2] == symbol)
        || (grid[0, 2] == symbol && grid[1, 1] == symbol && grid[2, 0] == symbol);
}

// true, if no free grid cell is left.
private bool CheckNoMovesRemaining()
{
    int numFreeSpaces = 9;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (grid[i,j] == PLAYER_1_SYMBOL || grid[i,j] == PLAYER_2_SYMBOL)
            {
                numFreeSpaces--;
            }
        }
    }

    return numFreeSpaces <= 0;
}

答案 4 :(得分:0)

这是一种检查方法。首先创建网格的所有8个切片:

var slices =
    Enumerable
        .Range(0, 3)
        .SelectMany(i => new []
        {
            Enumerable.Range(0, 3).Select(j => grid[i, j]),
            Enumerable.Range(0, 3).Select(j => grid[j, i])
        })
        .Concat(new [] { Enumerable.Range(0, 3).SelectMany(i => new [] { grid[i, i] }) })
        .Concat(new [] { Enumerable.Range(0, 3).SelectMany(i => new [] { grid[i, 2 - i] }) })
        .Select(z => String.Join("", z))
        .ToArray();

从此输入开始:

char[,] grid = new char[3, 3]
{
    { 'X', 'O', 'X' },
    { 'O', 'X', 'X' },
    { 'X', 'X', 'O' },
};

那会给你:

slices

现在很容易看出谁赢了:

bool xWins = slices.Any(z => z == "XXX") && slices.All(z => z != "OOO");
bool oWins = slices.Any(z => z == "OOO") && slices.All(z => z != "XXX");

看看董事会是否是抽奖检查,两者都是假的,如果有空位(假设_为空):

bool draw = !xWins && !oWins && slices.All(z => !z.Contains("_"));