Chess - Java Swing Game

Complete chess game engine with a rules-based AI. Swing-based UI. Won school’s computer science course and department awards.

demo

When you launch Chess, you're greeted with a (totally artificial) loading screen. Creating a new game presents you with plenty of options (including having two CPUs play each other). During gameplay, valid moves are highlighted, and a record of all moves is recorded in modified chess notation. At any point during gameplay, you can save the game state, and loading the file replays the moves. You can also modify the settings at any time.

Chess is written in Java and Swing, so all you need to run it yourself is a computer with Java installed. First download the JAR using the button above, then run

java -jar DennisKrasnovChess.jar
in a terminal.

oop design

This project has some sentimental value to me, as it's my first non-trivial project. I would have written Chess differently today by preferring functional pattens and relying less on null checks and singletons. However, I'm still proud of the elegant object oriented design.

For better readability, I omitted the obnoxious comments (they made us write in grade 10) and irrelevant imports, fields, and methods.

Figure is an abstract class describing a chess piece.

public abstract class Figure {
    // The colour of the figure in boolean
    private boolean whiteFigure;

    // The letter for each figure
    private String code;

    // Is the figure required to not lose (king)
    private boolean isImportant;

    // The score for the figure, used to find which turns are best
    private int score;

    public Figure(boolean whiteFigure, String code, boolean isImportant, int score) {
        this.whiteFigure = whiteFigure;
        this.code = code;
        this.isImportant = isImportant;
        this.score = score;
    }

    /**
     * Different for each figure, will return a list of moves that include
     * everything (including invalids, etc.)
     */
    protected abstract List<Move> getAllPossibilities();
}

Implementing a Figure is very simple, mainly involving specifying how a piece can move on a board using a declarative DSL (domain specific language).

public class Knight extends Figure {
   public Knight( boolean whiteFigure ) {
      super( whiteFigure, "N", false, 2 );
   }

   @Override
   protected List<Move> getAllPossibilities() {
      Board board = Board.getBoard();
      Square currentSquare = board.whereAmI( this );

      List<Move> possibilities = new ArrayList<Move>();
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, 1, 2 ) ); // Up-Up-Right
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, -1, 2 ) ); // Up-Up-Left
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, 2, 1 ) ); // Up-Right-Right
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, -2, 1 ) ); // Up-Left-Left
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, 1, -2 ) ); // Down-Down-Right
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, -1, -2 ) ); // Down-Down-Left
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, 2, -1 ) ); // Down-Right-Right
      addIfOnBoard( possibilities, board.getNextSquare( currentSquare, -2, -1 ) ); // Down-Left-Left
      return possibilities;
   }
}

For pieces that can move in a line, you specify a direction vector. You can come up with some crazy non-standard movement patterns using a combination of addIfOnBoard and addLineToList.

public class Bishop extends Figure {
   public Bishop( boolean whiteFigure ) {
      super( whiteFigure, "B", false, 3 );
   }

   @Override
   protected List<Move> getAllPossibilities() {
      Board board = Board.getBoard();
      Square currentSquare = board.whereAmI( this );

      List<Move> possibilities = new ArrayList<Move>();
      addLineToList( possibilities, currentSquare, 1, 1 ); // Up-Right
      addLineToList( possibilities, currentSquare, -1, 1 ); // Up-Left
      addLineToList( possibilities, currentSquare, 1, -1 ); // Down-Right
      addLineToList( possibilities, currentSquare, -1, -1 ); // Down-Left
      return possibilities;
   }
}

isUnderAttack is used for highlighting valid moves and checking the win condition. The board abstractly uses Figures, completely decoupled from implementation details. There can be any number of Figure implementations thanks to polymorphism.

public class Board {
    // Grid which chess is played on
    private Square[][] grid;

    /**
     * Checks every enemy move to see if it could hit square
     */
    public boolean isUnderAttack(Square square, boolean victimIsWhite) {
        // List of current enemy moves
        List<Move> enemyMoves = new ArrayList<Move>();

        // Goes through each enemy figure and adds all of its valid moves
        for (int h = 0; h < 8; h++) {
            for (int v = 0; v < 8; v++) {
                Figure piece = grid[h][v].getFigure();
                if (piece != null && piece.isWhiteFigure() != victimIsWhite
                        && piece.getAllMoves(Move.ATTACKABLE_SQUARES) != null) {
                    enemyMoves.addAll(piece.getAllMoves(Move.ATTACKABLE_SQUARES));
                }
            }
        }

        // Check to see if square is under attack by one of the enemy moves
        boolean isUnderFire = false;
        for (Move move : enemyMoves) {
            if (!isUnderFire && move.getDestination().equals(square)) {
                isUnderFire = true;
            }
        }
        return isUnderFire;
    }
}

flow chart

Take a gander by opening this image in a new tab.