Problem 4.8 — Chess
In one line
The inheritance-vs-composition battle ground: piece movement, game state, rules, castling, check/checkmate.
Think of it like this
A chess set where every piece moves differently. The mistake most people make is building one giant 'Piece' class with a big if/else for movement; the better way is giving each movement pattern (straight line, L-shape, diagonal) its own small rule that any piece can use.
Key ideas
- 01
Inheritance trap: 'Piece' base + King/Queen/... — castling, en passant and check break the pure hierarchy. Composition (movement rules as strategies) survives better.
- 02
Each Piece exposes validMoves(board, position) using a MovementRule (e.g. LinearMovement, StepMovement, LShapeMovement) — composition over inheritance.
- 03
GameState: board + turn + castlingRights + enPassantTarget + halfmoveClock + fullmoveNumber (FEN fields).
- 04
Rule separation: legalMove = piece pattern AND path clear AND not exposing own king AND special rules handled (castling, promotion).
- 05
Check detection: attacker-set on king; checkmate = in check AND no legal moves; stalemate = no legal moves and not in check.
- 06
Undo (take-back) needs move history snapshots — Command pattern shines (see Phase 2).
- 07
Interview scope tip: nail pawn/queen/knight moves + check detection first; castling/promotion are bonus depth.
Java / Spring map
- →
Record-based board + MovementRule strategies + MoveHistory stack for undo.
Code & diagrams
Movement as composition: one MovementRule per pattern, shared across pieces that move the same way.
public record Position(int row, int col) {}
public interface MovementRule {
List<Position> destinations(Board b, Position from);
}
// Straight lines, any distance, stopping at the first blocker (Rook, Bishop, Queen)
public final class LinearMovement implements MovementRule {
private final int[][] directions; // e.g. {{1,0},{-1,0},{0,1},{0,-1}} for a rook
public LinearMovement(int[][] directions) { this.directions = directions; }
public List<Position> destinations(Board b, Position from) {
List<Position> out = new ArrayList<>();
for (int[] d : directions) {
int r = from.row(), c = from.col();
while (true) {
r += d[0]; c += d[1];
if (!b.inBounds(r, c)) break;
Piece occupant = b.at(r, c);
if (occupant == null) { out.add(new Position(r, c)); continue; }
if (occupant.color() != b.at(from).color()) out.add(new Position(r, c)); // capture
break; // blocked either way
}
}
return out;
}
}
// Fixed offsets, ignores blockers along the way (Knight)
public final class LShapeMovement implements MovementRule {
private static final int[][] OFFSETS = {{1,2},{2,1},{-1,2},{-2,1},{1,-2},{2,-1},{-1,-2},{-2,-1}};
public List<Position> destinations(Board b, Position from) {
List<Position> out = new ArrayList<>();
for (int[] o : OFFSETS) {
int r = from.row() + o[0], c = from.col() + o[1];
if (b.inBounds(r, c) && (b.at(r, c) == null || b.at(r, c).color() != b.at(from).color()))
out.add(new Position(r, c));
}
return out;
}
}
public final class Piece {
private final Color color;
private final MovementRule rule; // COMPOSITION, not a subclass per piece type
public Piece(Color color, MovementRule rule) { this.color = color; this.rule = rule; }
public Color color() { return color; }
public List<Position> legalMoves(Board b, Position at) {
return rule.destinations(b, at).stream()
.filter(to -> !exposesOwnKing(b, at, to)) // "not exposing own king" rule lives here
.toList();
}
private boolean exposesOwnKing(Board b, Position from, Position to) { /* simulate + check */ return false; }
}
// A Rook is just: new Piece(WHITE, new LinearMovement(ROOK_DIRS));
// A Queen is: new Piece(WHITE, new LinearMovement(ALL_8_DIRS));
// Adding "Fischer random" pieces later = new MovementRule, zero changes to Piece.Explain without notes
Where does pure inheritance of pieces hurt the most — show King's castling edge case.
Practice
Implement knight + queen moves with clear-path rules, then the check detector.
Trade-offs
- ↔
Rule objects multiply but every new variant (Fischer random start) becomes a new rule set, not a rewrite.
Completion checklist
I can model board + pieces + move rules and detect check without a giant switch.