2013-05-29 104 views
8

我正在制作游戏国际象棋,几乎获得了一切,但只有一件事:我需要做到这一点,以便玩家不可能将棋子移动到棋盘上。我在如何解决这个问题上遇到了麻烦。国际象棋:获得所有合法国际象棋棋子

我现在所拥有的在伪码以生成有效的动作是: 类getMoveLocations(I定义的位置是在国际象棋的那些方块的一个): 如果该位置是在边界内,并且片在该位置是并且模拟的移动不会导致棋盘被检查,然后将该位置添加到棋子可以移动到的可能位置。

这个问题是我如何检查一个棋盘是否“检查”。在我的代码中,它认为一个棋盘将通过收集所有敌人的移动位置来进行“检查”,并查看是否有任何敌方移动位置与国王的位置重叠。

不幸的是,这是无限循环开始的地方;为了收集所有敌人的电影位置,每个敌人可能的移动位置需要确保其移动不会导致它被检查。为了确保敌人的位置都没有受到检查,它必须收集所有盟友的潜在移动位置等。等等。

我对如何获得工作算法很着迷。虽然我的代码“理论上”是合乎逻辑的,但它不能实现。我对A)更有效的方式来实现一种方法来检查所有的法律动作,或B)一种方法来解决这个无限循环

+0

你可以运行递归部分只为那些会创建一个检查(以确保它们是有效的)而不是其他(它们无关紧要,如果它们是无效的)的移动。这应该防止无限递归。 – assylias

+0

你做了一个叫getMoveLocations的类吗? – joshreesjones

+1

您正在寻找可能的举动“如果我在这里移动,并且对手在那里移动,那么如果我在对手移到这里之后移动到那里,我会进行检查吗?”?想象一下制作一个游戏,你有一个弹跳在墙上的球,并且球可以被玩家控制,你不会将他的移动限制在N条路径上,这不会导致与墙相撞(巨大的搜索),你会让他向任何一个方向移动UNTILL他与墙壁发生碰撞。同样,只检查移动是否会让你在移动时检查,而不考虑一块棋子的所有可能移动(巨大的搜索) – arynaq

回答

2

修改您的getMoveLocations程序接受一个标志,指示是否担心移动进入检查。例如,如果一件东西被钉住了,它仍然可以移动以捕捉对方国王。如果该标志设置为忽略检查风险,则跳过检查测试将会中断递归。

或者(等同)编写一个单独的方法来生成忽略移入检查问题的移动。使用该程序作为“让棋盘进行检查”测试的一部分。

+0

通过这样做,移动被认为是无效的,但实际上它有效的可能性还不存在吗? – Mike

+0

@ user2430625 - 我不这么认为。如果由于你的移动,对手可以立即拿走你的国王(即使它移动了一个可以对对手进行检查的棋子),那么你不能做出这样的举动。你能否提供一个显示其他情况的例子? –

+0

我不认为这种方法适用于复杂的职位与许多固定件。您可以在固定件之间建立循环关系。 – Zong

1

如果移动允许敌方的棋子能够对你的国王进行攻击移动,那么移动会使你的身体进入检查状态(因此不允许)。

请注意,将您的对手放在支票上是不同的 - 当您放置对手时,他们轮到回应。如果你可以把自己置于检查中,你将有0回应的机会。他们能够捕捉到你的国王,无论他们投入的位置多么糟糕,或者即使他们会“被检查”,他们总是会做出正确的举动 - 他们赢了!那之后什么都没有了。

因此,看看是否会采取行动将自己放在检查,看看是否有任何敌人的一块可以使攻击移动到你的国王。而已。你不是递归地解决未来的检查或类似的事情 - 如果他们现在可以攻击国王,那是无效的。

8

有一个更有效的方法来确定一边是否在检查:你只需从国王向外扫描,看看你是否找到可以攻击它的碎片。例如,从国王的位置,检查是否有任何敌方主教沿着对角线等。您根本不需要生成移动列表,因此递归是不必要的。这里有一些伪代码:

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

+0

他也必须特别处理castling。国王不得越过受到攻击的空间,因此海报还需要检查f1或f8是否为King-cast castling,d1或d8是否为Queen-cast castling。 –

+0

是的,而且还行。完全合法的生成非常复杂。如果在伪法律生成后评估检查,所有这些都可以轻松处理,但速度会慢一些。 – Zong

+0

但是只有在城堡里,海报才能检查2格;目的地和中间空间。 –

0

看来,一些海报不懂棋的发动机足够,所以请尝试争辩前仔细阅读和研究:

而不是检查,看看他们是否能搬到那里,检查它们是否可以攻击那里。您可能会希望以后为您的评估功能反正...

我不知道有多少个引擎这样做,但我知道干鳕鱼确实(见src文件夹中的evaluate.cpp),所以我假设它在GOOD引擎中相当标准。如果无论如何都需要完成评估,那么你最好在运动世代中使用它。

+0

如果你的算法接受这个答案的逻辑,这将是一个比所有敌人的有效动作更简单和可能更便宜的评估。对于国王的移动来说,这可能是一个相当简单的布尔操作:如果这个空间对ChessPiece.KING是有效的,并且不受敌人剩余棋子的攻击,那么这是一个有效的举动。如果ChessPiece.KING受到攻击,并且没有有效的移动,那么游戏就会以失败告终。 – scottb

+0

能够移动到那里并且除了castling,enpassant和pawn advance之外还有能力攻击的区别是什么?这与OP已经做的几乎完全一样。 – Zong

+0

我从来没有写过国际象棋引擎,但我已经做了大量的研究,我认为这是最好的方法。我主要关心的是,OP使用Java作为国际象棋引擎,显然他们不关心性能太多...... –