在井字棋游戏中检查胜者?

5

在一个两人游戏的井字棋中,最好的获胜方式是什么?目前我正在使用类似以下代码:

if (btnOne.Text == "X" && btnTwo.Text == "X" && btnThree.Text == "X")
{
    MessageBox.Show("X has won!", "X won!");
    return;
}
else
// I'm not going to write the rest but it's really just a bunch
// if statements.

那么我该如何摆脱多个 if 语句呢?

5个回答

11

Something alongs:

rowSum == 3 || columnSum == 3 || diagnolSum == 3

.. ?


不错的更新,我想我可能会采用这种方法,但我会保留问题未答复,看看是否有其他人能想出更好的方法(我们得让他们有机会,对吧?)。 - Kredns
已提取到另一个答案帖子中。 - chakrit
这确定了游戏结束,但没有区分队伍。 - quantumpotato

3

如果您将按钮存储在多维数组中,可以编写一些扩展方法来获取行、列和对角线。

public static class MultiDimensionalArrayExtensions
{
  public static IEnumerable<T> Row<T>(this T[,] array, int row)
  {
    var columnLower = array.GetLowerBound(1);
    var columnUpper = array.GetUpperBound(1);

    for (int i = columnLower; i <= columnUpper; i++)
    {
      yield return array[row, i];
    }
  }

  public static IEnumerable<T> Column<T>(this T[,] array, int column)
  {
    var rowLower = array.GetLowerBound(0);
    var rowUpper = array.GetUpperBound(0);

    for (int i = rowLower; i <= rowUpper; i++)
    {
      yield return array[i, column];
    }
  }

  public static IEnumerable<T> Diagonal<T>(this T[,] array,
                                           DiagonalDirection direction)
  {
    var rowLower = array.GetLowerBound(0);
    var rowUpper = array.GetUpperBound(0);
    var columnLower = array.GetLowerBound(1);
    var columnUpper = array.GetUpperBound(1);

    for (int row = rowLower, column = columnLower;
         row <= rowUpper && column <= columnUpper;
         row++, column++)
   {
      int realColumn = column;
      if (direction == DiagonalDirection.DownLeft)
        realColumn = columnUpper - columnLower - column;

      yield return array[row, realColumn];
    }
  }

  public enum DiagonalDirection
  {
    DownRight,
    DownLeft
  }
}

如果您使用一个3行3列的TableLayoutPanel,您可以轻松地通过编程方式创建按钮并将其存储到Button[3, 3]数组中。

Button[,] gameButtons = new Button[3, 3];

for (int row = 0; column <= 3; row++)
  for (int column = 0; column <= 3; column++)
  {
    Button button = new Button();
    // button...
    gameLayoutPanel.Items.Add(button);
    gameButtons[row, column] = button;
  }

检查是否有获胜者:

string player = "X";
Func<Button, bool> playerWin = b => b.Value == player;
gameButtons.Row(0).All(playerWin) ||
// ...
gameButtons.Column(0).All(playerWin) ||
// ...
gameButtons.Diagonal(DiagonalDirection.DownRight).All(playerWin) ||
// ...

2

另一种简单的方法是将可赢的位置保存为数据,然后使用循环检查所有可能的获胜情况,而不是多个if语句。

// winnable positions
var winnables = new[] {
    "012",
    "345",
    "678",
    "036",
    "147",
    "258",
    "048",
    "246"
};

// extracted from btnOne Two Three....
var gameState = new[] { "X", "O", "X", "whatever" };


string winner = null;

// check each winnable positions
foreach (var position in winnables) {

    var pos1 = int.Parse(position[0].ToString());
    var pos2 = int.Parse(position[1].ToString());
    var pos3 = int.Parse(position[2].ToString());

    if (gameState[pos1] == gameState[pos2] &&
        gameState[pos2] == gameState[pos3])
        winner = gameState[pos1];

}

// do we have a winner?
if (!string.IsNullOrEmpty(winner))
    /* we've got a winner */

基本上,不要使用btnOne btnTwo btnThree这样的名称,而是使用一个正确的按钮数组或一个以更易于访问的格式保存游戏状态的数组,这样计算会更容易。


int.Parse有点丑...但它有助于保持可赢的位置易于阅读。 - chakrit

0

我知道你已经不再寻找这个问题的答案了。但是,也许有人还在寻找。

使用@fran-casadome的answer和@chakrit的answer,我会像这样做。硬编码的可能性似乎是合适的。Linq让我们用流畅的步骤包装逻辑。非常好的使用方法。

private bool IsWinner(char player) => new[]
{
  new[] { 0, 1, 2 },
  new[] { 3, 4, 5 },
  new[] { 6, 7, 8 },
  new[] { 0, 3, 6 },
  new[] { 1, 4, 7 },
  new[] { 2, 5, 8 },
  new[] { 0, 4, 8 },
  new[] { 2, 4, 6 }
}.Any(indexes => indexes.All(index => _data[index] == player));

这是剩余的部分,为了上下文。

private readonly char[] _data =
  new[] { '1', '2', '3', '4', '5', '6', '7', '8', '9' };

public bool GameOver { get; private set; } = false;

public char? WinningPlayer { get; private set; } = null;

public char CurrentPlayer { get; private set; } = 'x';

public void Play(int position)
{
  var index = Array.IndexOf(_data, position);

  if (GameOver || index == -1)
  {
    return;
  }

  if (GameOver = IsWinner(_data[index] = CurrentPlayer))
  {
    WinningPlayer = CurrentPlayer;
  }
  else
  {
    CurrentPlayer = CurrentPlayer == 'x' ? 'o' : 'x';
  }
}

0

我倾向于这样做:

bool x_wins =
    Enumerable
        .Range(0, 3)
        .SelectMany(i => new Func<int, string>[] { x => array[i, x], x => array[x, i] })
        .Concat(new Func<int, string>[] { x => array[x, x], x => array[2 - x, x], })
        .Where(f => String.Concat(Enumerable.Range(0, 3).Select(x => f(x))) == "XXX")
        .Any();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接