Skip to main content
 首页 » 编程设计

c#之在按钮集合中创建、使用和比较元素

2024年01月28日28zfyouxi

我正在制作一个井字游戏项目,但遇到了一些困难。我对集合的了解不多,所以您可以看出问题所在。

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

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

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

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

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

请您参考如下方法:

您需要将数据(数组)和表示(按钮)分开。

Update

I published a sample console project that compiles and runs.
Its model is separate from presentation so you can take TicTacToe.cs and write a GUI for it.

你不需要字符串; bool 值适用于两种状态。
事实上,还有第三种状态,它是空的,所以你可以使用一个可空的 bool 值。

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

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、GetColumn 和 GetDiagonal 的信息进行计算>.

我没有检查代码,它只是一个草图。 (但是 console project 应该运行得很好。)