The code in general is fine. If it were a C program (or some other classic language) I would even say good. However this is Java and especially by using integers to represent the pieces you completely miss its point, which includes type safety and would eliminate things like throw new IllegalStateException("Should not get here.").
This seems like a prime example to use Enums and records:
enum PieceType {
PAWN, BISHOP, KNIGHT, ROOK, QUEEN, KING;
};
enum Color {
WHITE, BLACK;
}
record Piece(PieceType type, Color color) {}
private Piece[][] state = new Piece[N][N];
state[0][0] =
state[0][7] = new Piece(ROOK, WHITE);
Empty squares would be represented by null.
This could be extended further by putting piece type specific code such as the character representation and move validation into the PieceType instances.
If you do want to use integers instead, then at the least consider using bit manipulation, for example:
public static final byte EMPTY = 0;
public static final byte PAWN = (byte) 0b0000001;
public static final byte BISHOP = (byte) 0b0000010;
public static final byte KNIGHT = (byte) 0b0000100;
public static final byte ROOK = (byte) 0b0001000;
public static final byte QUEEN = (byte) 0b0010000;
public static final byte KING = (byte) 0b0100000;
public static final byte WHITE_PAWN = PAWN;
public static final byte WHITE_BISHOP = BISHOP;
public static final byte WHITE_KNIGHT = KNIGHT;
public static final byte WHITE_ROOK = ROOK;
public static final byte WHITE_QUEEN = QUEEN;
public static final byte WHITE_KING = KING;
public static final byte BLACK = (byte) 0b1000000;
public static final byte BLACK_PAWN = BLACK | PAWN;
public static final byte BLACK_BISHOP = BLACK | BISHOP;
// etc.
This allows simple checking of type:
if (piece & PAWN > 0) { ... }
and color
if (piece & BLACK == 0) {
// Piece is white
} else {
// piece is black
}
Some other points:
if (!getClass().equals(o.getClass())) {
This would be simpler as
if (o instanceof ChessBoardState) {
This would also eliminate the null check before, because this avoids accessing a potential null reference, and null instanceof ... always returns false anyway.
You are misusing the switch expression syntax a bit. It could be written as:
return switch (pieceCode) {
case EMPTY -> (x + y) % 2 == 0 ? '.' : '#';
case WHITE_PAWN -> 'P';
// etc.
}
```