0

我想使用 Floodfill 来发现扫雷游戏中的相邻单元格。我是 Floodfill 的新手,也许我误解了它。当它到达一个没有被地雷包围的牢房时,它永远不会停止。

这是揭开方法的代码:

public static void uncoverSurroundings(int x, int y, JButton[][] buttons)
{
    queue.add(new Point(x,y));
    int currentPnt = queue.size() - 1;
    Point p = queue.get(currentPnt);
    try
    {
        if (mineLayout[x][y+1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x][y-1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x+1][y+1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x-1][y-1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x-1][y].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x+1][y].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x-1][y+1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x+1][y-1].equals("Mine"))
            queue.remove(p);        
    }
    catch (NullPointerException|ArrayIndexOutOfBoundsException e)
    {
    }
    try
    {
        if (currentPnt + 1 == queue.size())
        {
        Point r = queue.get(currentPnt);
        queue.remove(currentPnt);
        buttons[r.x][r.y].setEnabled(false);
        queue.add(new Point (x, y+1));
        queue.add(new Point (x, y-1));
        queue.add(new Point (x+1, y+1));
        queue.add(new Point (x-1, y-1));
        queue.add(new Point (x-1, y));
        queue.add(new Point (x+1, y));
        queue.add(new Point (x-1, y+1));
        queue.add(new Point (x+1, y-1));
        }
    }
    catch (NullPointerException|ArrayIndexOutOfBoundsException e)
    {
    }
    if (!queue.isEmpty())
        index = queue.size() - 1;
        Point nextPnt = queue.get(index);
        uncoverSurroundings(nextPnt.x, nextPnt.y, buttons);
    }
}
4

1 回答 1

0

我可以看到您的代码存在许多问题。

首先,在代码的底部,您获取队列中的最后一个点,并使用队列中的最后一个点递归调用您的uncoverSurroundings方法。这会将点添加到队列中。如果该点随后使用您的一个调用从队列中删除queue.remove(p),那么您将到达底部,uncoverSurroundings而无需更改队列的大小。然后,您uncoverSurroundings使用相同的点和相同大小的队列uncoverSurroundings再次调用,最终使用相同的点和相同大小的队列再次调用,最终使用相同的点和相同大小的队列再次调用uncoverSurroundings......

您需要在结束时从队列中删除该点uncoverSurroundings

您遇到的第二个问题是您似乎无法记录一个正方形是否已被发现。显然,如果你发现了一个正方形,那么再次尝试发现它是没有意义的。您需要跟踪已发现的方格,如果在已发现的方格uncoverSurroundings上调用,则不应执行任何操作。

解决这两个问题后,您的代码将不再导致堆栈溢出,但它仍然不会按照您的意愿行事。实际上,您的uncoverSurroundings方法可以在包含地雷的正方形上调用自身。它这样做的原因是由于以下代码块:

    try
    {
        if (mineLayout[x][y+1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x][y-1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x+1][y+1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x-1][y-1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x-1][y].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x+1][y].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x-1][y+1].equals("Mine"))
            queue.remove(p);
        else if (mineLayout[x+1][y-1].equals("Mine"))
            queue.remove(p);        
    }
    catch (NullPointerException|ArrayIndexOutOfBoundsException e)
    {
    }

例如,如果方格 ( x, y-1) 不在网格中(y例如,由于为零),但方格 ( x-1, y+1) 包含一个地雷,那么您的点将p不会从队列中删除,因为这mineLayout[x][y-1]会导致ArrayIndexOutOfBoundsException抛出一个mineLayout将跳过剩余的检查。

捕获诸如NullPointerExceptionor之类的异常是不好的做法ArrayIndexOutOfBoundsException。如果抛出这些异常之一,那通常是因为您的代码中有错误。期望抛出此类异常并在抛出它们时捕获它们是草率的编码。但是,为了公平起见,我怀疑您试图避免检查您的坐标值是否在您的八个mineLayout调用中的每个调用的范围内。在这种情况下,最好有一种方法来检查给定的方格是否包含地雷,并且您可以将检查xy值是否在网格上的逻辑放在那里。例如,此方法可能如下所示:

    private static boolean isMineAt(int x, int y) {
        return 0 <= x && x < width && 0 <= y && y < height && mineLayout[x][y].equals("Mine");
    }

(我在这里假设widthheight包含网格的宽度和高度。)

这样,您可以用mineLayout以下内容替换所有支票:

        if (isMineAt(x, y+1))
            queue.remove(p);
        else if (isMineAt(x, y-1))
            queue.remove(p);
        // and so on...

您可能还想对 8 次调用做类似的事情queue.add(new Point(...));。您可以编写一个方法,在将点添加到队列之前检查x和值是否在网格上。y您还可以检查您希望添加到队列中的点是否已经在队列中,如果不是,则仅添加它。我会把它留给你作为练习。

于 2013-09-07T13:33:17.477 回答