Implementing "Check" in a Chess Game

sir_thursday picture sir_thursday · Apr 30, 2014 · Viewed 9.6k times · Source

This question is fairly large and difficult to solve without looking through my code, and if it's exceedingly so, then the scope is probably too large and I'll delete the question. Basically I have a working Chess game with all of the rules of Chess except Check (and thus also not Checkmate, stalemate, etc.) implemented.

I went about implementing Check by assigning the Squares of my ChessBoard two booleans: protectedByWhite and protectedByBlack. There are two main pieces of logic with check:

  • If a move is made by White that causes his king to be on a square that is protectedByBlack, and vice versa with Black, the move is "undone".
  • If a move is made by White that causes Black's king to be on a square that is protectedByWhite, and vice versa with Black, Black's next move must place his king on a square that is not protectedByWhite.

Thus the logic is fairly simple. In my ChessBoard class, I have testCheckWhite and testCheckBlack functions that are called after every move. Moves are called in my Square class (a simple mouse click function).

The main problem is that the code is buggy... and I'm not sure why/where. The main bug is that:

  • When black or white are in Check, if they make a move where they would still be in Check, the move is not undone. I know that the undo function is working fine, so some how my logic is wrong.

For example, I have labels on the side that alert when Black/White are in check. When I initially "Check" the opponent, the label notifies me of the check. However, when I try to move the king to a square where I would still be in check, the label incorrectly says that there is no Check. I've been working for some time now trying to identify where I've gone wrong, and I could use some direction.


RELEVANT CODE:

ChessBoard.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;
        }
    }

Square.java

.... // If a square is clicked that IS colored...
        } else {
            for(Square s : ChessBoard.BOARD_SQUARES) {
                if(s.hasPiece() && (s.getPiece()).getFocus()) {

                    // Check to make sure that the target square and current
                    // square are not the same
                    if(!this.equals(s)) {
                        movePiece(s);

                        ChessBoard.setProtectedSquares();

                        // Test for check
                        // 1) Find out what color the moved piece is
                        if((ChessBoard.getTurn()) == TeamColor.WHITE) {
                            if(ChessBoard.testCheckWhite()) {
                                // Undo move
                                s.movePiece(ChessBoard.getSquare(STORED_POSITION));
                                GameInfoPanel.gameStatus.setText("Illegal move, white in check");
                            } else if(ChessBoard.testCheckBlack()) {
                                // Move is okay, black is now in check
                                GameInfoPanel.gameStatus.setText("Okay move, black in check");
                                // Switch players' turn
                                ChessBoard.switchTurn();
                            } else {
                                // Move is okay, nothing happened
                                GameInfoPanel.gameStatus.setText("No one in check");
                                // Switch players' turn
                                ChessBoard.switchTurn();
                            }

                        } else {
                            if(ChessBoard.testCheckBlack()) {
                                // Undo move
                                s.movePiece(ChessBoard.getSquare(STORED_POSITION));
                                GameInfoPanel.gameStatus.setText("Illegal move, black in check");
                            } else if(ChessBoard.testCheckWhite()) {
                                // Move is okay, white is now in check
                                GameInfoPanel.gameStatus.setText("Okay move, white in check");
                                // Switch players' turn
                                ChessBoard.switchTurn();
                            } else {
                                // Move is okay, nothing happened
                                GameInfoPanel.gameStatus.setText("No one in check");
                                // Switch players' turn
                                ChessBoard.switchTurn();
                            }
                        }
                    }
                }
            }

            // Clear all color and focus
            ChessBoard.clearFocus();

            ChessBoard.setProtectedSquares();
        }

Answer

renz picture renz · Apr 30, 2014

I quite understand the algorithm of your code. Unfortunately, I don't see anything wrong with the snippet that you posted.

That's why you should always use Unit Tests while you code. :)

  1. Unit test the setProtectedSquares()
  2. Unit test the testCheckWhite()
  3. Unit test the testcCheckBlack()
  4. Unit test THEN REFACTOR the for(Square s : ChessBoard.BOARD_SQUARES) {...}

These will help you in the long run.

However, if you want to solve (hopefully) things quicker, use the debugger mode from your IDE.