Bird
Raised Fist0
LLDsystem_design~7 mins

Why chess tests polymorphism and strategy in LLD - Why This Architecture

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Problem Statement
Without a flexible design, adding new chess pieces or changing their behavior requires rewriting large parts of the code, making it hard to maintain and extend. This rigidity leads to bugs and slows down development when implementing new strategies or rules.
Solution
Chess uses polymorphism to let each piece define its own movement rules while sharing a common interface. Strategy pattern allows the game to change or select different move strategies dynamically, enabling flexible and maintainable code that can easily adapt to new rules or AI behaviors.
Architecture
ChessGame
Piece (base)
Pawn
MoveStrategy
◀──────────────┐
Aggressive

This diagram shows ChessGame using a base Piece class with polymorphic subclasses for each piece type. MoveStrategy interface allows dynamic selection of different move behaviors like Aggressive or Defensive.

Trade-offs
✓ Pros
Enables adding new piece types without changing existing code.
Supports dynamic change of move strategies at runtime.
Improves code maintainability and readability by separating concerns.
Facilitates testing different AI strategies independently.
✗ Cons
Introduces more classes and interfaces, increasing initial complexity.
May add slight runtime overhead due to dynamic dispatch.
Requires careful design to avoid excessive subclassing or strategy proliferation.
Use when the system needs to support multiple piece types with different behaviors and when move strategies may change or extend over time, especially in AI or rule variations.
Avoid if the chess implementation is very simple, fixed, and unlikely to change, as the added abstraction may be unnecessary overhead.
Real World Examples
Chess.com
Uses polymorphism to represent different chess pieces and strategy pattern to implement various AI difficulty levels and move strategies.
Lichess
Applies polymorphism for piece behavior and strategy pattern to allow different AI engines and custom rule sets.
DeepMind (AlphaZero)
Implements flexible strategy selection to train and test different move policies dynamically.
Code Example
Before, the ChessGame class had separate methods for each piece, making it hard to add new pieces or change behavior. After applying polymorphism, each piece class implements its own move method. The Strategy pattern allows selecting different move behaviors dynamically, improving flexibility and maintainability.
LLD
### Before: No polymorphism or strategy
class ChessGame:
    def move_pawn(self, position):
        # specific pawn move logic
        pass
    def move_knight(self, position):
        # specific knight move logic
        pass

### After: Using polymorphism and strategy
from abc import ABC, abstractmethod

class Piece(ABC):
    @abstractmethod
    def move(self, position):
        pass

class Pawn(Piece):
    def move(self, position):
        # pawn-specific move logic
        print(f"Pawn moves to {position}")

class Knight(Piece):
    def move(self, position):
        # knight-specific move logic
        print(f"Knight moves to {position}")

class MoveStrategy(ABC):
    @abstractmethod
    def select_move(self, piece, position):
        pass

class AggressiveStrategy(MoveStrategy):
    def select_move(self, piece, position):
        print("Aggressive move selected")
        piece.move(position)

class DefensiveStrategy(MoveStrategy):
    def select_move(self, piece, position):
        print("Defensive move selected")
        piece.move(position)

# Usage
pawn = Pawn()
knight = Knight()
strategy = AggressiveStrategy()
strategy.select_move(pawn, "E4")
strategy = DefensiveStrategy()
strategy.select_move(knight, "F6")
OutputSuccess
Alternatives
Procedural design
Uses conditional statements to handle piece behavior instead of polymorphism and strategy objects.
Use when: Choose when the system is very small and unlikely to change, and simplicity is preferred over flexibility.
State pattern
Encapsulates piece states and transitions rather than move strategies.
Use when: Choose when piece behavior depends heavily on internal states like promotion or en passant.
Summary
Polymorphism allows each chess piece to define its own movement behavior through a shared interface.
Strategy pattern enables dynamic selection of move behaviors, supporting flexible AI and rule variations.
Together, they make the chess system easier to extend, maintain, and test.

Practice

(1/5)
1. In the context of chess and system design, what does polymorphism primarily demonstrate?
easy
A. Chess pieces cannot change their behavior during the game
B. Chess pieces all move in the same way regardless of type
C. Chess strategy is about random moves without planning
D. Different chess pieces use the same method name but have unique move behaviors

Solution

  1. Step 1: Understand polymorphism in chess pieces

    Polymorphism means objects share the same interface but behave differently. Chess pieces all have a move method but move uniquely.
  2. Step 2: Relate polymorphism to chess piece behavior

    Each piece type (pawn, knight, bishop) implements move differently, showing polymorphism.
  3. Final Answer:

    Different chess pieces use the same method name but have unique move behaviors -> Option D
  4. Quick Check:

    Polymorphism = Same method, different behavior [OK]
Hint: Polymorphism means same method, different actions [OK]
Common Mistakes:
  • Thinking all pieces move the same way
  • Confusing polymorphism with inheritance only
  • Ignoring that method names are shared
2. Which of the following code snippets correctly shows polymorphism for chess pieces in a low-level design?
easy
A. class Piece { move() { /* generic move */ } } class Pawn extends Piece { move() { /* pawn move */ } }
B. class Pawn { move() { /* pawn move */ } } class Knight { jump() { /* knight jump */ } }
C. function move(piece) { if(piece.type == 'pawn') { /* move */ } else { /* no move */ } }
D. class Piece { move() { console.log('move'); } } let piece = new Piece(); piece.move();

Solution

  1. Step 1: Identify polymorphism in code

    Polymorphism requires a base class with a method overridden by subclasses. class Piece { move() { /* generic move */ } } class Pawn extends Piece { move() { /* pawn move */ } } shows a base Piece class with move(), overridden by Pawn.
  2. Step 2: Check other options for polymorphism

    class Pawn { move() { /* pawn move */ } } class Knight { jump() { /* knight jump */ } } lacks shared method names; function move(piece) { if(piece.type == 'pawn') { /* move */ } else { /* no move */ } } uses conditional logic, not polymorphism; class Piece { move() { console.log('move'); } } let piece = new Piece(); piece.move(); has no subclassing.
  3. Final Answer:

    class Piece { move() { /* generic move */ } } class Pawn extends Piece { move() { /* pawn move */ } } -> Option A
  4. Quick Check:

    Base class + overridden method = polymorphism [OK]
Hint: Look for base class with overridden methods [OK]
Common Mistakes:
  • Confusing conditional logic with polymorphism
  • Missing method overriding in subclasses
  • Ignoring inheritance structure
3. Given the following pseudo-code, what will be the output when calling move() on each piece in the list?
class Piece { move() { return 'generic move'; } } class Knight extends Piece { move() { return 'L-shape move'; } } class Bishop extends Piece { move() { return 'diagonal move'; } } pieces = [new Piece(), new Knight(), new Bishop()] for p in pieces: print(p.move())
medium
A. L-shape move\ndiagonal move\ngeneric move
B. generic move\nL-shape move\ndiagonal move
C. generic move\ngeneric move\ngeneric move
D. Error: move method not found

Solution

  1. Step 1: Understand method overriding in subclasses

    Each subclass overrides move() to return its specific move string.
  2. Step 2: Trace the loop calling move()

    For Piece instance, move() returns 'generic move'. For Knight, 'L-shape move'. For Bishop, 'diagonal move'.
  3. Final Answer:

    generic move\nL-shape move\ndiagonal move -> Option B
  4. Quick Check:

    Overridden methods print their own strings [OK]
Hint: Each subclass method overrides base method output [OK]
Common Mistakes:
  • Assuming base method output for all pieces
  • Mixing order of outputs
  • Expecting runtime errors incorrectly
4. Identify the error in this chess piece design code snippet:
class Piece { move() { throw 'Not implemented'; } } class Queen extends Piece { } let q = new Queen(); q.move();
medium
A. Queen class should not inherit from Piece
B. Piece class should not have a move() method
C. Queen class does not override move(), causing runtime error
D. No error, code runs fine

Solution

  1. Step 1: Analyze base class move() method

    Piece.move() throws an error if called directly, indicating it must be overridden.
  2. Step 2: Check Queen class implementation

    Queen does not override move(), so calling q.move() calls base method and throws error.
  3. Final Answer:

    Queen class does not override move(), causing runtime error -> Option C
  4. Quick Check:

    Abstract method not overridden = runtime error [OK]
Hint: Abstract methods must be overridden to avoid errors [OK]
Common Mistakes:
  • Assuming base method runs without error
  • Thinking inheritance is wrong here
  • Ignoring the throw statement in base method
5. How does combining polymorphism and strategy in chess help design a flexible and smart system?
hard
A. Polymorphism allows different piece behaviors; strategy plans moves ahead for better decisions
B. Polymorphism forces all pieces to behave identically; strategy ignores future moves
C. Strategy replaces polymorphism by hardcoding moves; polymorphism is unnecessary
D. Polymorphism and strategy are unrelated concepts in system design

Solution

  1. Step 1: Understand polymorphism's role in flexibility

    Polymorphism lets different pieces share an interface but act differently, enabling flexible design.
  2. Step 2: Understand strategy's role in smart planning

    Strategy involves planning moves ahead to make smart decisions, improving system intelligence.
  3. Step 3: Combine both concepts

    Together, polymorphism provides flexible behaviors, and strategy guides smart choices, creating a robust system.
  4. Final Answer:

    Polymorphism allows different piece behaviors; strategy plans moves ahead for better decisions -> Option A
  5. Quick Check:

    Polymorphism + strategy = flexible, smart system [OK]
Hint: Polymorphism = flexibility; strategy = planning [OK]
Common Mistakes:
  • Thinking polymorphism means identical behavior
  • Ignoring the importance of planning in strategy
  • Separating polymorphism and strategy as unrelated