Home > chess > Chess05: Enforcing game rules

Chess05: Enforcing game rules

chess icon 72 In this article we will take a closer look at enforcing game rules. Because we seperated the view logic from the game logic in one of the earlier articles, implementing rule enforcement is easier as you might think at this stage.

This is the fifth part in a series of articles about programming a chess game in Java using Swing. As all articles build upon each other, I encourage you to also have a look at the previous articles in this series (Chess01: Dragging game pieces, Chess02: Introducing game state, Chess03: Separating view and logic, Chess04: Implementing an alternative user interface).

There are several ways of implementing rule enforcement. In this example I have chosen to implement the movement validation logic in a single class called MoveValidator. The MoveValidator class covers the basic chess moves for each piece. However, it does not cover all rules, as the focus of the article is on the general concept of enforcing game rules and not on implementing a fully featured chess game. Missing rules are e.g. stalemate, checkmate, en passant, castling, …

To enable move validation we simply add four lines at the beginning of our movePiece() method in the ChessGame class.

public class ChessGame {

       //..
	/**
	 * Move piece to the specified location. If the target location is occupied
	 * by an opponent piece, that piece is marked as 'captured'
	 * @param sourceRow the source row (Piece.ROW_..) of the piece to move
	 * @param sourceColumn the source column (Piece.COLUMN_..) of the piece to move
	 * @param targetRow the target row (Piece.ROW_..)
	 * @param targetColumn the target column (Piece.COLUMN_..)
	 */
	public void movePiece(int sourceRow, int sourceColumn, int targetRow, int targetColumn) {

		if( !this.moveValidator.isMoveValid(sourceRow, sourceColumn, targetRow, targetColumn)){
			System.out.println("move invalid");
			return;
		}

		Piece piece = getNonCapturedPieceAtLocation(sourceRow, sourceColumn);

		//check if the move is capturing an opponent piece
		int opponentColor = (piece.getColor()==Piece.COLOR_BLACK?Piece.COLOR_WHITE:Piece.COLOR_BLACK);
		if( isNonCapturedPieceAtLocation(opponentColor, targetRow, targetColumn)){
			Piece opponentPiece = getNonCapturedPieceAtLocation( targetRow, targetColumn);
			opponentPiece.isCaptured(true);
		}

		piece.setRow(targetRow);
		piece.setColumn(targetColumn);

		this.changeGameState();
	}
       //..

}

The isValidMove() method of the MoveValidator class will first check if the a piece exists at the source location. If so, it checks if the color is matching the current game state. Then it validates if the target location is valid. Afterwards the movement is further analyzed by calling a validation method that corresponds to the moving piece type. If there is still no problem with the move, we should check for resulting stalemate and checkmate situations. However, this has been left out for this article.

public class MoveValidator {
       //..
	/**
	 * Checks if the specified move is valid
	 * @param sourceRow
	 * @param sourceColumn
	 * @param targetRow
	 * @param targetColumn
	 * @return true if move is valid, false if move is invalid
	 */
	public boolean isMoveValid(int sourceRow,
			int sourceColumn, int targetRow, int targetColumn) {

		sourcePiece = chessGame.getNonCapturedPieceAtLocation(sourceRow, sourceColumn);
		targetPiece = this.chessGame.getNonCapturedPieceAtLocation(targetRow, targetColumn);

		// source piece does not exist
		if( sourcePiece == null ){
			System.out.println("no source piece");
			return false;
		}

		// source piece has right color?
		if( sourcePiece.getColor() == Piece.COLOR_WHITE
				&& this.chessGame.getGameState() == ChessGame.GAME_STATE_WHITE){
			// ok
		}else if( sourcePiece.getColor() == Piece.COLOR_BLACK
				&& this.chessGame.getGameState() == ChessGame.GAME_STATE_BLACK){
			// ok
		}else{
			System.out.println("it's not your turn");
			return false;
		}

		// check if target location within boundaries
		if( targetRow < Piece.ROW_1 || targetRow > Piece.ROW_8
				|| targetColumn < Piece.COLUMN_A || targetColumn > Piece.COLUMN_H){
			System.out.println("target row or column out of scope");
			return false;
		}

		// validate piece movement rules
		boolean validPieceMove = false;
		switch (sourcePiece.getType()) {
			case Piece.TYPE_BISHOP:
				validPieceMove = isValidBishopMove(sourceRow,sourceColumn,targetRow,targetColumn);break;
			case Piece.TYPE_KING:
				validPieceMove = isValidKingMove(sourceRow,sourceColumn,targetRow,targetColumn);break;
			case Piece.TYPE_KNIGHT:
				validPieceMove = isValidKnightMove(sourceRow,sourceColumn,targetRow,targetColumn);break;
			case Piece.TYPE_PAWN:
				validPieceMove = isValidPawnMove(sourceRow,sourceColumn,targetRow,targetColumn);break;
			case Piece.TYPE_QUEEN:
				validPieceMove = isValidQueenMove(sourceRow,sourceColumn,targetRow,targetColumn);break;
			case Piece.TYPE_ROOK:
				validPieceMove = isValidRookMove(sourceRow,sourceColumn,targetRow,targetColumn);break;
			default: break;
		}
		if( !validPieceMove){
			return false;
		}else{
			// ok
		}

		// handle stalemate and checkmate
		// ..

		return true;
	}
       //..
}

Here is an example of the validation method for the piece type ‘Knight’.

public class MoveValidator {
       //..
	private boolean isValidKnightMove(int sourceRow, int sourceColumn, int targetRow, int targetColumn) {
		// The knight moves to any of the closest squares which are not on the same rank,
		// file or diagonal, thus the move forms an "L"-shape two squares long and one
		// square wide. The knight is the only piece which can leap over other pieces.

		// target location possible?
		if( isTargetLocationFree() || isTargetLocationCaptureable()){
			//ok
		}else{
			System.out.println("target location not free and not captureable");
			return false;
		}

		if( sourceRow+2 == targetRow && sourceColumn+1 == targetColumn){
			// move up up right
			return true;
		}else if( sourceRow+1 == targetRow && sourceColumn+2 == targetColumn){
			// move up right right
			return true;
		}else if( sourceRow-1 == targetRow && sourceColumn+2 == targetColumn){
			// move down right right
			return true;
		}else if( sourceRow-2 == targetRow && sourceColumn+1 == targetColumn){
			// move down down right
			return true;
		}else if( sourceRow-2 == targetRow && sourceColumn-1 == targetColumn){
			// move down down left
			return true;
		}else if( sourceRow-1 == targetRow && sourceColumn-2 == targetColumn){
			// move down left left
			return true;
		}else if( sourceRow+1 == targetRow && sourceColumn-2 == targetColumn){
			// move up left left
			return true;
		}else if( sourceRow+2 == targetRow && sourceColumn-1 == targetColumn){
			// move up up left
			return true;
		}else{
			return false;
		}
	}
       //..
}

The method chessGame.changeGameState() has been extended as well. It now checks if the end condition of the game has been reached.

public class ChessGame {
       //..
	/**
	 * switches the game state depending on the current board situation.
	 */
	public void changeGameState() {

		// check if game end condition has been reached
		//
		if (this.isGameEndConditionReached()) {

			if (this.gameState == ChessGame.GAME_STATE_BLACK) {
				System.out.println("Game over! Black won!");
			} else {
				System.out.println("Game over! White won!");
			}

			this.gameState = ChessGame.GAME_STATE_END;
			return;
		}

		switch (this.gameState) {
			case GAME_STATE_BLACK:
				this.gameState = GAME_STATE_WHITE;
				break;
			case GAME_STATE_WHITE:
				this.gameState = GAME_STATE_BLACK;
				break;
			case GAME_STATE_END:
				// don't change anymore
				break;
			default:
				throw new IllegalStateException("unknown game state:" + this.gameState);
		}
	}

	/**
	 * check if the games end condition is met: One color has a captured king
	 *
	 * @return true if the game end condition is met
	 */
	private boolean isGameEndConditionReached() {
		for (Piece piece : this.pieces) {
			if (piece.getType() == Piece.TYPE_KING && piece.isCaptured()) {
				return true;
			} else {
				// continue iterating
			}
		}

		return false;
	}
       //..
}

In addition to the changes above some changes were done to the user interface to handle the new game end condition.
In the next article we will have a look at how to highlight all valid target locations for a game piece.

Resources:

The source code (eclipse project): source code (updated 22.02.2016)
The chess icons come from: http://ixian.com/chess/jin-piece-sets/

  1. Elle
    June 9, 2014 at 6:15 pm

    Hi,this i a greart tutorial!Nice work!
    I only have one question…if you may help…how can i change the method IsValidpPawnMove so the pawns will move 2 squares when it’s their first move?I know I need a boolean variable like the isValid but I just can’t make the code work right…

    • proghammer
      June 9, 2014 at 9:07 pm

      @Elle: The fastest way is probably to change the method signature from:

      isValidPawnMove(sourceRow,sourceColumn,targetRow,targetColumn);

      to

      isValidPawnMove(sourcePiece, sourceRow,sourceColumn,targetRow,targetColumn);

      so that the logic can access the sourcePiece.
      Then you can add a boolean flag to the piece, which indicates if the piece has already been moved or not. It the piece has not been moved and it is a Pawn, then it is allowed to be moved two spaces instead of one (if both are vacant). And dont’t forget to update the flag after the piece has been moved.

      I hope this helps.

  2. November 18, 2014 at 8:19 pm

    Hi! I really loved your tutorial, I spent hours reading it and understandint it, you did such a nice work and you really helped me out!
    I only have two question and I hope you can help me, if I understood everything you implement the method gameConditionReached as some kind of checkmate, but I didn’t find if you implement some method for the check condition, I was thinking that maybe I can write it under the isValidKingMove, but I don’t know how I do it.
    Also, I was checking if you can help me with the pawn promotion move.
    Hope you answer, and thanks again!

    • proghammer
      November 29, 2014 at 9:48 pm

      @ingskyli: Thank you for your kind words! At the moment there is no check for “in check”. There are various ways to implement it. You could e.g. add some logic at the end of the MoveValidator.isMoveValid() method to check that the own king is not “in check”. Just changing “isValidKingMove” is probably not enough. For checking if the own king is not in chess, you can e.g. iterate all opponent pieces and check if one of them can capture the king, or you can check manually from the king position if there are any pieces in range, that could capture him.
      Pawn promotion is a bit tricky, as you have to ask the player to which piece he would like to promote the pawn. This information needs to be forwarded to the game logic afterwards. When implementing an AI this becomes even trickier, as the AI needs to generate all promotion moves and the move object needs to be extended to also keep track of promotion moves. But that will become more clear when you reach some later articles.
      I hope this helps a little bit.

  3. Haroon
    February 25, 2015 at 6:35 pm

    One easy way (and that worked for me)to make your pawn move 2 squares at first place is to introduce a new variable in piece class

    public boolean firstMove = true;

    then in the function isValidPawnMove() write these lines after the first check for same coloumn position
    if(sourceRow 2 == targetRow && sourcePiece.firstMove) {
    isValidMove = true;
    }
    else if(sourceRow 1 ==targetRow){
    isValidMove == true}

    do the same for black piece, and then in the chess game class, in the movePiece() function add these lines before the “return” statement

    if(piece.getType()==Piece.TYPE_PAWN){
    piece.firstMove = false;
    }

    well, i have no claim for its efficiency, but it sorted out the problems for me.

    • JULIO DAVID RESENDIZ CRUZ
      November 25, 2019 at 1:12 am

      Did it really help you?

  4. Haroon
    February 26, 2015 at 12:38 pm

    There is a plus sign in the line if(sourceRow +2 ==targetRow)

    as i typed it from my mobile phone, for some reason it didn’t show in the final display. Sorry for that

  5. Dan S
    December 9, 2018 at 1:14 am

    Hi! I really love your tutorial and it helped me a lot to learn more Java, keep it up!
    I only have a problem: NullPointerException with the images, with the background one. How could I fix this?
    I also have a NullPointerException when declaring:

    public static void main(String[ ] args){
    new ChessGUI;
    }

    I don’t get why.

    And other one when instancing:
    URL Img = getClass…
    this.Img = new ImageIcon(Img).getImage();

    Thank you very much again for this tutorial!

  1. September 29, 2010 at 1:10 pm
  2. March 10, 2011 at 10:23 am

Leave a comment