Bird
Raised Fist0

Design a Chess Game - Board, Pieces, Move Validation, Check Detection, Turn

LLDhardInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Design a Chess Game - Board, Pieces, Move Validation, Check Detection, Turn
40 min ยท hard
hard

Design a chess game system that supports standard chess rules including piece movement, turn-based play, check detection, and board state management.

  • The board is an 8x8 grid with 64 squares.
  • Each piece type (King, Queen, Rook, Bishop, Knight, Pawn) has unique move validation rules.
  • The system must detect check and prevent illegal moves that leave the king in check.
  • Players alternate turns; only the player whose turn it is can move.
  • The design must allow extensibility for future variants (e.g., Chess960).
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Board and Square Representation
Design classes to represent the chess board and individual squares, ensuring efficient access and state management.
๐Ÿ’ก Hint
Use composition to model Board containing Squares; Squares hold piece references. Use enums for ranks and files.
class Board contains Square[][] squares - composition with lifecycle ownership; enum Rank and File for board coordinates to ensure type safety and exhaustive switch.
Mid: Uses composition for Board-Square; Senior: Explains enum Rank/File prevent invalid coordinates and enable exhaustive switch in move logic.
โš ๏ธCommon Wrong Answer
Using int or String for coordinates leads to invalid positions at runtime and no compile-time checks.
Step 2
Model Pieces and Move Validation
Design an abstract Piece class and concrete subclasses for each piece type with move validation logic.
๐Ÿ’ก Hint
Use abstract class Piece with shared state (color, position) and abstract method isValidMove; subclasses override with specific rules.
abstract class Piece with fields color, position; abstract boolean isValidMove(Position to, Board board) - abstract class needed because interface cannot hold instance state in Java.
Mid: Identifies abstract class for shared state and polymorphic behavior; Senior: Explains why interface cannot hold position state and why abstract method enforces contract.
โš ๏ธCommon Wrong Answer
Using interface for Piece with default methods but no fields causes position state to be managed externally, complicating design and violating encapsulation.
Step 3
Implement Turn Management and Move Execution
Design a mechanism to enforce turn order and execute moves atomically, preventing race conditions in a concurrent environment.
๐Ÿ’ก Hint
Use a GameController class with volatile currentPlayer field and synchronized method executeMove to atomically validate and update board state.
volatile PlayerColor currentPlayer; public synchronized boolean executeMove(Position from, Position to) - synchronized ensures atomic check-and-set of currentPlayer and board update; volatile guarantees visibility across threads.
Mid: Uses synchronized to prevent concurrent moves; Senior: Explains thread interleaving where without synchronized, two threads could move simultaneously causing inconsistent board state.
โš ๏ธCommon Wrong Answer
No synchronization leads to race conditions where two moves can be processed concurrently, corrupting game state.
Step 4
Detect Check and Prevent Illegal Moves
Design logic to detect if a move results in the player's own king being in check and reject such moves.
๐Ÿ’ก Hint
Implement method isInCheck(PlayerColor) in Board; executeMove calls this after tentative move; rollback if true.
Board method public boolean isInCheck(PlayerColor color) uses piece positions to detect threats; executeMove performs tentative move, calls isInCheck, and reverts if king is in check - enforces Liskov Substitution by ensuring move validity across subclasses.
Mid: Implements check detection after move; Senior: Explains how tentative move and rollback require careful state management to avoid partial updates.
โš ๏ธCommon Wrong Answer
Not checking for check allows illegal moves that leave king vulnerable, breaking chess rules.
Step 5
Design for Extensibility and Maintainability
Ensure the design supports adding new piece types or chess variants without modifying existing classes.
๐Ÿ’ก Hint
Use Open/Closed Principle: abstract Piece class with polymorphic move validation; Board and GameController depend on Piece abstraction, not concrete classes.
Piece is abstract class with polymorphic isValidMove; Board and GameController depend on Piece abstraction - Open/Closed Principle enforced by programming to abstract class, enabling extension without modification.
Mid: Recognizes use of abstract class for extensibility; Senior: Explains how adding new Piece subclasses requires no changes to Board or GameController, avoiding fragile base class problem.
โš ๏ธCommon Wrong Answer
Hardcoding piece types in Board or GameController violates Open/Closed Principle, requiring code changes for new pieces.
๐Ÿ”Œ
API Design Challenge
Design APIs for move execution, board state retrieval, and check detection that are intuitive, thread-safe, and extensible for future chess variants.
  • Use synchronized methods or locks to ensure atomic move execution.
  • Design Position and Move classes to encapsulate coordinates and move details.
  • Expose Board state via immutable snapshots or controlled accessors to prevent external mutation.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Board-Square composition with lifecycle ownership
  • Rank and File enums for coordinates
  • abstract class Piece with shared state and abstract isValidMove
  • Concrete Piece subclasses overriding isValidMove
  • GameController with volatile currentPlayer and synchronized executeMove
Good to Have - 30%
  • Board.isInCheck method for check detection
  • Tentative move and rollback logic in executeMove
  • SOLID principles explicitly applied
  • Extensibility via abstract Piece class
  • Thread-safe turn management with volatile and synchronized
Bonus - 10%
  • Support for Chess variants via subclassing
  • Detailed concurrency scenario explanation
  • Use of enums for PlayerColor and PieceType
Using int or String for board coordinates leads to invalid positions and runtime errors; enums prevent this at compile time.
Using interface for Piece with stateful behavior breaks encapsulation since interfaces cannot hold instance fields in Java.
No synchronization on move execution causes race conditions, allowing simultaneous moves corrupting game state.
Hardcoding piece types in Board or GameController violates Open/Closed Principle, requiring code changes for new pieces.
โœ…
Model Answer
โ–ผ Reveal

This design models a chess game with a Board composed of Squares, each holding a Piece or null. Coordinates are represented by Rank and File enums for type safety. An abstract Piece class holds shared state and declares an abstract isValidMove method, overridden by concrete subclasses for each piece type. GameController manages turn order with a volatile currentPlayer and synchronized executeMove method to ensure atomic move execution and thread safety. Moves are tentatively applied and validated against check conditions via Board.isInCheck, with rollback if illegal. The design follows SOLID principles, enabling extensibility for new piece types or variants without modifying existing classes.

  • Use composition for Board containing Square[][] with lifecycle ownership to ensure encapsulation and efficient access.
  • Represent board coordinates with Rank and File enums to prevent invalid positions and enable exhaustive switch statements.
  • Implement Piece as an abstract class with shared state (color, position) and abstract isValidMove method to enforce polymorphic move validation.
  • Use volatile currentPlayer and synchronized executeMove in GameController to ensure thread-safe turn management and atomic move execution.
  • Implement tentative move application and rollback in executeMove by calling Board.isInCheck to prevent illegal moves leaving king in check.
  • Design Board.isInCheck to scan opponent pieces for threats to the king, enforcing chess rules.
  • Follow SOLID principles: Single Responsibility for each class, Open/Closed by programming to Piece abstraction, and Liskov Substitution by interchangeable Piece subclasses.
  • Enable extensibility by allowing new Piece subclasses without modifying Board or GameController.
  • Board class uses composition to contain 64 Square objects, each holding a Piece reference or null.
  • Rank and File enums represent board coordinates, enabling type-safe position handling and exhaustive switch statements.
  • abstract class Piece holds shared state (color, position) and declares abstract isValidMove method for polymorphic move validation.
  • Concrete Piece subclasses override isValidMove with piece-specific movement rules.
  • GameController manages turn order with volatile currentPlayer and synchronized executeMove method to ensure atomic move execution and visibility.
  • executeMove performs tentative move, calls Board.isInCheck to detect illegal moves, and rolls back if move leaves king in check.
  • Board.isInCheck scans opponent pieces to detect threats to the king, ensuring rule enforcement.
  • Design follows SOLID principles: Single Responsibility (each class has focused role), Open/Closed (extend Piece without modifying Board/GameController), Liskov Substitution (Piece subclasses interchangeable).
  • Use of volatile and synchronized ensures thread-safe turn management and move execution in concurrent scenarios.
  • Extensibility is achieved by programming to abstract Piece class, allowing new piece types or variants without modifying existing code.

Practice

(1/5)
1. In a command pattern implementation for undo/redo, a Command interface declares an execute() method and concrete commands implement this interface to encapsulate operations. Which OOP concept is primarily applied here and why is this the correct use case?
easy
A. Encapsulation, because each command object hides the details of the operation it performs
B. Inheritance, because concrete commands extend a base class to reuse code
C. Polymorphism, because commands override execute() to provide different behaviors
D. Abstraction, because the interface hides implementation details from the client

Solution

  1. Step 1: Identify the role of Command objects

    Each command encapsulates all information needed to perform an action, hiding the operation's details from the invoker.
  2. Step 2: Differentiate encapsulation from other concepts

    While polymorphism and abstraction are involved, the key concept is encapsulation because commands package the action and its parameters as an object.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Command objects encapsulate operations and data for undo/redo [check]
Hint: Command pattern encapsulates operations as objects [check]
Common Mistakes:
2. In designing the Job Scheduler, should the RetryPolicy be an abstract class or an interface, and why?
easy
A. Interface, because RetryPolicy requires multiple inheritance of behavior.
B. Interface, because RetryPolicy defines behavior only and implementations may vary widely without shared state.
C. Abstract class, because interfaces cannot have method implementations in Java.
D. Abstract class, because RetryPolicy needs to hold state like retry count and share code.

Solution

  1. Step 1: Analyze RetryPolicy requirements

    RetryPolicy typically holds state such as retry count, max retries, and backoff strategy, which benefits from shared code and fields.
  2. Step 2: Abstract class vs interface in Java

    Abstract classes can hold state and provide default implementations, while interfaces (pre-Java 8) cannot hold state. Since RetryPolicy needs state, abstract class fits better.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    RetryPolicy holds state -> abstract class [check]
Hint: Stateful behavior -> abstract class; stateless -> interface [check]
Common Mistakes:
3. In the in-memory file system design, a FileSystem class has a private constructor and a static synchronized getInstance() method returning the single instance. Which pattern is applied here and why is this the correct use case?
easy
A. Factory pattern, because it controls object creation centrally.
B. Singleton pattern, to ensure only one instance manages the entire file system state.
C. Builder pattern, to construct complex file system objects step-by-step.
D. Prototype pattern, to clone existing file system instances efficiently.

Solution

  1. Step 1: Identify the pattern from constructor and method signature

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance, matching Singleton pattern characteristics.
  2. Step 2: Reason why Singleton fits this use case

    Singleton is appropriate because the file system state must be globally consistent and unique; multiple instances would cause conflicting states.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
4. In designing the Player abstraction for the Tic-Tac-Toe game, should Player be an abstract class or an interface, and why?
easy
A. Abstract class, because Java interfaces cannot have default methods for shared logic.
B. Interface, because players only define behavior like making moves, allowing multiple inheritance of behavior.
C. Abstract class, because players share common state like name and score that should be inherited.
D. Interface, because it allows storing player state directly in the interface.

Solution

  1. Step 1: Analyze state and behavior requirements

    Players have common state (e.g., name, symbol, score) and some shared behavior (e.g., basic validation). Abstract classes allow state fields and shared method implementations.
  2. Step 2: Why abstract class is better than interface here

    Interfaces define behavior contracts but cannot hold instance state. Although Java 8+ interfaces support default methods, they cannot hold mutable state. Thus, abstract class suits better for shared state and behavior.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Shared state + behavior -> abstract class, not interface [check]
Hint: State needs abstract class; behavior-only fits interface [check]
Common Mistakes:
5. In the ParkingLot class, if parkVehicle() is not synchronized, which exact sequence of Thread 1 and Thread 2 causes double-booking of the same spot?
medium
A. Thread 1 checks spot availability -> true, context switch; Thread 2 checks same spot -> true; both assign spot simultaneously.
B. Thread 1 releases spot, Thread 2 immediately parks vehicle on it.
C. Thread 1 parks vehicle, Thread 2 parks vehicle after Thread 1 finishes.
D. Thread 1 and Thread 2 both wait on a lock, then park sequentially.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available but before marking it occupied, Thread 2 also reads it as available.
  2. Step 2: Both threads proceed to assign the same spot concurrently

    Because no lock protects the check and assignment, both parkVehicle calls succeed, causing double-booking.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act without lock -> double-booking race [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard