我想在扫雷游戏中使用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);
}
}
答案 0 :(得分: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
,{ {1}})包含一个我的,然后您的点y+1
将不会从队列中删除,因为p
将导致mineLayout[x][y-1]
被抛出,而ArrayIndexOutOfBoundsException
上的剩余检查将被跳过。
捕获mineLayout
或NullPointerException
等异常是不好的做法。如果抛出其中一个异常,通常是因为您的代码中存在错误。期望抛出这些异常并在抛出它们时捕获它们是邋coding的编码。但是,为了公平对待您,我怀疑您试图避免检查您的八个ArrayIndexOutOfBoundsException
来电中的每一个的坐标值是否在范围内。在这种情况下,有一个方法可以检查给定方块是否包含一个矿井,并且你可以设置逻辑以检查mineLayout
和x
值是否在那里的网格上。例如,此方法可能如下所示:
y
(我假设 private static boolean isMineAt(int x, int y) {
return 0 <= x && x < width && 0 <= y && y < height && mineLayout[x][y].equals("Mine");
}
和width
包含网格的宽度和高度。)
这样您可以使用以下内容替换所有height
支票:
mineLayout
您可能还希望对 if (isMineAt(x, y+1))
queue.remove(p);
else if (isMineAt(x, y-1))
queue.remove(p);
// and so on...
的八次调用执行类似操作。您可以编写一个方法,在将点添加到队列之前检查网格上是否有queue.add(new Point(...));
和x
值。您还可以检查要添加到队列中的点是否已经在队列中,如果不是,则仅添加它。我会把它作为锻炼给你。