public void undoLastMove() {
    // Check that we have a move to undo
    if (moves.isEmpty()) {
      throw new InternalError("No move available to undo");
    }

    final Move lastMove = getLastMove();
    final Point from = lastMove.getFrom();
    final Point to = lastMove.getTo();
    final Piece piece = lastMove.getPiece();

    squares[from.getX()][from.getY()].setPiece(piece);

    if (lastMove.isLeftCastling()) {
      squares[to.getX()][to.getY()].setPiece(null);
      if (piece.getColor() == Color.W) {
        setPiece(Piece.WHITE_ROOK, King.W_LEFT_ROOK);
        setPiece(null, to.incrementX(1));
      } else {
        setPiece(Piece.BLACK_ROOK, King.B_LEFT_ROOK);
        setPiece(null, to.incrementX(1));
      }
    } else if (lastMove.isRightCastling()) {
      squares[to.getX()][to.getY()].setPiece(null);
      if (piece.getColor() == Color.W) {
        setPiece(Piece.WHITE_ROOK, King.W_RIGHT_ROOK);
        setPiece(null, to.decrementX(1));
      } else {
        setPiece(Piece.BLACK_ROOK, King.B_RIGHT_ROOK);
        setPiece(null, to.decrementX(1));
      }
    } else {
      final Piece captured = lastMove.getCaptured();

      // Undoing an en passant move?
      if (lastMove.isEnPassant()) {
        if (captured.getColor() == Color.B) {
          squares[to.getX()][to.getY() - 1].setPiece(captured);
        } else {
          squares[to.getX()][to.getY() + 1].setPiece(captured);
        }
        squares[to.getX()][to.getY()].setPiece(null);
      } else {
        squares[to.getX()][to.getY()].setPiece(captured);
      }

      // Keep track of the kings
      if (piece == WHITE_KING) {
        whiteKing = squares[from.getX()][from.getY()];
      } else if (piece == BLACK_KING) {
        blackKing = squares[from.getX()][from.getY()];
      }
    }

    // Remove move from history
    moves.remove(moves.size() - 1);
  }
  public void doMove(Move move) {
    final Point from = move.getFrom();
    final Point to = move.getTo();
    final Piece piece = move.getPiece();

    // Carry out the move on the board
    squares[from.getX()][from.getY()].setPiece(null);
    move.setCaptured(squares[to.getX()][to.getY()].getPiece());
    squares[to.getX()][to.getY()].setPiece(piece);

    // Keep track of the kings
    if (piece == WHITE_KING) {
      whiteKing = squares[to.getX()][to.getY()];
    } else if (piece == BLACK_KING) {
      blackKing = squares[to.getX()][to.getY()];
    }

    // Check for castling first
    if (move.isLeftCastling()) {
      if (piece.getColor() == Color.W) {
        setPiece(null, King.W_LEFT_ROOK);
        setPiece(Piece.WHITE_ROOK, to.incrementX(1));
      } else {
        setPiece(null, King.B_LEFT_ROOK);
        setPiece(Piece.BLACK_ROOK, to.incrementX(1));
      }
    } else if (move.isRightCastling()) {
      if (piece.getColor() == Color.W) {
        setPiece(null, King.W_RIGHT_ROOK);
        setPiece(Piece.WHITE_ROOK, to.decrementX(1));
      } else {
        setPiece(null, King.B_RIGHT_ROOK);
        setPiece(Piece.BLACK_ROOK, to.decrementX(1));
      }
    } else {
      // Check for pawn promotions
      if (piece.isPromoted(to)) {
        setPiece(piece.getColor().getQueen(), to);
        move.setPromoted(true);
      }

      // En passant?
      final Move lastMove = getLastMove();
      if (move.isEnPassantAllowed(lastMove)) {
        final Point lastTo = lastMove.getTo();
        move.setEnPassant(true);
        move.setCaptured(squares[lastTo.getX()][lastTo.getY()].getPiece());
        squares[lastTo.getX()][lastTo.getY()].setPiece(null);
      }
    }

    // Record last move
    moves.add(move);
  }