Design Patterns [1/N] – Strategy Pattern

Problem:

You have Classes agree in all its attributes but differ only in some behaviors.
Like in our Case the Chess Game, all the units differ only in the movement way.

Solution: Strategy Pattern

The best solution to such situations is to use the Strategy Pattern.
The idea is to define the Strategies “ways of movement” independent from the Classes “Chess units”, and make it interchangeable in order to be able to change the strategy in runtime.

StrategyPatternUML


Chess Game

Download source – sourceforge

ChessGame

It is a direct apply to the pattern on the chess game, if you are not familiar with its rules, you’ll find almost all what you need here.

In this example we don’t need to change the behavior in Runtime. “this point will be discussed at the end of the post under Changeable Behavior in Run-time”.
Notes:

  • Not all functions implemented. [IsCheckmate – IsGameEnd]
  • Not all rules applied. [Castling – Promotion]

As shown in the screenshot, the UI consist of: the Board, Current Player Picture, History, and Exit and Reset buttons.
and the board is two picture boxes, One Holds the board background image, and the other I draw the game units on. – just for simplicity –

The Problem here is in the Chess units moving behavior, as each piece has its one legal moves differ from the other pieces.


The Code

Game main Classes:

  • Board Class.
  • Unit Class.
  • Some other classes inherited from Unit for each Game Unit [rook, knight, bishop, queen, king and pawns].

GameMainClassDiagram

Applying the Strategy pattern to solve require adding a new interface “iMoveBehavior” contains one method called “IsLegalMove”, and create a class for each moving behavior that implement the “iMoveBehavior” interface methods.

and add an instance from Class implements “iMoveBehavior” interface inside the Unit Class, so we can set in runtime the moving behavior for each Unit, and able to invoke “IsLegalMove” from that instance.

iMoveBehavior Interface:

interface iMoveBehavior
{
    bool IsLegalMove(int oldRow, int oldColumn, int newRow, int newColumn, bool kill = true);
}

MoveEightDirections” Queen Movement:

class QueenMove:iMoveBehavior
{
public bool IsLegalMove(int oldRow, int oldColumn, int newRow, int newColumn, bool kill)
{
int vDiff = Math.Abs(oldRow - newRow);
int hDiff = Math.Abs(oldColumn - newColumn);

// Diagonal
if (hDiff == vDiff)
if (vDiff != 0)
return true;

// Horizontal
if (hDiff != 0 && vDiff == 0)
return true;

// Vertical
if (vDiff != 0 && hDiff == 0)
return true;

return false;
}
}

and so on for the other movements.

ChessStrategyPatternClassDiagram

All Movement Strategy Classes.

As you see, I have to add some extra private fields and methods for the PawnMove Class, as it require that.

Using The Code

To make it easy on implementation, I give each piece a number called it “Type”

and this number also will determine if the piece either white or black one.

you can find Types Values in “Notes.txt”

Now, lets take a little about Board Class:

image

It holds a 2D array of “Units


private Unit[,] _board = new Unit[8, 8];

Some functions for check players units on the board:

//Check if the board in given row and column is null or not.

public bool IsEmptyCell(int row, int column);

//Check on the type of the unit in given row and column

public bool ExistWhiteUnit(int row, int column);

//Check on the type of the unit in given row and column

public bool ExistBlackUnit(int row, int column);

Function to get 2D array of integer that describe the distribution of the units on the board -for drawing-


public int[,] GetBoard()
{
int[,] types = new int[8, 8];

for (int r = 0; r < 8; r++)

for (int c = 0; c < 8; c++)

if (_board[r, c] == null)

types[r, c] = 0;

else

types[r, c] = _board[r, c].Type;
return types;
}

Move Function and couple of Not Implemented Functions*

public bool Move(int oldRow, int oldColumn, int newRow, int newColumn)
{
//Check if the "Start" and the "Destination" represent a legal movement.
bool kill = _board[newRow, newColumn] != null;
bool legalMove = _board[oldRow, oldColumn].Move(oldRow, oldColumn, newRow, newColumn, kill);

if (!legalMove)
return false;

//Checking that there is no other units on the pass between the "Start" and the "Destination".
//Note: Knights has the ability to jump over the other Chess Units, so I don't check on it
if (_board[oldRow, oldColumn] != null && _board[oldRow, oldColumn].Type%10 != 3)
{
bool cleanPass;
int
startRow = oldRow,
startColumn = oldColumn,
endRow = newRow,
endColumn = newColumn,
numberOfUnits = 0;

while (startColumn != endColumn || startRow != endRow)
{
if (_board[startRow, startColumn] != null)
{
numberOfUnits++;
}
if (startRow > endRow)
startRow--;
if (startRow < endRow) startRow++; if (startColumn > endColumn)
startColumn--;
if (startColumn < endColumn)
startColumn++;
}
if (numberOfUnits == 1)
cleanPass = true;
else
cleanPass = false;

if (!cleanPass)
return false;
}

//Move the Unit after checking that every thing is OK.
_board[newRow, newColumn] = _board[oldRow, oldColumn];
_board[oldRow, oldColumn] = null;
return true;
}
public bool IsGameEnd()
{
 throw new NotImplementedException();
}

public int IsCheckmate(int blackKingRow, int blackKingColumn, int whiteKingRow, int whiteKingColumn)
{
 throw  new NotImplementedException();
}

Unit Class:

image

the most important couple of lines in this Class are:

private iMoveBehavior _movingBehavior;

and inside the Move method:

bool legalMove = _movingBehavior.IsLegalMove(oldRow, oldColumn, newRow, newColumn, kill);

Don’t Forget to leave a comment,  and waiting for your opinions ☺

Advertisements