无法发现我Java俄罗斯方块游戏中的错误

3

我和我的朋友用Java制作了一个类似俄罗斯方块的游戏,一开始它能正常运行一段时间,但在总方块数达到某个特定值时就会突然出现bug。例如,有时候在放置了42个方块后出现问题,下一次可能是46个,再下一次是44个。

[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][2][2][ ][ ][ ][ ][ ][ ][7]
[ ][2][2][2][3][3][ ][4][ ][7]
[2][2][1][3][3][1][ ][4][ ][7]

上面是一个在bug发生之前的样例。不要过于解读上面的示例,因为bug并不依赖于棋子位置等因素。
以下是bug发生后网格的样子...
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][ ][ ][2][ ][ ][ ][ ][ ][ ]
[ ][2][2][ ][ ][ ][ ][ ][ ][7]
[ ][2][2][2][3][3][ ][4][ ][7]
[2][2][1][3][3][1][ ][4][ ][7]

注意到2一直延伸到网格的顶部了吗?这就是发生的事情。
现在事情变得有趣了。我们已经将其限制为特定的函数和特定的代码行。
grid[x+legend[piece.type].fetch(i, 0, piece.rotate)]
    [y+legend[piece.type].fetch(i, 1, piece.rotate)]
    .type = piece.type+1;

上面的代码行被认为是引起问题的确切代码行。问题在于该行代码没有任何问题。我已经调试了整个程序大约一周,并在每次更改后记录了网格日志。
上面两个网格之间的区别是对该行的一个调用。它不应该可能分配网格中的所有其他单元格。调试日志显示变量为:
i: 0
legend[piece.type].block.length: 4
legend[piece.type].fetch(i, 0, piece.rotate): 0
legend[piece.type].fetch(i, 1, piece.rotate): 0
piece.type: 1
piece.rotate: 3
x: 3
y: 6

那些是影响上述第二个网格的变量。有错的代码行应该是:
grid[3+0][6+0].type = 1+1;

有可能我们发现了Java本身的一个bug吗?我已经花了几个小时来调试这段代码,但我从来没有遇到过找不到的bug(虽然我通常使用Python编程,但是Java对我来说还是相对较新的)。

此外,似乎这个bug受到网格大小的影响。如果我将网格的高度设置为较低的数字,就需要更少的方块才能引起bug。同样地,如果我制作一个更高的网格,则需要更多的方块。

希望Java专家们能够帮助解决这个问题。

附加信息:

p.s. 我在Mac上使用Eclipse编程,使用JRE JVM 1.6。我也尝试了JRE JVM 1.4,但出现了相同的情况。

private Grid grid[][] = new Grid[dimx][dimy];

以上代码是创建网格时的代码。Grid是一个类:

public class Grid {
    int type;

    public Grid(int type) {
    }
}

这是一部分详细的调试日志代码... 这是打印基于文本的网格的函数。
public void printGrid(int line) {
    System.out.print("\n\n\n"+line+":\n");
    for (int y = 0;y < grid[0].length;y++) {
        for (int x = 0;x < grid.length;x++) {
            if (grid[x][y].type == 0) {
                System.out.print("[ ]");
            } else {
                System.out.print("["+grid[x][y].type+"]");
            }

        }
        System.out.println();
    }
    System.out.println("\n\n");
}

变量'line'并不重要,仅告诉我们从哪行调用了函数printGrid()。
如果您需要更多信息,请告诉我。
更多附加信息:
这是该程序的所有文件。

哦,这里还有一段调试日志的摘录,以便为您提供更多信息。

http://pastebin.com/9VKhF8CR

它包括成功放置一个棋子(即在错误发生之前放置的最后一个棋子),然后显示错误发生。函数的所有变量都被打印出来,没有一个变量出现错位。该错误会发生在网格上任何位置放置任何棋子时。每次调用我上面提到的代码行后都会打印出网格。如果需要更多信息,请告诉我。我希望解决这个错误。


6
“我们是否可能发现了Java本身的一个漏洞?” - 不太可能。首先怀疑自己的代码... - Mitch Wheat
有任何消息吗?是NullPointerException或类似的错误吗?在我看来,当您知道产生日志文件的代码时,它非常有用。 - Luc M
我建议你在其他地方寻找问题所在。(“当你排除了不可能的,无论剩下什么,不管多么不可能,一定是真相。”)你能展示一下创建grid和打印它的代码吗? - Ted Hopp
@Ted 我已经在原帖中添加了所需的信息。哦,你引用的“……排除不可能的,剩下的无论多么不可能,就是真相……”唯一的问题是,在出现错误时没有其他内容写入网格。只有在放置新方块和完成一行时(行上面的所有方块向下移动一个)才会写入网格。当放置新方块时出现错误,但只有在已经放置了许多总方块之后才会出现。一旦出现错误,它就会一直发生(随机),直到我关闭并重新打开游戏。 - Bandit
从您发布的示例来看,似乎是在一个列中有四个项目时发生了这种情况。您能否确认一下? - Ted Hopp
@Ted 这个错误不仅限于一列中的四个项目。据我所知,这个错误在这方面是完全随机的。然而,它总是会“射”到网格的顶部,从不发生其他情况。此外,该错误似乎与总块数(大约10块范围内)之间存在相关性。该错误还与网格的大小有关。更大的网格会导致错误在游戏后期发生。较小的网格则相反。出现错误并按“g”(新游戏)将导致下一场比赛早期出现错误。 - Bandit
1个回答

4
从您的问题描述来看,似乎grid[0]grid[1]grid[6]都是引用同一行数组的(但grid[7]及其以后是引用不同数组的)。这就导致了您所描述的症状,即更改单个元素会导致所有这些行中的元素都发生变化。

您可以尝试:

System.err.println(grid[0] == grid[1]);

尝试并查看是否返回truefalse。您可能会得到true


这是一个很棒的假设,即使最后证明不是根本原因,我仍会支持它。 - Amir Afghani
我将上述代码添加到我的程序中,但它记录的是 false。我还想重申这个事实,即这只会在一段时间后发生。这个 bug 不会立即出现,需要一段时间才能出现。我会继续搜索并告诉你们如果我有任何新的发现。 - Bandit
“过了一会儿”这部分对我们来说毫无意义,因为您没有展示整个代码,只有最终结果。我们所能做的就是检查您所描述的内容并提出可能发生的原因。您的数据结构如何变成那样并不是您问题的一部分。但我可以向您保证,在Java中,数据结构不会在您没有实际做任何事情的情况下随机更改。” - Greg Hewgill
@Bandit:我怀疑问题出在这里:grid[x][yRow] = grid[x][yRow-1];。由于grid的每个元素本身都是一个Grid对象,这个赋值语句将使得两个单元格grid[x][yRow]grid[x][yRow-1]指向同一个Grid对象。当你改变那个Grid对象的内容时,两个单元格看起来都会发生变化。由于你的Grid对象只包含一个公共整数字段,我建议将grid的声明和初始化更改为private int grid[][] = new int[dimx][dimy];,这样赋值语义将是你需要的(标量复制而不是引用复制)。 - Greg Hewgill
1
如果您想继续使用Grid类,另一种更简单的解决方案可能是grid[x][yRow].type = grid[x][yRow-1].type; - Greg Hewgill
@Greg 你真是个天才。这下解决了。非常感谢你的帮助……还有其他人的帮助。你们都是最棒的。 - Bandit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接