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 ?

10
  • 3
    I don't really understand why you would model the pieces like this at all. When you call MakeMove() which move will it make? I'd say what you really need to start with is modelling the state of the board and see what classes you need for that
    – jk.
    Apr 18, 2012 at 9:03
  • 11
    I think your basic misconception is not to realize that in chess "color" is an really attribute of the player rather than the piece. That's why we say "blacks move" etc., the coloring is there to identify which player owns the piece. A pawn follows the same movement rules and restrictions no matter what color it is the only variation is which direction is "forward". Apr 18, 2012 at 10:49
  • 5
    when you can't figure "when to stop" inheritance, better drop it completely. You can re-introduce inheritance on later design / implementation / maintenance stage, when hierarchy will become obvious to you. I use this approach, works like a charm
    – gnat
    Apr 18, 2012 at 11:05
  • 1
    The more challenging issue is whether to create a sub-class for each piece type or not. The Piece type determines more behavioral aspects than piece color.
    – NoChance
    Apr 18, 2012 at 11:24
  • 3
    If you're tempted to start a design with ABCs (Abstract Base Classes) do us all a favor and don't... The cases where ABCs are actually useful are very rare and even then, it's usually more useful to use an interface instead. Apr 18, 2012 at 17:59

5 Answers 5

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.
12
  • 4
    @Freshblood I think it would be better to have a direction property and use that to move than to use the color property. Color should ideally only be used for rendering, not for logic. Apr 18, 2012 at 9:22
  • 7
    Besides, the pieces do move in the same direction, forward, backward, left, right. The difference is from which side of the board they start. On a road, cars in opposing lanes both move forward.
    – slipset
    Apr 18, 2012 at 9:26
  • 1
    @Blrfl You might be right that the direction property is a boolean. I don't see what's wrong with that. What confused me about the question up until Freshblood's comment above is why he would want to change movement logic based on color. I would say that is more difficult to understand because it makes no sense for color to effect behaviour. If all you want to do is distinguish which player owns which piece you could put them in two separate collections and avoid the need for a property or inheritance. The pieces themselves could be blissfully unaware of which player they belong to. Apr 18, 2012 at 23:11
  • 1
    I think we are looking at the same thing from two different perspectives. If the two sides were red and blue would their behaviour be different to when they are black and white? I would say no, which is the reason I am saying the colour is not the cause of any behavioural differences. It's only a subtle distinction but that is why I would use a direction or maybe a player property instead of a color one in the game logic. Apr 22, 2012 at 8:11
  • 1
    @Freshblood white castle's moves differ from black's - how? Do you mean castling? Both King-side castling and Queen-side castling are 100% symmetrical for Black and White. Apr 28, 2012 at 11:42
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.

1
  • +1 for pointing out that color-specific code is likely a no-no. White and black pawns essentially behave the same way, just like all the other pieces. Apr 28, 2012 at 9:37
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.

1
  • Backwards movement is only illegal in case of pawns. And they don't really have to know whether they're black or white - it's sufficient (and logical) to make them distinguish forwards vs. backwards. The Board class (for example) could be in charge of converting these concepts into -1 or +1 depending on the color of the piece. Apr 28, 2012 at 9:42
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) {}
}
3
  • But who determines what piece gets which move calculator? If you had a piece base class, and had PawnPiece, you could make that assumption. But then at that rate, the piece itself could just implement how it moves, as designed in the original question Apr 18, 2012 at 12:10
  • @WeekendWarrior - "Personally I wouldn't inherit anything from Piece at all". It seems that Piece is responsible for more than simply calculating moves, which is the reason for splitting out movement as a separate concern. Determining which piece gets which calculator would be a matter of dependency injection, e.g. var my_knight = new Piece(new KnightMoveCalculator()) Apr 18, 2012 at 12:46
  • This would be a good candidate for the flyweight pattern. There are 16 pawns on a chessboard and they all share the same behaviour. This behaviour could easily be extracted out into a single flyweight. The same goes for all the other pieces.
    – MattDavey
    Apr 19, 2012 at 9:11
-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.

12
  • 1
    It may not purpose to write chess engine too faster.
    – Freshblood
    Apr 18, 2012 at 11:16
  • 5
    -1. Judgements like "hypothetically it could become a performance issue, so it is bad", are IMHO almost always pure superstition. And inheritance is not just about "extensibility" in the sense you mention it. Different chess rules apply to different pieces, and each kind of piece having a different implementation of a method like WhichMovesArePossible is a reasonable approach.
    – Doc Brown
    Apr 18, 2012 at 13:30
  • 2
    If you don't need extensibility, an approach based on switch/enum is preferable because it's faster than a virtual method dispatch. A chess engine is CPU-heavy, and the operations that are done most often (and therefore efficiency critical) are performing moves and undoing them. Just one level above that comes listing all possible moves. I expect this will also be time critical. The fact is, I have programmed chess engines in C. Even without OOP, low-level optimizations on the board representation were significant. What kind of experience do you have to dismiss mine?
    – Joh
    Apr 18, 2012 at 14:30
  • 2
    @Joh: I won't disrespect your experience, but I am pretty sure that's not what the OP was after. And though low-level optimizations may be significant for several problems, I consider it as a mistake to make them a basis for high-level design decisions - especially when one does not face any performance issues so far. My experience is that Knuth was very right when saying "Premature optimization is the root of all evil."
    – Doc Brown
    Apr 18, 2012 at 16:17
  • 1
    -1 I have to agree with Doc. The fact is, this "engine" the OP is talking about could simply be the validation engine for a 2 player chess game. In that case, thinking about the performance of OOP vs any other style is completely ludicrous, simple validations of moments would take milliseconds even on a low end ARM device. Don't read more into the requirements than is actually stated. Apr 18, 2012 at 18:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.