Bird
Raised Fist0
LLDsystem_design~10 mins

Piece movement rules (polymorphism) in LLD - Scalability & System Analysis

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
Scalability Analysis - Piece movement rules (polymorphism)
Growth Table: Piece Movement Rules with Polymorphism
Users / GamesPieces per GameMovement Rule Checks per MoveConcurrent MovesMemory Usage
100 users32 pieces1-5 checks10 concurrentLow (few objects)
10,000 users32 pieces1-5 checks1,000 concurrentModerate (thousands of objects)
1,000,000 users32 pieces1-5 checks100,000 concurrentHigh (millions of objects)
100,000,000 users32 pieces1-5 checks10,000,000 concurrentVery High (billions of objects)

As users and concurrent games grow, the number of piece objects and movement rule checks increase linearly, impacting memory and CPU.

First Bottleneck

The first bottleneck is the CPU processing the polymorphic movement rule checks for each piece move. Each move requires calling the correct movement logic based on piece type, which can be CPU intensive at scale.

Memory usage also grows as each piece is an object with its own behavior, increasing heap size and garbage collection overhead.

Scaling Solutions
  • Horizontal scaling: Add more game servers to distribute concurrent games and moves.
  • Caching: Cache common movement results or precompute valid moves to reduce repeated calculations.
  • Object pooling: Reuse piece objects to reduce memory churn and GC overhead.
  • Optimize polymorphism: Use efficient dispatch methods or flatten movement logic to reduce CPU cost.
  • Sharding: Partition games by user or game ID to isolate load.
Back-of-Envelope Cost Analysis

Assuming 1 move per second per user:

  • At 1,000 users: ~1,000 moves/sec, easily handled by 1-2 servers.
  • At 1,000,000 users: ~1,000,000 moves/sec, requires ~200-300 servers (assuming 3,000 moves/sec per server).
  • Memory per piece object ~1 KB, 32 pieces/game, 1M games = ~32 GB memory just for pieces.
  • Network bandwidth depends on move data size (~100 bytes), so 1M moves/sec = ~100 MB/s bandwidth.
Interview Tip

Start by explaining how polymorphism helps organize movement rules cleanly. Then discuss how CPU and memory grow with users and moves. Identify the CPU as the first bottleneck due to polymorphic calls. Propose horizontal scaling and caching as primary solutions. Mention object pooling and sharding for memory and load distribution. Keep answers structured: problem, bottleneck, solution.

Self Check

Your game server handles 1,000 moves per second. Traffic grows 10x to 10,000 moves per second. What do you do first and why?

Answer: Add more game servers (horizontal scaling) to distribute the increased load and maintain performance, because CPU is the bottleneck processing movement rules.

Key Result
Polymorphic piece movement rules scale linearly with users and moves, making CPU the first bottleneck; horizontal scaling and caching are key to handle growth.

Practice

(1/5)
1. What is the main benefit of using polymorphism for piece movement rules in a game design?
easy
A. It allows each piece to have its own move logic without type checks.
B. It forces all pieces to share the same move logic.
C. It requires manual checking of piece types before moving.
D. It prevents pieces from moving on the board.

Solution

  1. Step 1: Understand polymorphism concept

    Polymorphism allows different objects to be treated through a common interface while having their own behavior.
  2. Step 2: Apply to piece movement

    Each piece class implements its own move() method, so no need to check piece type before moving.
  3. Final Answer:

    It allows each piece to have its own move logic without type checks. -> Option A
  4. Quick Check:

    Polymorphism = own move logic without type checks [OK]
Hint: Polymorphism means no type checks for moves [OK]
Common Mistakes:
  • Thinking all pieces share the same move logic
  • Believing manual type checks are needed
  • Confusing polymorphism with inheritance only
2. Which of the following is the correct way to declare a base class method for piece movement in a polymorphic design?
easy
A. move(self): pass
B. def move(self): pass
C. def move(): pass
D. def move(self, board): return

Solution

  1. Step 1: Recall method declaration syntax in Python

    Instance methods must have self as the first parameter.
  2. Step 2: Identify correct method signature

    def move(self): pass correctly declares a method with self and no implementation.
  3. Final Answer:

    def move(self): pass -> Option B
  4. Quick Check:

    Method with self parameter = def move(self): pass [OK]
Hint: Instance methods always start with self parameter [OK]
Common Mistakes:
  • Omitting self parameter in method
  • Using incorrect syntax without def keyword
  • Adding unnecessary parameters without context
3. Given the following code, what will be the output?
class Piece:
    def move(self):
        return "Base move"

class Knight(Piece):
    def move(self):
        return "L-shaped move"

pieces = [Piece(), Knight()]
for p in pieces:
    print(p.move())
medium
A. Base move\nL-shaped move
B. L-shaped move\nL-shaped move
C. Error: move() not implemented
D. Base move\nBase move

Solution

  1. Step 1: Understand method overriding

    Subclass Knight overrides move() to return "L-shaped move".
  2. Step 2: Trace the loop output

    First object is Piece, prints "Base move"; second is Knight, prints "L-shaped move".
  3. Final Answer:

    Base move\nL-shaped move -> Option A
  4. Quick Check:

    Base class and overridden subclass moves printed [OK]
Hint: Subclass method overrides base method output [OK]
Common Mistakes:
  • Assuming base method always runs
  • Expecting same output for all pieces
  • Confusing method overriding with overloading
4. Identify the error in the following polymorphic piece movement code:
class Piece:
    def move(self):
        pass

class Bishop(Piece):
    def move():
        print("Diagonal move")

b = Bishop()
b.move()
medium
A. Cannot instantiate Bishop directly
B. Piece.move() should return a value
C. Bishop.move() missing self parameter
D. print statement syntax error

Solution

  1. Step 1: Check method signatures

    Bishop.move() lacks self parameter, so it is not a proper instance method.
  2. Step 2: Understand call context

    Calling b.move() passes self automatically, causing a TypeError due to missing parameter.
  3. Final Answer:

    Bishop.move() missing self parameter -> Option C
  4. Quick Check:

    Instance methods must have self parameter [OK]
Hint: Instance methods always need self parameter [OK]
Common Mistakes:
  • Ignoring missing self in subclass method
  • Thinking base class method must return value
  • Assuming print syntax is wrong
5. You are designing a chess game using polymorphism for piece movement. How should you structure your classes to allow easy addition of new piece types without changing existing code?
hard
A. Write a single move() function with if-else for each piece type.
B. Implement move logic only in the base class and override rarely.
C. Use global variables to track piece types and moves.
D. Create a base Piece class with an abstract move() method; each piece subclass implements move().

Solution

  1. Step 1: Apply polymorphism design principle

    Use a base class with an abstract or empty move() method to define interface.
  2. Step 2: Implement subclasses for each piece

    Each piece subclass provides its own move() logic, enabling extension without modifying base code.
  3. Final Answer:

    Create a base Piece class with an abstract move() method; each piece subclass implements move(). -> Option D
  4. Quick Check:

    Base class + subclass move() = scalable design [OK]
Hint: Base class with abstract move() enables easy extension [OK]
Common Mistakes:
  • Using if-else instead of polymorphism
  • Relying on global variables for logic
  • Putting all move logic in base class only