Design Chess

Let's design a system to play online chess.

Chess is a two-player strategy board game played on a chessboard, which is a checkered gameboard with 64 squares arranged in an 8×8 grid. There are a few versions of game types that people play all over the world. In this design problem, we are going to focus on designing a two-player online chess game.

widget

System Requirements

We’ll focus on the following set of requirements while designing the game of chess:

  1. The system should support two online players to play a game of chess.

  2. All rules of international chess will be followed.

  3. Each player will be randomly assigned a side, black or white.

  4. Both players will play their moves one after the other. The white side plays the first move.

  5. Players can’t cancel or roll back their moves.

  6. The system should maintain a log of all moves by both players.

  7. Each side will start with 8 pawns, 2 rooks, 2 bishops, 2 knights, 1 queen, and 1 king.

  8. The game can finish either in a checkmate from one side, forfeit or stalemate (a draw), or resignation.

Use case diagram

We have two actors in our system:

  • Player: A registered account in the system, who will play the game. The player will play chess moves.
  • Admin: To ban/modify players.

Here are the top use cases for chess:

  • Player moves a piece: To make a valid move of any chess piece.
  • Resign or forfeit a game: A player resigns from/forfeits the game.
  • Register new account/Cancel membership: To add a new member or cancel an existing member.
  • Update game log: To add a move to the game log.
widget
Use case diagram

Class diagram

Here are the main classes for chess:

  • Player: Player class represents one of the participants playing the game. It keeps track of which side (black or white) the player is playing.

  • Account: We’ll have two types of accounts in the system: one will be a player, and the other will be an admin.

  • Game: This class controls the flow of a game. It keeps track of all the game moves, which player has the current turn, and the final result of the game.

  • Box: A box represents one block of the 8x8 grid and an optional piece.

  • Board: Board is an 8x8 set of boxes containing all active chess pieces.

  • Piece: The basic building block of the system, every piece will be placed on a box. This class contains the color the piece represents and the status of the piece (that is, if the piece is currently in play or not). This would be an abstract class and all game pieces will extend it.

  • Move: Represents a game move, containing the starting and ending box. The Move class will also keep track of the player who made the move, if it is a castling move, or if the move resulted in the capture of a piece.

  • GameController: Player class uses GameController to make moves.

  • GameView: Game class updates the GameView to show changes to the players.

widget
Class diagram
widget

Activity diagrams

Make move: Any Player can perform this activity. Here are the set of steps to make a move:

widget

Code

Here is the code for the top use cases.

Enums, DataTypes, Constants: Here are the required enums, data types, and constants:

class GameStatus(Enum):
  ACTIVE, BLACK_WIN, WHITE_WIN, FORFEIT, STALEMATE, RESIGNATION = 1, 2, 3, 4, 5, 6


class AccountStatus(Enum):
  ACTIVE, CLOSED, CANCELED, BLACKLISTED, NONE = 1, 2, 3, 4, 5


class Address:
  def __init__(self, street, city, state, zip_code, country):
    self.__street_address = street
    self.__city = city
    self.__state = state
    self.__zip_code = zip_code
    self.__country = country


class Person():
  def __init__(self, name, address, email, phone):
    self.__name = name
    self.__address = address
    self.__email = email
    self.__phone = phone

Box: To encapsulate a cell on the chess board:

class Box:
  def __init__(self, piece, x, y):
    self.__piece = piece
    self.__x = x
    self.__y = y

  def get_piece(self):
    return self.__piece

  def set_piece(self, piece):
    self.__piece = piece

  def get_x(self):
    return self.__x

  def set_x(self, x):
    self.__x = x

  def get_y(self):
    return self.__y

  def set_y(self, y):
    self.__y = y

Piece: An abstract class to encapsulate common functionality of all chess pieces:

from abc import ABC, abstractmethod

class Piece(ABC):
  def __init__(self, white=False):
    self.__killed = False
    self.__white = white

  def is_white(self):
    return self.__white

  def set_white(self, white):
    self.__white = white

  def is_killed(self):
    return self.__killed

  def set_killed(self, killed):
    self.__killed = killed

  def can_move(self, board, start_box, end_box):
    None

King: To encapsulate King as a chess piece:

class King(Piece):
  def __init__(self, white):
    self.__castling_done = False
    super().__init__(white)

  def is_castling_done(self):
    return self.__castling_done

  def set_castling_done(self, castling_done):
    self.__castling_done = castling_done

  def can_move(self, board, start_box, end_box):
    # we can't move the piece to a box that has a piece of the same color
    if end_box.get_piece().is_white() == self.is_white():
      return False

    x = abs(start_box.get_x() - end_box.get_x())
    y = abs(start_box.get_y() - end_box.get_y())
    if x + y == 1:
      # check if self move will not result in king being attacked, if so return True
      return True

    return self.is_valid_castling(board, start_box, end_box)

  def is_valid_castling(self, board, start, end):

    if self.is_castling_done():
      return False

    # check for the white king castling
    if self.is_white() and start.get_x() == 0 and start.get_y() == 4 and end.get_y() == 0:
      # confirm if white king moved to the correct ending box
      if abs(end.get_y() - start.get_y()) == 2:
        # check if there the Rook is in the correct position
        # check if there is no piece between Rook and the King
        # check if the King or the Rook has not moved before
        # check if self move will not result in king being attacked
        # ...
        self.set_castling_done(True)
        return True

    else:
      # check for the black king castling
      self.set_castling_done(True)
      return True

    return False

  def is_castling_move(self, start, end):
    # check if the starting and ending position are correct
    None

Knight: To encapsulate Knight as a chess piece:

class Knight(Piece):
  def __init__(self, white):
    super().__init__(white)

  def can_move(self, board, start, end):

    # we can't move the piece to a box that has a piece of the same color
    if end.get_piece().is_white() == self.is_white():
      return False

    x = abs(start.get_x() - end.get_x())
    y = abs(start.get_y() - end.get_y())
    return x * y == 2

Board: To encapsulate a chess board:

class Board:
  def __init__(self):
    self.__boxes = [[]]

  def Board(self):
    self.reset_board()

  def get_box(self, x, y):
    if x < 0 or x > 7 or y < 0 or y > 7:
      raise Exception("Index out of bound")
    return self.__boxes[x][y]

  def reset_board(self):
    # initialize white pieces
    boxes[0][0] = Box(0, 0, Rook(True))
    boxes[0][1] = Box(0, 1, Knight(True))
    boxes[0][2] = Box(0, 2, Bishop(True))
    # ...
    boxes[1][0] = Box(1, 0, Pawn(True))
    boxes[1][1] = Box(1, 1, Pawn(True))
    # ...

    # initialize black pieces
    boxes[7][0] = Box(7, 0, Rook(False))
    boxes[7][1] = Box(7, 1, Knight(False))
    boxes[7][2] = Box(7, 2, Bishop(False))
    # ...
    boxes[6][0] = Box(6, 0, Pawn(False))
    boxes[6][1] = Box(6, 1, Pawn(False))
    # ...

    # initialize remaining boxes without any piece
    for i in range(2, 6):
      for j in range(0, 8):
        boxes[i][j] = Box(i, j, None)

Player: To encapsulate a chess player:

class Player(Account):
  def __init__(self, person, white_side=False):
    self.__person = person
    self.__white_side = white_side

  def is_white_side(self):
    return self.__white_side

Move: To encapsulate a chess move:

class Move:
  def is_white_side(self, player, start_box, end_box, piece_killed, castling_move=False):
    self.__player = player
    self.__start = start_box
    self.__end = end_box
    self.__piece_moved = self.__start.get_piece()
    self.__piece_killed = piece_killed
    self.__castling_move = castling_move

  def is_castling_move(self):
    return self.__castling_move

  def set_castling_move(self, castling_move):
    self.__castling_move = castling_move

Game: To encapsulate a chess game:

class Game:
  def __init__(self):
    self.__players = []
    self.__board = Board()
    self.__current_turn = None
    self.__status = GameStatus.ACTIVE
    self.__moves_played = []

  def initialize(self, player1, player2):
    self.__players[0] = player1
    self.__players[1] = player2

    self.__board.reset_board()

    if player1.is_white_side():
      self.__current_turn = player1
    else:
      self.__current_turn = player2

    self.__moves_played.clear()

  def is_end(self):
    return self.get_status() != GameStatus.ACTIVE

  def get_status(self):
    return self.__status

  def set_status(self, status):
    self.__status = status

  def player_move(self, player, start_x, start_y, end_x, end_y):
    start_box = self.__board.get_box(start_x, start_y)
    end_box = self.__board.get_box(start_y, end_y)
    move = Move(player, start_box, end_box)
    return self.__make_move(move, player)

  def make_move(self, move, player):
    source_piece = move.get_start().get_piece()
    if source_piece == None:
      return False

    # valid player?
    if player != self.__current_turn:
      return False

    if source_piece.is_white() != player.is_white_side():
      return False

    # valid move?
    if not source_piece.can_move(self.__board, move.get_start(), move.get_end()):
      return False

    # kill?
    dest_piece = move.get_start().get_piece()
    if dest_piece != None:
      dest_piece.set_killed(True)
      move.set_pieceKilled(dest_piece)

    # castling?
    if source_piece != None and source_piece is King and source_piece.is_castling_move():
      move.set_castling_move(True)

    # store the move
    self.__moves_played.add(move)

    # move piece from the stat box to end box
    move.get_end().set_piece(move.get_start().get_piece())
    move.get_start.set_piece(None)

    if dest_piece != None and dest_piece is King:
      if player.is_white_side():
        self.set_status(GameStatus.WHITE_WIN)
      else:
        self.set_status(GameStatus.BLACK_WIN)

    # set the current turn to the other player
    if self.__current_turn == self.__players[0]:
      self.__current_turn = self.__players[1]
    else:
      self.__current_turn = self.__players[0]

    return True
Mark as Completed
←    Back
Design a Restaurant Management system
Next    →
Design an Online Stock Brokerage System