在国际象棋游戏中实施“检查”

时间:2014-04-30 06:04:17

标签: java chess

这个问题相当大,很难在不查看我的代码的情况下解决,如果它非常严重,那么范围可能太大了,我将删除这个问题。基本上我有一个工作的国际象棋游戏,其中包括国际象棋的所有规则,除了 检查 (和 因此也不是Checkmate,僵局等。)已实施。

我通过将ChessBoard的Squares分配给两个布尔值来实现Check:protectedByWhiteprotectedByBlack。检查有两个主要的逻辑:

  • 如果怀特做出让他的国王在广场上的举动 那是protectedByBlack,反之亦然,黑色,此举是 “撤消”。
  • 如果白方采取行动导致布莱克的国王位于protectedByWhite的方格上,反之亦然黑方,黑方的下一步必须将他的国王放在一个不是{{1}的方格上}。

因此逻辑非常简单。在我的protectedByWhite课程中,我有ChessBoardtestCheckWhite个函数,每次移动后都会调用它们。在我的testCheckBlack类(简单的鼠标单击功能)中调用移动。

主要问题是代码有问题...而且我不确定为什么/在哪里。主要的错误是:

  • 当Check中有黑色或白色时,如果他们在仍然处于Check状态的位置移动,则移动不会被撤消。我知道撤销功能工作正常,所以我的逻辑是错误的。

例如,当黑/白被检查时,我的标签侧面会发出警报。当我最初“检查”对手时,标签会通知我支票。然而,当我试图将国王移到一个我还在检查的广场时,标签错误地说没有检查。我已经工作了一段时间,试图找出我出错的地方,我可以用一些方向。


相关代码:

ChessBoard.Java

Square

Square.java

public static void setProtectedSquares() {
        // Reset
        for(Square s : BOARD_SQUARES) {
            s.protectedByWhite = false;
            s.protectedByBlack = false;
        }

        // Now set protections
        for(Square s : BOARD_SQUARES) {
            if(s.hasPiece() && s.getPiece().getTeamColor().equals(TeamColor.WHITE)) {
                Piece p = s.getPiece();
                for(int[] position : p.getLegalMoves(p.getPosition())) {
                    if(hasSquare(position)) {
                        getSquare(position).protectedByWhite = true;
                    }
                }
            }
        }
        for(Square s : BOARD_SQUARES) {
            if(s.hasPiece() && s.getPiece().getTeamColor().equals(TeamColor.BLACK)) {
                Piece p = s.getPiece();
                for(int[] position : p.getLegalMoves(p.getPosition())) {
                    if(hasSquare(position)) {
                        getSquare(position).protectedByBlack = true;
                    }
                }
            }
        }
}

public static boolean testCheckWhite() {

        // Get king position
        int[] whiteKingPosition = new int[]{};
        for(Square s : BOARD_SQUARES) {
            Piece p = s.getPiece();
            if(s.hasPiece() && (p.getPieceType()).equals(PieceType.KING)) {
                if((p.getTeamColor()).equals(TeamColor.WHITE)) {
                    whiteKingPosition = p.getPosition();
                }
            }
        }

        if(hasSquare(whiteKingPosition) && getSquare(whiteKingPosition).protectedByBlack) {
            GameInfoPanel.inCheckWhite.setText("White is in check");
            return true;
        } else {
            GameInfoPanel.inCheckWhite.setText("White is not in check");
            return false;
        }
    }

    public static boolean testCheckBlack() {

        // Get king position
        int[] blackKingPosition = new int[]{};
        for(Square s : BOARD_SQUARES) {
            Piece p = s.getPiece();
            if(s.hasPiece() && (p.getPieceType()).equals(PieceType.KING)) {
                if((p.getTeamColor()).equals(TeamColor.BLACK)) {
                    blackKingPosition = p.getPosition();
                }
            }
        }

        if(hasSquare(blackKingPosition) && getSquare(blackKingPosition).protectedByWhite) {
            GameInfoPanel.inCheckBlack.setText("Black is in check");
            return true;
        } else {
            GameInfoPanel.inCheckBlack.setText("Black is not in check");
            return false;
        }
    }

3 个答案:

答案 0 :(得分:3)

我完全理解你的代码算法。不幸的是,我发现您发布的代码段没有任何问题。

这就是为什么在编码时应始终使用单元测试的原因。 :)

  1. 单元测试setProtectedSquares()
  2. 单元测试testCheckWhite()
  3. 单元测试testcCheckBlack()
  4. 单位测试那么反​​映者for(Square s : ChessBoard.BOARD_SQUARES) {...}
  5. 从长远来看,这些将对您有所帮助。

    但是,如果您想更快地解决(希望)事情,请使用IDE中的调试器模式。

答案 1 :(得分:2)

这是一条建议的调查线。让我们假设这是一个场景:白色已检查黑色和黑色国王可以移动到另一个受白色保护的方格。

根据提供的代码以及黑色(在检查时)可以移动到受白色保护的方块这一事实,必须是ChessBoard.testCheckBlack()方法返回false的情况。现在基于实现ChessBoard.testCheckBlack()方法,我们可以得出结论文本

  

"黑色正在检查"

必须在GameInfoPanel上输出。

如果这是正确的,则需要逐一检查以下内容:

hasSquare(blackKingPosition)
getSquare(blackKingPosition).protectedByWhite // just protectedByWhite 

如果您可以为上述方案发布GameInfoPanel消息,那就太棒了。

答案 2 :(得分:2)

检查任何方格是否受到威胁是一项有用的功能,您不仅可以使用直接的King攻击,还可以使用castling,因为不允许King进行“直通”检查。 这意味着你必须确保最多三个方格可以从对手的直接攻击线中自由地合法地击败国王。

考虑到这一点,你可能想要构建一个这样的方法:

public static final boolean isThreatenedSquare(
          Color threatenedColor, 
          Square threatenedSquare, 
          Chessboard board)

滑动片的算法可以使用平行阵列来定义从原点(受威胁的方块)辐射出的8个方向(攻击线或“射线”)。例如:

int[] rowDirections = {-1, -1, -1, 0, 0, 1, 1, 1};
int[] colDirections = {-1, 0, 1, -1, 1, -1, 0, 1};
  • 第一个索引集( - 1,-1)表示 diagoanl “ray”在“西北”方向移动
  • 第二个索引集( - 1,0)表示在方向移动的垂直“ray”
  • 第三个索引集( - 1,1)表示在<东北方向移动的对角线“ray”
  • 第四个索引集(0,-1)表示以“西”方向移动的水平“ray”

......等等。通过一次放出一个方格,您只需检查方块(确保您在棋盘边界内)并查看它是否被占用。如果是,请确定它是友方还是对手。仅仅因为我们击中了对手的棋子并不一定意味着我们受到威胁的广场正受到攻击。

例如,主教只能沿着对角线射线攻击,所以沿着垂直射线找到一条射线会阻止我们的射线进一步向外辐射,但是主教不会威胁到这个方块。 我们可以巧妙地描述任何滑动件相对于我们之前定义的平行定向阵列的攻击能力。例如:

boolean bishopThreats[] = {true, false, true, false, false, true, false, true};
boolean rookThreats[] = {false, true, false, true, true, false, true, false};
boolean queenThreats[] = {true, true, true, true, true, true, true, true};
boolean kingThreats[] = {true, true, true, true, true, true, true, true};

上面的阵列显示主教只能沿着对角线威胁,沿着垂直线和水平线的鲁克斯,皇后和国王可以向任何方向进攻。

Pawns有点棘手,因为它们沿对角线攻击,但仅在东北+西北方向(白色),东南+西南方向(黑色)攻击。

boolean kill = threatenedColor.equals(Color.black) ? true : false;
boolean pawnThreats[] = {kill, false, kill, false, false, !kill, false, !kill};

一切就绪后,所需要的只是使用几个嵌套的for循环。外面的一个迭代所有方向,内部一个方向一次辐射出一个方格,直到我们击中棋盘的边缘或击中一块,以先到者为准。这是滑动件的算法。骑士与滑动件不同,但这里提出的一般想法也适用。

boolean threatDetected = false;
int threatenedRow = threatenedSquare.getRow();
int threatenedCol = threatenedSquare.getCol();

for(int direction = 0; direction < 8 && !threatDetected; direction++) {
    // RESET OUR COORDINATES TO PROCESS CURRENT LINE OF ATTACK. 
    // INCREMENT VALUES ARE SAME AS DIRECTION ARRAY VALUES
    int row = threatenedRow;
    int col = threatenedCol;
    int rowIncrement = rowDirections[direction];
    int colIncrement = colDirections[direction];

    // RADIATE OUTWARDS STARTING FROM ORIGIN UNTIL WE HIT A PIECE OR ARE OUT OF BOUNDS
    for(int step = 0; step < 8; step++) {
        row = row + rowIncrement;
        col = col + colIncrement;

        // IF WE ARE OUT OF BOUNDS, WE STOP RADIATING OUTWARDS FOR 
        // THIS RAY AND TRY THE NEXT ONE
        if(row < 0 || row > 7 || col < 0 || col > 7) {
            break;
        }
        else {
            // LOOK AT CURRENT SQUARE AND SEE IF IT IS OCCUPIED BY A PIECE
            Square square = board.getSquare(row, col);
            IPiece piece = square.getPiece();
            if(piece != null) {
                // RADIATING OUTWARDS MUST STOP SINCE WE HIT A PIECE, ONLY
                // QUESTION IS WHAT DID WE HIT? FRIEND OR FOE?  
                if(!piece.getColor.equals(threatenedColor)) {
                    // WE ARE FACING AN OPPONENT, DOES IT HAVE THE CAPABILITY
                    // TO ATTACK US GIVEN THE DIRECTIONAL LINE OF ATTACK  
                    // WE ARE CURRENTLY ANALYZING     
                    if(piece instanceof Bishop && bishopThreats[direction]) 
                        threatDetected = true;
                    else if(piece instanceof Rook && rookThreats[direction]) 
                        threatDetected = true;
                    else if(piece instanceof Queen && queenThreats[direction]) 
                        threatDetected = true;
                     else {
                        if(step == 0) {  
                            // PAWNS AND KINGS DONT HAVE THE REACH OF OTHER SLIDING
                            // PIECES; THEY CAN ONLY ATTACK SQUARES THAT ARE CLOSEST
                            // TO ORIGIN  
                            if(piece instanceof Pawn && pawnThreats[direction]) 
                                threatDetected = true;
                            if(piece instanceof King && kingThreats[direction]) 
                                threatDetected = true;
                        }  
                    }
                }
                break;
            }
        }
    }
}