Search code examples
oopdesign-patternschess

What pattern do I use for interdependent class


I have read from other sources that it isn't a good idea for objects to know about each other especially the ones on same level. It should be more like hierarchy.

My problem is quite unique as I haven't figured out a way around it. Also I have been unlucky to come across any topic that addresses my issue specifically.

The problem I am building a chess app and I am constructing the model of the app. Right now I have an abstract objects like for example Piece which other pieces like Queen, Knight and the rest would inherit from. I also have a Board class which handles all board model and state of a game. Now each of my piece has a generateMove() method to calculate possible moves from their position and to do this they need to know the state of the board. Also the Pieces are been instantiated by Board at startup.

Question Do I go ahead and instantiate Pieces by e.g

public class ChessBoard{
   Boardbit = 64bit
   Knight = new Knight(start_position, this) 

  //calculate

}

and then in Knight class method

public long generateMove(ChessBoard);

If no, what other ways can I go about it?


Solution

  • Making Chessboard to know the Knight and vise versa is not elegant. I agree with you in this point. Kind of sticking with the 'Tell don't ask' rule forces the 'higher level' element, in this case the chessboard, to tell the piece to move while providing all the required information. The Chessboard itself doesn't know which piece is moving (in this case of predicting possible moves for all pieces), but for sure never knows any details about how a piece can move or is allowed to move. This is just one possible solution using sort of Strategy Pattern. (The Visitor or some similar pattern could also be used here):

    Main() {
        chessboard = new Chessboard()
        PiecesCollection = new PiecesCollection(new Knight(KnightStrategy, Color.Black))
        chessboard.AddPieces(PiecesCollection)
    
        CollectionOfAllPossibleMoveCollections = chessBoard.CallculateAllPossibleMoves()
    
        Move selectedMove = ShowOrSelectMove(CollectionOfAllPossibleMoveCollections)
        chessboard.ExecuteMove(selectedMove)
    }
    
    public class Chessboard{
       // fields
       PiecesCollectionWhite
       // where 'PiecesCollectionWhite' is a collection of `Piece`
    
       PiecesCollectionBlack
       // where 'PiecesCollectionBlack' is a collection of `Piece`
    
       CurrentlyVisitedPositionsCollection
       // where 'CurrentlyVisitedPositionsCollection' is a collection of `Position`
    
       // methods
       AddPieces(PiecesCollection, Color)
    
       CallculateAllPossibleMoves(Color) {
         CollectionOfPossibleMoveCollections = 
             FOREACH Piece IN PiecesCollection OF Color
                 DO Piece.CalculateMoves(this.CurrentlyVisitedPositionsCollection)
         return CollectionOfAllPossibleMoveCollections // where 'CollectionOfAllPossibleMoveCollections ' is a collection that holds a collection of `Move` of which each nested collection represents the possible moves of a chess piece.
       }
    
       ExecuteMove(Move) {
           RemovePieceFromBoardIfNecessary(Move.ToPosition)
       }
    } 
    
    public class Piece 
    {
       // fields
       Strategy
       Position
       Color
    
       // methods
       CallculateMoves(CurrentlyVisitedPositionsCollection) {
           PossibleMovesCollection = this.Strategy.Execute(CurrentlyVisitedPositionsCollection, this.Position)
           return PossibleMovesCollection where `PossibleMovesCollection` is a collection of `Move`
       }
    }
    
    public class Knight extends Piece
    {
       ctor(Strategy, Color)
    }
    
    public class Stragtegy
    {
      abstract Execute(currentPosition, allPiecesPositions) : PossibleMovesCollection 
    }
    
    public class KnightStrategy extends Strategy
    {
      Execute(currentPosition, allPiecesPositions) {
          PossibleMovesCollection = ApplyKnightMoveAlgorithm()
          return PossibleMovesCollection 
      }
    
      private ApplyKnightMoveAlgorithm() : PossibleMovesCollection 
    }
    
    public class Move
    {
      Position fromPosition
      Position toPosition
    }
    
    public class Color
    {
      Black
      White
    }
    
    public class Position
    {
      Color
      xCoordinate
      yCoordinate
    }
    

    This is just a sketch and not a complete example. There is some state information or operations missing. E.g. maybe you would have to store the Color that was moved last on the chessboard.

    Since the Chessboard returns all possible moves (information about all currently visited coordinates) you can easily enhance the algorithm by implementing some intelligence to predict the best possible moves from this information. So before the controller or in this case the Main() will call Chessboard.ExecuteMove(Move) it could make a call to PredictionEngine.PredictBestMove(CollectionOfAllPossibleMoveCollections).