【问题标题】:recursive stackoverflow minesweeper c#递归stackoverflow扫雷c#
【发布时间】:2016-03-25 13:54:00
【问题描述】:

我正在写一个扫雷游戏。下面是扫雷中 3 种方法的代码。第一种方法是检查按下按钮周围的所有空间并计算周围有多少炸弹。下一个方法将被递归调用,以便如果用户按下一个周围有 0 个按钮的按钮,它将打开所有也表示它周围有 0 个方块的方块。第三种方法是检查它是否会在检查中绑定。空格递归调用给我一个stackoverflow错误,我做错了什么?

谢谢!

   private int GameLogicChecker(int x, int y)
    {
        int count = 0;
        if (_grid[x, y] != -1)
        {
            if (x + 1 < SizeX)
            {   //Right
                if (_grid[x + 1, y] == -1)
                    count++;
            }
            if (x - 1 > 0)
            {   //Left
                if (_grid[x - 1, y] == -1)
                    count++;
            }
            if (y + 1 < SizeY)
            {   //Upper
                if (_grid[x, y + 1] == -1)
                    count++;
            }
            if (y - 1 > 0)
            {   //Lower
                if (_grid[x, y - 1] == -1)
                    count++;
            }
            if (x + 1 < SizeX && y + 1 < SizeY)
            {   //Right-Upper
                if (_grid[x + 1, y + 1] == -1)
                    count++;
            }
            if (x + 1 < SizeX && y - 1 > 0)
            {   //Right-Lower
                if (_grid[x + 1, y - 1] == -1)
                    count++;
            }
            if (x - 1 > 0 && y + 1 < SizeY)
            {   //Left-Upper
                if (_grid[x - 1, y + 1] == -1)
                    count++;
            }
            if (x - 1 > 0 && y - 1 > 0)
            {   //Left-Lower
                if (_grid[x - 1, y - 1] == -1)
                    count++;
            }
        }
        return count;
    }

    void OpenEmptySpace(int x, int y)
    {
        for (var k = -1; k <= 1; k++)
        {
            for (var l = -1; l <= 1; l++)
            {
                if (CheckBounds(x + k, y + l) && GameLogicChecker(x + k, y + l) == 0)
                {
                    _buttons[x + k, y + l].Text = "0";
                    OpenEmptySpace(x + k, y + l);
                }
            }
        }
    }

    private bool CheckBounds(int x, int y)
    {
        return x >= 0 && x < SizeX && y >= 0 && y < SizeY;
    }

【问题讨论】:

  • 您是否尝试过单步执行代码以观察其行为和瞬态值?
  • 当 k 为 0 且 l 为 0 且 if 检查为真时,您将使用初始 x 和 y 值再次调用 OpenEmtpySpace,并创建一个无限循环。
  • 确实,OpenEmptySpace 被编码为永远不会结束递归......不太清楚你为什么会期待其他情况。
  • lk 都为零时,除了跳过明显的递归步骤之外,您还应该跟踪已经发现的位置,以防止振荡递归。

标签: c# recursion stack-overflow minesweeper


【解决方案1】:

这是给您的另一个答案,按照更好的编码实践逐个重写您的方法。就像在另一个答案中一样,假定了一个名为 Explored[SizeX, SizeY] 的布尔网格。


1. GameLogicChecker()

private int GameLogicChecker(int x, int y)
{
    if (_grid[x, y] == -1) return 0;
    int count = 0;
    if (x + 1 < SizeX && _grid[x + 1, y] == -1) //Right
    {
        count++;
    }
    if (x - 1 > 0 && _grid[x - 1, y] == -1) //Left
    {
        count++;
    }
    if (y + 1 < SizeY && _grid[x, y + 1] == -1) //Upper
    {
        count++;
    }
    if (y - 1 > 0 && _grid[x, y - 1] == -1) //Lower
    {
        count++;
    }
    if (x + 1 < SizeX && y + 1 < SizeY && _grid[x + 1, y + 1] == -1) //Right-Upper
    {
        count++;
    }
    if (x + 1 < SizeX && y - 1 > 0 && _grid[x + 1, y - 1] == -1) //Right-Lower
    {
        count++;
    }
    if (x - 1 > 0 && y + 1 < SizeY && _grid[x - 1, y + 1] == -1) //Left-Upper
    {
        count++;
    }
    if (x - 1 > 0 && y - 1 > 0 && _grid[x - 1, y - 1] == -1) //Left-Lower
    {
        count++;
    }
    return count;
}

什么更好?从特殊情况的方法返回更快。减少了 If(...) 块中的嵌套。


2。 OpenEmptySpace()

public/private void OpenEmptySpace(int x, int y)
{
    for (var deltaX = -1; deltaX <= 1; deltaX += 2)
    {
        for (var deltaY = -1; deltaY <= 1; deltaY += 2)
        {
            var thisX = x + deltaX;
            var thisY = y + deltaY;
            if (OpeningNotNeeded(thisX, thisY)) 
            {
                continue;
            }
            Explored[thisX, thisY] = true;
            _buttons[thisX, thisY].Text = "0";
            OpenEmptySpace(thisX, thisY);
        }
    }
}

private bool OpeningNotNeeded(int x, int y)
{
    return !CheckBounds(x, y)
           || GameLogicChecker(x, y) != 0 
           || Explored[x, y];
}

什么更好?在两个循环中正确命名的索引变量。正确书写的条件(+= 2 而不是++)。减少了 If(...) 中的嵌套。 If(...) 中的方法调用比三个谓词更容易阅读。添加了有用的临时变量,可以清楚地知道前面编写的代码中的 x + ky + l 是什么。


3。 CheckBounds() 写得很好。

【讨论】:

    【解决方案2】:

    对于 k = 0 和 l = 0,你一遍又一遍地呼唤自己......


    感谢@BenVoigt 指出两个相邻的零也会导致无限递归。因此,为了解决这个问题,一种方法是也创建一个布尔网格,并将特定单元格的值设置为true(如果它已经运行一次)。假设网格名为Explored,我在下面的代码中添加了它的条件。


    如果您坚持使用当前代码,请尝试将条件更改为:

    if (CheckBounds(x + k, y + l) 
        && GameLogicChecker(x + k, y + l) == 0 
        && !(k == 0 && l == 0)
        && !Explored[x + k, y + l])
    {
        Explored[x + k, y + l] = true;
        _buttons[x + k, y + l].Text = "0";
        OpenEmptySpace(x + k, y + l);
    }
    

    【讨论】:

    • @displayName:如果你有两个相邻的零,你仍然会有无限递归。
    • 请注意,CheckBounds(5,5) 将调用CheckBounds(5,6),然后如果两者周围都有零炸弹,它将调用CheckBound(5,5) 等等。
    • @BenVoigt:感谢您的意见,我已经更新了答案并归功于您。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多