MineSweeper 中的递归函数给出计算器溢出错误

时间:2021-02-10 19:33:34

标签: c# recursion stack-overflow

enter image description here

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

我需要找到一种方法来调用带有名称的按钮,而不是将所有 81 个按钮都转入 foreach 循环 但可能它会再次给我同样的错误,因为很多时候它会去递归。或者我需要找到另一种算法来禁用按钮。

按钮名称就是这样 => 用于 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;
        }

2 个答案:

答案 0 :(得分:1)

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

Directions

您需要跟踪哪些按钮已被处理。这可以例如使用列表完成:您将所有 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);
    }
}

答案 1 :(得分:1)

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

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.
}

如果按钮有一个状态属性,你也可以根据它的状态做出这个决定。