有一个更有效的方法来确定一边是否在检查:你只需从国王向外扫描,看看你是否找到可以攻击它的碎片。例如,从国王的位置,检查是否有任何敌方主教沿着对角线等。您根本不需要生成移动列表,因此递归是不必要的。这里有一些伪代码:
function leftInCheck(board, sideToCheck) {
// one of the four rays for bishop/queen attacks
d := 0
while (king rank + d, king file + d) is on the board
piece := board[king rank + d][king file + d]
if piece is an enemy bishop or queen
return true
if piece is not an empty square // a piece blocks any potential
break // attack behind it so we can stop
d := d + 1
// do this for all the other forms of attack
...
return false
}
正如你可以看到有一些代码重复,但你可以缩短它。我保持原样,所以很容易理解。你可以通过产生伪法律动作来产生法律动作,就像你现在正在做的那样,制作每一个动作,并省略那些让你检查上述子程序的动作。这具有自然而然的附加优点。下面是一些伪代码:
function legalMoves(board, sideToMove) {
moveList := empty
for each move in pseudoLegalMoves()
make(move)
if not leftInCheck(board, sideToMove)
moveList.add(move)
unmake(move) // you may not need this
return moveList
}
对于易位,你仍然要检查是否有对国王和车之间的方块攻击。幸运的是,这很容易,因为您可以将上述子例程扩展到除王之外的其他方块。
我假设你没有使用位板或0x88,而是用一个简单的数组表示。这使得实施合法移动生成(没有中间伪法律移动)有点困难,因为它需要非常快地生成攻击地图来确定固定的棋子。如果你雄心勃勃,这是一种可能性。
作为补充说明,我对这里的其他答案有些失望。我甚至不会向任何想要写出好动作生成器的人推荐自己的答案(它只针对那些不熟悉国际象棋程序设计的人)。这是一个经过彻底检查并具有众所周知的解决方案的主题,但却引发了独创性的想法。当然,这没有什么不对,但为什么重新发明车轮,更糟?看看well established methods of move generation。
你可以运行递归部分只为那些会创建一个检查(以确保它们是有效的)而不是其他(它们无关紧要,如果它们是无效的)的移动。这应该防止无限递归。 – assylias
你做了一个叫getMoveLocations的类吗? – joshreesjones
您正在寻找可能的举动“如果我在这里移动,并且对手在那里移动,那么如果我在对手移到这里之后移动到那里,我会进行检查吗?”?想象一下制作一个游戏,你有一个弹跳在墙上的球,并且球可以被玩家控制,你不会将他的移动限制在N条路径上,这不会导致与墙相撞(巨大的搜索),你会让他向任何一个方向移动UNTILL他与墙壁发生碰撞。同样,只检查移动是否会让你在移动时检查,而不考虑一块棋子的所有可能移动(巨大的搜索) – arynaq