我一直在努力实现Java中的奥赛罗游戏,我将用于研究目的,我需要快速实现尽可能多的游戏,所以这就是我所做的:
Board [] []实施
我的第一种方法是使用board [] []多维数组来表示电路板,只是为了使它工作,并确保它正常工作。我说完了,我很高兴。但是,正如大多数人所知,这不是很快,因为我每秒只能玩1,500场比赛(随机动作,只是为了测试)。
棋盘
然后,我将该板实现为BitBoard,它大大加快了移动计算,并且与之前的实现相比速度极快。通过这种实现,它可以每秒播放高达20k的游戏。这是我用于移动计算的代码,它非常好用,但重复:
private void calculateMoves() {
legal = 0L;
long potentialMoves;
long currentBoard = getCurrentBoard();
long opponentBoard = getOpponentBoard();
long emptyBoard = emptyBoard();
// UP
potentialMoves = (currentBoard >> SIZE) & DOWN_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves >> SIZE) & DOWN_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// DOWN
potentialMoves = (currentBoard << SIZE) & UP_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves << SIZE) & UP_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// LEFT
potentialMoves = (currentBoard >> 1L) & RIGHT_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves >> 1L) & RIGHT_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// RIGHT
potentialMoves = (currentBoard << 1L) & LEFT_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves << 1L) & LEFT_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// UP LEFT
potentialMoves = (currentBoard >> (SIZE + 1L)) & RIGHT_MASK & DOWN_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves >> (SIZE + 1L)) & RIGHT_MASK & DOWN_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// UP RIGHT
potentialMoves = (currentBoard >> (SIZE - 1L)) & LEFT_MASK & DOWN_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves >> (SIZE - 1L)) & LEFT_MASK & DOWN_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// DOWN LEFT
potentialMoves = (currentBoard << (SIZE - 1L)) & RIGHT_MASK & UP_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves << (SIZE - 1L)) & RIGHT_MASK & UP_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
// DOWN RIGHT
potentialMoves = (currentBoard << (SIZE + 1L)) & LEFT_MASK & UP_MASK & opponentBoard;
while (potentialMoves != 0L) {
long tmp = (potentialMoves << (SIZE + 1L)) & LEFT_MASK & UP_MASK;
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
moves.clear();
}
类
然后我尝试用这种方式清理代码:
private MoveFinder[] finders = new MoveFinder[] {new UpFinder(), new DownFinder(), new LeftFinder(),
new RightFinder(), new UpLeftFinder(), new UpRightFinder(), new DownLeftFinder(), new DownRightFinder()};
private void calculateMoves() {
legal = 0L;
long potentialMoves;
long currentBoard = getCurrentBoard();
long opponentBoard = getOpponentBoard();
long emptyBoard = emptyBoard();
for (MoveFinder finder : finders) {
potentialMoves = finder.next(currentBoard) & opponentBoard;
while (potentialMoves != 0L) {
long tmp = finder.next(potentialMoves);
legal |= tmp & emptyBoard;
potentialMoves = tmp & opponentBoard;
}
}
moves.clear();
}
private interface MoveFinder {
long next(long next);
}
private class UpFinder implements MoveFinder {
@Override
public long next(long n) {
return (n >> SIZE) & DOWN_MASK;
}
}
private class DownFinder implements MoveFinder {
@Override
public long next(long n) {
return (n << SIZE) & UP_MASK;
}
}
// and so on for the rest of the moves (LeftFinder, RightFinder, etc).
出于某种原因,通过这种方法,我每秒只能运行15k游戏!为什么这段代码要慢得多? Java方法调用是否昂贵?是因为是从数组调用和对象吗?是因为我为每种方法传递了长n的副本吗?
任何想法都会很棒,因为我并不擅长优化代码。
谢谢!
答案 0 :(得分:2)
Java字节码与本机asm代码完全不同。当您使用接口和多态时,它的执行方式与在代码中的执行方式相同。方法调用也是如此。
在java字节码生成中没有进行优化,这就是它变慢的原因。函数调用比重复的内联代码慢,而后期绑定比静态绑定慢。
然而,我比第一个代码更喜欢你的第二个代码,我相信它应该保持这样。
答案 1 :(得分:1)
使用该数组不应该增加太多开销。
我认为方法调用可能会增加最多的开销,特别是如果它们没有得到优化的话。
您可以尝试类似原始代码的方法,并使用方法调用而不包含它们的类,并查看性能是否更好;我似乎记得Java优化getter和setter,所以将类调用的方法放在一个类中可能会阻止它进行类似的优化。