Skip to content

Welcome to Tech by Example

Menu
  • Home
  • Posts
  • System Design Questions
Menu

Chess Game System Design

Posted on February 7, 2023February 8, 2023 by admin

Table of Contents

  • Overview
  • Requirements
  • All actors in a game of Chess
  • UML Diagram
    • Piece
    • Cell
    • Move
    • Player
    • Board
    • Game Class
  • Low–Level Diagram
  • Code
  • Conclusion

Overview

In this tutorial, we are going to design a chess game. Chess is a board game that is played between two players.  More information about chess is available here

https://en.wikipedia.org/wiki/Chess

Before we start, please note that this is Low-Level Design Problem. We are going to design a chess game using Object Oriented Principles.

Designing a Chess Game is a very common system design interview question and as with any other system design interview this is what the interviewer is looking for

  • Your knowledge of Object-Oriented Design
  • How do you frame your design in terms of Design Patterns

Also, note that this question is not a distributed system design.

In this tutorial, we will discuss the low-level design of the Chess Game. Along with that, we will also see into the working code for the Chess Game. Here is the table of contents

  • Requirements
  • Actor in the design
  • UML diagram
  • The low-Level design represented in Go Programming Language
  • Working Code

Requirements

The requirement of this tutorial is to design a chess game as per the rules of chess using Object Oriented Design Principles

This article can be referred to know more about the rules of chess

https://en.wikipedia.org/wiki/Rules_of_chess

The next step is to identify all actors in a game of Chess

All actors in a game of Chess

  • Board
  • Cell
  • Player
    • Human Player
    • Computer Player
  • Move
  • Pieces
    • King
    • Queen
    • Bishop
    • Rook
    • Knight
    • Pawn
  • Game
  • Move

Let’s look at each of these actors

  • Board – We have an 8*8 board in a game of chess. So total there are 64 cells
  • Cell – A cell represents one of the cells of 8*8 = 64 cells
  • Player – It represents one of the two players playing the game. A player can be a
    • Human Player –
    • Computer Player
  • Move – It represents a move made by one of the players
  • Pieces– There are different types of pieces in a chess game namely
    • King
    • Queen
    • Bishop
    • Rook
    • Knight
    • Pawn
  • Game –  The game class controls the entire flow of the chess game. It decides which player turn it is, the overall result of the game, etc.
  • Move – It simply represents a move in the game.

Other than the above actors we also have GameStatus Enum that has the below types

  • FirstPlayerWin
  • SecondPlayerWin
  • GameInProgress
  • GameDraw
  • Stalemate

UML Diagram

Below is the UML diagram for the Chess Game

Let’s understand this UML diagram by looking at different components and how each component integrate with other components

Piece

The simplest component in the above design is a Piece. As mentioned above a Piece is of 7 types namely king, queen, rook, bishop, knight, and pawn. The Piece class is an abstract class that has the below information

  • white – it is set to true if the piece is white otherwise false
  • killed – whether a piece is captured or not.

Other than that it also provides a function canMove() which returns true if a given piece can move from its current location on the board to a new location. This function has the below signature

  • canMove(board, location, location)

Cell

The next simplest component is a cell. A cell represents a cell on the board. A cell will have the below components

  • location – It represents the x and y coordinate of the cell on the board
  • piece – The piece which is present on a given cell. It will be nil if the piece is not present at that cell location

Move

It represents a move in a chess game. There are three different moves that can happen in chess

  • Resign
  • Draw Offer
  • Moving a piece from its current cell to a new cell

It has below components that represent either of the above three types of move.

  • Current Location – The current location of the piece
  • New Location – The new location to which a piece has moved to
  • Piece – The piece which got moved
  • Resign – The move is a resign or not
  • Draw Offered – Weather a player offered a draw or not.

Player

There are two different types of player

  • Human Player
  • Computer Player

A player will have below fields or components

  • isWhite – whether the player is playing as a white or black
  • getNextMove – get the next move of the player
  • agreeDraw – whether the player agreed to a draw offer

Board

Next is Board class. It represents the board of the game. It has below components

  • Square – Two-dimensional array of Cell
  • Dimension – The dimension of the board which will be always 8

Game Class

Next is the most important class in the system which is Game class. It has below components

  • Board  – It represents the board
  • First Player  – The first player
  • Second Player  – The second player
  • First Player Turn – Will be true if the current turn is of the first player otherwise false
  • Game Status  – What is the current Game Status
  • Moves  – List of moves that have been made till now

Low–Level Diagram

Here is the low-level diagram in the Go Programming Language

Game

type game struct {
	board           *board
	firstPlayer     iPlayer
	secondPlayer    iPlayer
	firstPlayerTurn bool
	gameStatus      GameStatus
	moves           []move
}

func (this *game) play() error {}

func (g *game) setGameStatus(win bool, whiteWon bool, stalemate bool) {}

func (g *game) printResult() {}

Game Status

type GameStatus uint8

const (
    GameInProgress GameStatus = iota
    GameDraw
    Stalemate
    FirstPlayerWin
    SecondPlayerWin
)

Board

type board struct {
	square    [][]cell
	dimension int
}

func (this *board) printBoard() {}

func (this *board) makeMove(move move) error {}

Cell

type cell struct {
	location location
	piece    piece
}

Move

type move struct {
    currLocation location
    newLocation  location
    piece        piece
    resign       bool
    drawOffer    bool
}

iPlayer Interface

type iPlayer interface {
    isWhite() bool
    getNextMove(board) move
    agreeDraw(board) bool
    getID() int
}

Human Player

type humanPlayer struct {
    white bool
    id    int
}

func (this humanPlayer) getID() int {}

func (this humanPlayer) isWhite() bool {}

func (this humanPlayer) getNextMove(board board) move {}

func (this humanPlayer) agreeDraw(board board) bool {}

Computer Player

type computerPlayer struct {
	white bool
	id    int
}

func (this computerPlayer) isWhite() bool {}

func (this computerPlayer) getID() int {}

func (this computerPlayer) getNextMove(board board) move {}

func (this computerPlayer) agreeDraw(board board) bool {}

Piece

type piece interface {
	canMove(board, location, location) error
	getPieceType() PieceType
	isKilled() bool
	isWhite() bool
}

Piece Type

type PieceType uint8

const (
	King PieceType = iota
	Queen
	Rook
	Bishop
	Knight
	Pawn
)

King Piece

type king struct {
	white  bool
	killed bool
}

func (this *king) canMove(board board, currLocation, newLocation location) error {}

func (this *king) getPieceType() PieceType {}

func (this *king) isKilled() bool {}

func (this *king) isWhite() bool {}

Queen Piece

type queen struct {
	white  bool
	killed bool
}

func (this *queen) canMove(board board, currLocation, newLocation location) error {}

func (this *queen) getPieceType() PieceType {}

func (this *queen) isKilled() bool {}

func (this *queen) isWhite() bool {}

Rook Piece

type rook struct {
	white  bool
	killed bool
}

func (this *rook) canMove(board board, currLocation, newLocation location) error {}

func (this *rook) getPieceType() PieceType {}

func (this *rook) isKilled() bool {}

func (this *rook) isWhite() bool {}

Bishop Piece

type bishop struct {
	white  bool
	killed bool
}

func (this *bishop) canMove(board board, currLocation, newLocation location) error {}

func (this *bishop) getPieceType() PieceType {}

func (this *bishop) isKilled() bool {}

func (this *bishop) isWhite() bool {}

Knight Piece

type knight struct {
	white  bool
	killed bool
}

func (this *knight) canMove(board board, currLocation, newLocation location) error {}

func (this *knight) getPieceType() PieceType {}

func (this *knight) isKilled() bool {}

func (this *knight) isWhite() bool {}

Pawn Piece

type pawn struct {
	white  bool
	killed bool
}

func (this *pawn) canMove(board board, currLocation, newLocation location) error {}

func (this *pawn) getPieceType() PieceType {}

func (this *pawn) isKilled() bool {}

func (this *pawn) isWhite() bool {}

Location

type location struct {
	i int
	j int
}

Code

Let’s look at the code. The below code is not fully working functional code as per the chess algorithm and moves, but it will give you an idea

game.go

package main

import "fmt"

type game struct {
	board           *board
	firstPlayer     iPlayer
	secondPlayer    iPlayer
	firstPlayerTurn bool
	gameStatus      GameStatus
	moves           []move
}

func initGame(b *board, p1, p2 iPlayer) *game {
	game := &game{
		board:           b,
		firstPlayer:     p1,
		secondPlayer:    p2,
		firstPlayerTurn: true,
		gameStatus:      GameInProgress,
	}
	return game
}

func (this *game) play() error {
	var move move
	var err error
	for {
		if this.firstPlayerTurn {
			move = this.firstPlayer.getNextMove(*this.board)
			if move.resign {
				this.setGameStatus(true, true, false)
			}
			if move.drawOffer {
				if this.secondPlayer.agreeDraw(*this.board) {
					this.setGameStatus(false, true, false)
					break
				}
			}
			err := this.board.makeMove(move)
			if err != nil {
				return err
			}
		} else {
			move = this.firstPlayer.getNextMove(*this.board)
			if move.resign {
				this.setGameStatus(true, true, false)
			}
			if move.drawOffer {
				if this.secondPlayer.agreeDraw(*this.board) {
					this.setGameStatus(false, true, false)
					break
				}
			}
			err = this.board.makeMove(move)
			if err != nil {
				return err
			}
		}
		this.moves = append(this.moves, move)
		win, draw, stalemate := this.checkGameStatus()
		this.setGameStatus(win, draw, stalemate)
		if this.gameStatus != GameInProgress {
			break
		}
	}
	return nil
}

func (this *game) checkGameStatus() (win bool, whiteWon bool, stalemate bool) {

	return true, true, true

}

func (g *game) setGameStatus(win bool, whiteWon bool, stalemate bool) {
	if win {
		if whiteWon {
			if g.firstPlayer.isWhite() {
				g.gameStatus = FirstPlayerWin
				return
			} else {
				g.gameStatus = SecondPlayerWin
				return
			}
		}
	}
	if stalemate {
		g.gameStatus = Stalemate
	}
	g.gameStatus = GameDraw
	return
}

func (g *game) printResult() {
	switch g.gameStatus {
	case GameInProgress:
		fmt.Println("Game in Progress")
	case GameDraw:
		fmt.Println("Game Drawn")
	case Stalemate:
		fmt.Println("Stalemate")
	case FirstPlayerWin:
		fmt.Println("First Player Win")
	case SecondPlayerWin:
		fmt.Println("Second Player Win")
	default:
		fmt.Println("Invalid Game Status")
	}
	g.board.printBoard()
}

gamestatus.go

package main

type GameStatus uint8

const (
	GameInProgress GameStatus = iota
	GameDraw
	Stalemate
	FirstPlayerWin
	SecondPlayerWin
)

board.go

package main

type board struct {
	square    [][]cell
	dimension int
}

func (this *board) printBoard() {

}

func (this *board) makeMove(move move) error {
	err := move.piece.canMove(*this, move.currLocation, move.newLocation)
	if err != nil {
		return err
	}

	//Mark the piece at new location as as the current piece
	this.square[move.newLocation.i][move.newLocation.j].piece = move.piece

	//Mark the piece at current location as nil
	this.square[move.currLocation.i][move.currLocation.j].piece = nil
	return nil
}

cell.go

package main

type cell struct {
	location location
	piece    piece
}

move.go

package main

type move struct {
	currLocation location
	newLocation  location
	piece        piece
	resign       bool
	drawOffer    bool
}

iPlayer.go

package main

type iPlayer interface {
	isWhite() bool
	getNextMove(board) move
	agreeDraw(board) bool
	getID() int
}

humanPlayer.go

package main

type humanPlayer struct {
	white bool
	id    int
}

func (this humanPlayer) getID() int {
	return this.id
}

func (this humanPlayer) isWhite() bool {
	return this.white
}

func (this humanPlayer) getNextMove(board board) move {
	currLocation := location{
		i: 1,
		j: 0,
	}

	newLocation := location{
		i: 2,
		j: 0,
	}
	return move{
		currLocation: currLocation,
		piece:        board.square[1][0].piece,
		newLocation:  newLocation,
	}
}

func (this humanPlayer) agreeDraw(board board) bool {
	return false
}

computerPlayer.go

package main

type computerPlayer struct {
	white bool
	id    int
}

func (this computerPlayer) isWhite() bool {
	return this.white
}
func (this computerPlayer) getID() int {
	return this.id
}

func (this computerPlayer) getNextMove(board board) move {
	currLocation := location{
		i: 1,
		j: 0,
	}

	newLocation := location{
		i: 2,
		j: 0,
	}
	return move{
		currLocation: currLocation,
		piece:        board.square[1][0].piece,
		newLocation:  newLocation,
	}
}

func (this computerPlayer) agreeDraw(board board) bool {
	return false
}

piece.go

package main

type piece interface {
	canMove(board, location, location) error
	getPieceType() PieceType
	isKilled() bool
	isWhite() bool
}

pieceType.go

package main

type PieceType uint8

const (
	King PieceType = iota
	Queen
	Rook
	Bishop
	Knight
	Pawn
)

king.go

package main

type king struct {
	white  bool
	killed bool
}

func (this *king) canMove(board board, currLocation, newLocation location) error {
	return nil
}

func (this *king) getPieceType() PieceType {
	return King
}

func (this *king) isKilled() bool {
	return this.killed
}

func (this *king) isWhite() bool {
	return this.white
}

queen.go

package main

type queen struct {
	white  bool
	killed bool
}

func (this *queen) canMove(board board, currLocation, newLocation location) error {
	return nil
}

func (this *queen) getPieceType() PieceType {
	return Queen
}

func (this *queen) isKilled() bool {
	return this.killed
}

func (this *queen) isWhite() bool {
	return this.white
}

rook.go

package main

type rook struct {
	white  bool
	killed bool
}

func (this *rook) canMove(board board, currLocation, newLocation location) error {
	return nil
}

func (this *rook) getPieceType() PieceType {
	return Pawn
}

func (this *rook) isKilled() bool {
	return this.killed
}

func (this *rook) isWhite() bool {
	return this.white
}

bishop.go

package main

type bishop struct {
	white  bool
	killed bool
}

func (this *bishop) canMove(board board, currLocation, newLocation location) error {
	return nil
}

func (this *bishop) getPieceType() PieceType {
	return Pawn
}

func (this *bishop) isKilled() bool {
	return this.killed
}

func (this *bishop) isWhite() bool {
	return this.white
}

knight.go

package main

type knight struct {
	white  bool
	killed bool
}

func (this *knight) canMove(board board, currLocation, newLocation location) error {
	return nil
}

func (this *knight) getPieceType() PieceType {
	return Knight
}

func (this *knight) isKilled() bool {
	return this.killed
}

func (this *knight) isWhite() bool {
	return this.white
}

pawn.go

package main

type pawn struct {
	white  bool
	killed bool
}

func (this *pawn) canMove(board board, currLocation, newLocation location) error {
	return nil
}

func (this *pawn) getPieceType() PieceType {
	return Pawn
}

func (this *pawn) isKilled() bool {
	return this.killed
}

func (this *pawn) isWhite() bool {
	return this.white
}

location.go

package main

type location struct {
	i int
	j int
}

main.go

package main

func main() {

	whiteKing, whiteQueen, whiteRooks, whiteBishops, whiteKnights, whitePawns := makePieces(true)
	blackKing, blackQueen, blackRooks, blackBishops, blackKnights, blackPawns := makePieces(true)

	cells := make([][]cell, 8)

	for i := 0; i < 8; i++ {
		cells[i] = make([]cell, 8)
	}

	//Fill White Pieces in the first row
	cells[0][0] = cell{location: location{i: 0, j: 0}, piece: whiteRooks[0]}
	cells[0][1] = cell{location: location{i: 0, j: 1}, piece: whiteKnights[0]}
	cells[0][2] = cell{location: location{i: 0, j: 2}, piece: whiteBishops[0]}
	cells[0][3] = cell{location: location{i: 0, j: 3}, piece: whiteKing}
	cells[0][4] = cell{location: location{i: 0, j: 4}, piece: whiteQueen}
	cells[0][5] = cell{location: location{i: 0, j: 5}, piece: whiteBishops[1]}
	cells[0][6] = cell{location: location{i: 0, j: 6}, piece: whiteKnights[1]}
	cells[0][7] = cell{location: location{i: 0, j: 7}, piece: whiteRooks[1]}
	//Fill White Pawns in the second row
	for i := 0; i < 8; i++ {
		cells[1][i] = cell{location: location{i: 0, j: 7}, piece: whitePawns[i]}
	}

	//Fill Black Pieces in the first row
	cells[7][0] = cell{location: location{i: 7, j: 0}, piece: blackRooks[0]}
	cells[7][1] = cell{location: location{i: 7, j: 1}, piece: blackKnights[0]}
	cells[7][2] = cell{location: location{i: 7, j: 2}, piece: blackBishops[0]}
	cells[7][3] = cell{location: location{i: 7, j: 3}, piece: blackKing}
	cells[7][4] = cell{location: location{i: 7, j: 4}, piece: blackQueen}
	cells[7][5] = cell{location: location{i: 7, j: 5}, piece: blackBishops[1]}
	cells[7][6] = cell{location: location{i: 7, j: 6}, piece: blackKnights[1]}
	cells[7][7] = cell{location: location{i: 7, j: 7}, piece: blackRooks[1]}
	//Fill Black Pawns in the second row
	for i := 0; i < 8; i++ {
		cells[6][i] = cell{location: location{i: 0, j: 7}, piece: blackPawns[i]}
	}

	board := &board{
		square:    cells,
		dimension: 8,
	}

	player1 := humanPlayer{
		white: true,
		id:    1,
	}

	player2 := computerPlayer{
		white: false,
		id:    1,
	}

	game := initGame(board, player1, player2)
	game.play()
	game.printResult()
}

func makePieces(isWhite bool) (*king, *queen, [2]*rook, [2]*bishop, [2]*knight, [8]*pawn) {

	king := &king{
		white: isWhite,
	}

	queen := &queen{
		white: isWhite,
	}

	rooks := [2]*rook{}
	for i := 0; i < 2; i++ {
		r := &rook{
			white: true,
		}
		rooks[i] = r
	}

	bishops := [2]*bishop{}
	for i := 0; i < 2; i++ {
		b := &bishop{
			white: true,
		}
		bishops[i] = b
	}

	knights := [2]*knight{}
	for i := 0; i < 2; i++ {
		k := &knight{
			white: true,
		}
		knights[i] = k
	}

	pawns := [8]*pawn{}
	for i := 0; i < 8; i++ {
		p := &pawn{
			white: isWhite,
		}
		pawns[i] = p
	}

	return king, queen, rooks, bishops, knights, pawns
}

Conclusion

This is all about the low-level design of the chess game. We hope you have liked this article. Please share feedback in the comments

©2025 Welcome to Tech by Example | Design: Newspaperly WordPress Theme