【问题标题】:Recursion function in MineSweeper give stackoverflow error [closed]MineSweeper 中的递归函数给出 stackoverflow 错误 [关闭]
【发布时间】:2021-05-14 13:10:03
【问题描述】:

好的,首先是 28x28 正方形的按钮。当我使用递归函数禁用所有没有文本的按钮(button.Name = string.Empty)时,例如当我单击图像中的 8x2 按钮时,递归函数会禁用从 8x2 到 8x8 的所有按钮在循环中一一向东。 (代码 1) 但是,当我向LoopControlEmpty 函数添加更多递归方式时,以不同的方式(如 N、NW、SE 等)禁用更多块。程序给了我一个 StackOverFlow 错误。 (代码 2)

我需要找到一种方法来用它们的名字调用按钮,而不是在 foreach 循环中打开所有 81 个按钮 但可能它会再次给我同样的错误,因为很多时间它会去递归。或者我需要找到另一种算法来禁用按钮。

按钮名称是这样的 => 对于 0x0 中的按钮 button.Name = "button0x0"

代码 1

private void btn_Click(object sender, EventArgs e)
        {
            // Using sender as Button because i need to reach that buttons properties.
            Button btn = sender as Button;
            if (btn.Text == "M")
            {
                btn.BackColor = Color.FromArgb(255, 0, 0);
                foreach (Button button in this.Controls)
                {
                    button.Enabled = false;
                }
            }
            else if (btn.Text == string.Empty)
            {
                btn.Enabled = false;
                LoopControlEmpty(btn);
            }
            else
                btn.Enabled = false;
        }

        public void LoopControlEmpty(Button btn)
        {
            foreach (Button btnIn in this.Controls)
            {
                // Going East
                if (btn.Location.X + 28 == btnIn.Location.X && btn.Location.Y == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
            }
            return;
        }

代码 2

public void LoopControlEmpty(Button btn)
        {
            foreach (Button btnIn in this.Controls)
            {
                // Going East
                if (btn.Location.X + 28 == btnIn.Location.X && btn.Location.Y == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
                //Going North
                if (btn.Location.X == btnIn.Location.X && btn.Location.Y + 28 == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
                //Goint South-West
                if (btn.Location.X - 28 == btnIn.Location.X && btn.Location.Y -28 == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
                
            }
            return;
        }

【问题讨论】:

  • 我很困惑你为什么要递归。你能不能只从这个函数返回当前的 Button 并继续传递它直​​到Button.Text == ""
  • 现在是学习如何使用调试器单步调试代码并查看发生了什么的好时机。
  • @RufusL 不要成为 *ss 我知道那里发生了什么 :)
  • 不,我是认真的。如果您单步执行代码,您应该能够看到堆栈溢出的原因。
  • @RufusL 它正在溢出,因为发生了大量递归,并且每次它都会打开 81 个按钮

标签: c# recursion stack-overflow


【解决方案1】:

原因是,如果你向东走,然后向西南走,然后向北走,你就会到达起点。这将永远持续下去。

您需要跟踪哪些按钮已被处理。这可以例如使用列表完成:将所有 81 个按钮放入列表中,然后从列表中删除已处理的按钮。

public IList<Button> GetAllButtons()
{
    var buttonsToProcess = new List<Button>();
    foreach (Button btnIn in this.Controls)
    {
        buttonsToProcess.Add(btnIn);
    }
    return buttonsToProcess;
}

public void LoopControlEmpty(Button btn, IList<Button> buttonsToProcess)
{
    var neighbors = new List<Button>();
    foreach (Button btnIn in buttonsToProcess)
    {
        if (btnIn.Text != string.Empty) continue;
        // Going East
        if (btn.Location.X + 28 == btnIn.Location.X && btn.Location.Y == btnIn.Location.Y)
        {
            neighbors.Add(btnIn);
        }
        //Going North
        if (btn.Location.X == btnIn.Location.X && btn.Location.Y + 28 == btnIn.Location.Y)
        {
            neighbors.Add(btnIn);
        }
        //Goint South-West
        if (btn.Location.X - 28 == btnIn.Location.X && btn.Location.Y -28 == btnIn.Location.Y)
        {
            neighbors.Add(btnIn);
        }
    }
    // Make sure to not process them again
    foreach(Button neighbor in neighbors) 
    {
        buttonsToProcess.Remove(neighbor);
    }
    // Process the neighbors
    foreach(Button neighbor in neighbors) 
    {
        neighbor.Enabled = false;
        LoopControlEmpty(neighbor, buttonsToProcess);
    }
}

【讨论】:

  • 不,这 3 个只是例子,我不会只去 N E SW。想象一下所有的方法都在那里,但在 3 种方法中它已经开始给出 Stackoverflow 错误。
  • @LesBenjamim:当然更多的方法会让情况变得更糟。
  • @LesBenjamim:我已经用建议更新了答案
  • 好吧,我没有看到你的答案,所以这可能会解决我的问题,但我不知道当我将按钮放入堆栈时有多少次会产生错误,这就是为什么这种方式减少了很多但它仍然可能会进行很多递归,因为当我按下 8x2 功能时,需要禁用除第一张图片中的 0x5 以外的所有按钮
  • 我确实解决了它。非常感谢!!
【解决方案2】:

这不是一个好方法。在您的按钮矩阵中创建您自己的具有逻辑坐标属性的按钮。

public class MineButton : Button
{
    public int Row { get; set; }
    public int Column { get; set; }
}

你也可以给它添加一个枚举类型的状态属性。

然后以编程方式创建按钮并将它们添加到二维数组(矩阵)中。

private const int FieldWidth = 28, FieldHeight = 28;
private MineButton[,] Field = new MineButton[FieldWidth, FieldHeight];

在表单构造函数中

InitializeComponent();
for (int row = 0; row < FieldWidth; x++) {
    for (int col = 0; col < FieldHeight; col++) {
        var btn = new MineButton{
            Row = row,
            Column = col,
            Location = new Point(10 + 28 * col, 10 + 28 * row),
            Size = new Size(24, 24)
        }
        Field[col, row] = btn;
        Controls.Add(btn);
    }
}

现在查找和处理按钮要容易得多。

private void btn_Click(object sender, EventArgs e)
{
    var btn = sender as MineButton;
    int row = btn.Row;
    int col = btn.Col;

    ...
}

现在有了坐标,就可以在Field矩阵中轻松找到相邻的按钮了。

if (col < FieldWidth - 1) {
    var buttonToTheRight = Field[col + 1, row];
    ...
}

您还应该只处理未处理的按钮来停止递归。例如

if (btn.Enabled) {
    btn.Enabled = false;
    do recursive calls, etc.
}

如果按钮具有状态属性,您还可以根据其状态做出此决定。

【讨论】:

  • 是的,当我收到错误消息时,我想到了这种方法,但我试图找到一种方法来访问带有名称的按钮,这就是为什么我给他们 button.Name = $"button{i}x{j }" 而不是从头开始。有什么方法可以使用 Name 属性来处理这个问题,因为当我按下 3x3 时,如果我只能达到 2x2 2x3 2x4 3x2 3x4 4x2 4x3 4x4 只有这些按钮,问题可能会得到解决?
  • 您还可以添加一个不可见按钮的边框,您只能将其添加到二维数组中,而不是添加到表单中。 Field 数组会大 2 个按钮。这将大大缓解边界条件,因为您始终可以按 3x3 采取行动。
猜你喜欢
  • 1970-01-01
  • 2011-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 2015-09-03
相关资源
最近更新 更多