9

Once upon time ago I asked a question on Stack Overflow about inheritance.

I have said I design chess engine in OOP fashion. So I inherit all my pieces from Piece abstract class but inheritance still goes. Let me show by code

public abstract class Piece
{
    public void MakeMove();
    public void TakeBackMove();
}

public abstract class Pawn: Piece {}

public class WhitePawn :Pawn {}

public class BlackPawn:Pawn {}

Programmers has been found my design a little bit over engineering and suggested to remove colored piece classes and hold piece color as a property member like below.

public abstract class Piece
{
    public Color Color { get; set; }
    public abstract void MakeMove();
    public abstract void TakeBackMove();
}

In this way Piece can know his color. After implementation i have seen my implementation goes like below.

public abstract class Pawn: Piece
{
    public override void MakeMove()
    {
        if (this.Color == Color.White)
        {

        }
        else
        {

        }
    }

    public override void TakeBackMove()
    {
        if (this.Color == Color.White)
        {

        }
        else
        {

        }
    }
}

Now I see that keep color property causing if statements in implementations. It makes me feel we need specific color pieces in inheritance hierarchy.

In such a case would you have classes like WhitePawn, BlackPawn or would you go to design by keeping Color property?

Without seen such a problem how would you want to start design? Keep Color property or having inheritance solution ?

Edit: I want to specify that my example may not completely match real life example. So before try to guess implementation details just concentrate the question.

I actually simply asking if using Color property will cause if statements better to use inheritance ?

Freshblood
  • 1,517

5 Answers5

12

Imagine you design an application which contains vehicles. Do you create something like this?

class BlackVehicle : Vehicle { }
class DarkBlueVehicle : Vehicle { }
class DarkGreenVehicle : Vehicle { }
// hundreds of other classes...

In real life, color is a property of an object (either a car or a chess piece), and must be represented by a property.

Are MakeMove and TakeBackMove different for blacks and whites? Not at all: the rules are the same for both players, which means that those two methods will be exactly the same for blacks and whites, which means that you are adding a condition where you don't need it.

On the other hand, if you have WhitePawn and BlackPawn, I will not be surprised if soon or later you'll write:

var piece = (Pawn)this.CurrentPiece;
if (piece is WhitePawn)
{
}
else // The pice is of type BlackPawn
{
}

or even something like:

public abstract class Pawn
{
    public Color Player
    {
        get
        {
            return this is WhitePawn ? Color.White : Color.Black;
        }
    }
}

// Now imagine doing the same thing for every other piece.
4

What you should ask yourself is why you really need those statements in your Pawn class:

    if (this.Color == Color.White)
    {

    }
    else
    {
    }

I am pretty sure it will be sufficient to have a small set of functions like

public abstract class Piece
{
    bool DoesPieceHaveSameColor(Piece otherPiece) { /*...*/   }

    Color OtherColor{get{/*...*/}}
    // ...
}

in your Piece class and implement all of the functions in Pawn (and the other derivations of Piece) using those functions, without ever having any of those if statements above. Only exception may be the function to determine the move direction for a Pawn by its color, since this is specific to pawns, but in almost all other cases you won't need any color-specific code.

The other interesting question is if you should really have different subclasses for different kinds of pieces at all. That seems reasonable and natural to me, since the rules for the moves for each piece are different and you can surely implement this by using different overloaded functions for each kind of piece.

Of course, one could try to model this in a more generic way, by separating the properties of the pieces from their moving rules, but this may end in an abstract class MovingRule and a subclass hierarchy MovingRulePawn, MovingRuleQueen, ... I would not start it that way, since it could easily lead to another form of over-engineering.

Doc Brown
  • 218,378
2

Inheritance is not as useful when the extension does not affect the object's external capabilities.

The Color property does not affect how a Piece object is used. For example, all pieces could conceivably invoke a moveForward or moveBackwards method (even if the move isn't legal), but the Piece's encapsulated logic uses the Color property to determine the absolute value that represents a forward or backwards movement (-1 or +1 on the "y axis"), as far as your API is concerned, your superclass and subclasses are identical objects (since they all expose the same methods).

Personally, I'd define some constants that represent each piece type, then use a switch statement to branch into private methods that return possible moves for "this" instance.

leo
  • 1,208
0

Personally I wouldn't inherit anything from Piece at all, because I don't think you gain anything from doing that. Presumably a piece doesn't actually care how it moves, only which squares are a valid destination for its next move.

Perhaps you could create a Valid Move Calculator which knows the available movement patterns for a type of piece, and is able to retrieve a list of valid destination squares, for example

interface IMoveCalculator
{
    List<Square> GetValidDestinations();
}

class KnightMoveCalculator : IMoveCalculator;
class QueenMoveCalculator : IMoveCalculator;
class PawnMoveCalculator : IMoveCalculator; 
// etc.

class Piece
{
    IMoveCalculator move_calculator;
public:
    Piece(IMoveCalculator mc) : move_calculator(mc) {}
}
Ben Cottrell
  • 12,133
-2

In this answer, I'm assuming that by "chess engine", the author of the question means "chess AI", not merely a move validation engine.

I think using OOP for pieces and their valid movements is a bad idea for two reasons:

  1. The strength of a chess engine is highly dependent on how fast it can manipulate boards, see e.g. Chess program board representations. Considering the level of optimization this article describes, I don't think a regular function call is affordable. A call to a virtual method would add a level of indirection and incur an even higher performance penalty. The cost of OOP isn't affordable there.
  2. The benefits of OOP such as extensibility are of no use for pieces, as chess isn't likely to get new pieces any time soon. A viable alternative to inheritance-based type hierarchy includes using enums and switch. Very low-tech and C-like, but hard to beat performance-wise.

Moving away from performance considerations, including the color of the piece in the type hierarchy is in my opinion a bad idea. You will find yourself in situations where you need to check if two pieces have different colors, for instance for capturing. Checking this condition is easily done using a property. Doing it using is WhitePiece-tests would be uglier in comparison.

There is also another practical reason for avoiding moving move generation to piece-specific methods, namely that different pieces share common moves, e.g. rooks and queens. In order to avoid duplicate code, you would have to factorize common code into a base method, and have yet another costly call.

That being said, if it's the first chess engine you are writing, I would definitely advise to go with clean programming principles and avoid the optimization tricks described by Crafty's author. Dealing with the specifics of chess rules (castling, promotion, en-passant) and complex AI techniques (hashing boards, alpha-beta cut) is challenging enough.

Joh
  • 479