1

我正在做一个井字游戏项目,我遇到了一些困难。我只是对收藏有一个松散的把握,所以你可以看到问题所在。

问题是,我创建了 9 个按钮,这些按钮在单击时会更改其背景图像,并禁用它们自己。我已经设法让它为 2 名玩家运行,但我的愿望是创建某种 AI。

我需要一个按钮集合,这样我就可以比较它们的属性,并避免通常的一对一比较和大量的 If 语句。

我真正想要的是能够测试同一行或同一列中的按钮是否具有相同的值(背景图像)。到目前为止,我使用的是一个描述符号值的字符串数组,但它完全与按钮分离,并且输入所有代码需要很长时间。

如果它不能按照我想象的方式完成,请告诉我。我愿意接受建议,将不胜感激。

哦,如果您需要任何代码或更多详细信息,请告诉我。

4

2 回答 2

5

您需要分离数据(数组)和演示文稿(按钮)。

更新

我发布了一个可以编译和运行的示例控制台项目
它的模型与展示是分开的,所以你可以TicTacToe.cs为它编写一个 GUI。

在此处输入图像描述

你不需要字符串;布尔值适用于两种状态。
实际上,还有第三种状态,它是空的,因此您可以使用可为空的布尔值。

所以 X 对应于true,O对应于 ,false空白对应于null
我将创建一个封装可空布尔方形数组的类:

class TicTacToe {
    const int Length = 3;
    private bool? [][] _data;
    private bool? _winner;

    public TicTacToe ()
    {
        _data = Enumerable
            .Range (0, Length)
            .Select (_ => new bool? [Length])
            .ToArray ();
    }
}

然后我将行、列和对角线表示为向量:

public bool? GetCell (int row, int column)
{
    return _data [row][column];
}

public IEnumerable<bool?> GetRow (int index)
{
    return _data [index];
}

IEnumerable<int> GetIndices ()
{
   return Enumerable.Range (0, Length);
}

public IEnumerable<bool?> GetColumn (int index)
{
    return GetIndices ()
        .Select (GetRow)
        .Select (row => row.ElementAt (index));
}

public IEnumerable<bool?> GetDiagonal (bool ltr)
{
    return GetIndices ()
        .Select (i => Tuple.Create (i, ltr ? i : Length - 1 - i))
        .Select (pos => GetCell (pos.Item1, pos.Item2));
}

public IEnumerable<IEnumerable<bool?>> GetRows ()
{
    return GetIndices ()
        .Select (GetRow);
}

public IEnumerable<IEnumerable<bool?>> GetColumns ()
{
    return GetIndices ()
        .Select (GetColumn);
}

public IEnumerable<IEnumerable<bool?>> GetDiagonals ()
{
    return new [] { true, false }
        .Select (GetDiagonal);
}

public IEnumerable<IEnumerable<bool?>> GetVectors ()
{
    return GetDiagonals ()
        .Concat (GetRows ())
        .Concat (GetColumns ());
}

然后我会写一个函数,它接受一个向量并说它是否是一个获胜的向量:

static bool? FindWinner (IEnumerable<bool?> vector)
{
    try {
        return vector
            .Distinct ()
            .Single ();
    } catch (InvalidOperationException) {
        return null;
    }
}

static bool? FindWinner (IEnumerable<IEnumerable<bool?>> vectors)
{
    return vectors
        .Select (FindWinner)
        .FirstOrDefault (winner => winner.HasValue);
}

public bool? FindWinner ()
{
    return FindWinner (GetVectors ());
}

现在我们可以打电话GetWinner来看看是否有人已经赢了。
然后我会写一个方法来采取行动:

public bool MakeMove (int row, int column, bool move)
{
    if (_winner.HasValue)
        throw new InvalidOperationException ("The game is already won.");

    if (_data [row][column].HasValue)
        throw new InvalidOperationException ("This cell is already taken.");

    _data [row][column] = move;
    _winner = FindWinner ();

    return move == _winner;
}

public bool? Winner {
    get { return _winner; }
}

这都是在TicTacToe课堂上。
您的 GUI 应该创建它并调用它的方法。

单击按钮时,您可以执行以下操作:

private TicTacToe _game = new TicTacToe ();
private Button [][] _buttons = new Button [][3];

const bool HumanPlayer = true;
const bool AIPlayer = false;

public void HandleButtonClick (object sender, EventArgs e)
{
    // Assuming you put a Tuple with row and column in button's Tag property
    var position = (Tuple<int, int>) ((Button) sender).Tag;
    var row = position.Item1;
    var column = position.Item2;

    // Sanity check
    Debug.Asset (sender == _buttons [row][column]);

    bool won = _game.MakeMove (row, column, HumanPlayer);

    if (won) {
        MessageBox.Show ("You won.");
    }

    RefreshButtons ();
}

void RefreshButtons ()
{
    for (var i = 0; i < 3; i++) {
        for (var j = 0; j < 3; j++) {
            var btn = _buttons [i][j];
            var cell = _game.GetCell (i, j);

            btn.Enabled = !cell.HasValue;
            btn.Text = cell.HasValue
                ? (cell.Value ? "X" : "O")
                : string.Empty;
        }
    }
}

您的 AI 还应该调用并MakeMove根据来自调用和 的信息进行GetRow计算。GetColumnGetDiagonal

我没有检查代码,它只是一个草图。(但控制台项目应该运行得很好。)

于 2013-05-14T18:36:39.207 回答
1

我会使用一个包含游戏当前状态的游戏状态类。例如:

public class Game
{
    // These will have null for unselected, true for circle, false for cross, or something like that
    public bool?[][] SquareStates = new bool?[3][3];

    // Maybe a property to show a game is in progress
    public bool GameInProgress = false;

    // Maybe a function to restart game
    public void Restart() { ... }

    // And maybe a function to check for a winner
    public string CheckWinner() { ... }

    // Maybe another function to make AI make its next move
    // and updates SquareStates.
    public void AINextMove(out int row, out int column) { ... }
}

一旦你有了这样的类,你的表单将只包含一个 Game 的实例,然后在按下按钮时更新 SquareStates 数组,然后检查获胜者,调用 AINextMove,再次检查获胜者,然后使用新的 SquareStates。

于 2013-05-14T18:23:53.000 回答