5

I am implementing a chess module as I learn C++. In the module I have (partially) implemented:

  • a Board class (grid, pieces, move methods etc.)
  • enumerated types Colour (BLACK/WHITE) and Rank (KING, QUEEN, ..., PAWN)

and

  • a Piece structure grouping colour and rank (BLACK KING,..., WHITE PAWN).

I am now deciding how to represent the contents of a square on the board (grid). A square must either contain a Piece (BLACK KING), or nothing. Options considered:

  • create a struct Square containing two data members, {bool occupied ; Piece piece;}, (possibly extending the Piece class).

    MY OBJECTION: Solution seems a little too heavy-weight, and in the event that the square is not occupied then the piece member should be empty. I feel it may lead to exceptions being needed for some class methods, which should not be necessary.

  • extending the enumeration of Rank or Colour to include an empty option.

    MY OBJECTION: Feels semantically inelegant, hacky and unnatural.

  • looking into other packages like std::optional

    MY OBJECTION: For coding style and simplicity, I'd like to avoid the use of extra machinery. Would std::optional even be suitable?

  • using NULL or nullptr to represent the state of an empty square

    MY OBJECTION: Again, seems hacky. The contents of an empty square are NOT NULL, nor the number 0, should not be comparable to the number 26... but should be a fresh, new constant EMPTY_SQUARE.

None of these options quite seem to fit. Is there some more elegant way of extending a class (like Piece) with other members (like EMPTY_SQUARE) which has none of the original class's data members? (Are there any other options?)

Community
  • 1
  • 1
Owen
  • 448
  • 3
  • 11
  • Related: https://stackoverflow.com/questions/2537942/nullable-values-in-c and https://stackoverflow.com/questions/7645578/best-way-to-represent-nullable-member-in-c – Cody Gray - on strike Feb 10 '19 at 07:49
  • 1
    Why not create an interface class to represent the square content, and derive either a `Piece` or `Empty` class to define the content. Then your `Square` struct should hold a pointer to the interface. You can check which "child" class exist in the square later – NirMH Feb 10 '19 at 07:50
  • 2
    Use `std::optional`, it's exactly the right tool. I don't understand your objection about "extra machinery" to that approach. – Ulrich Eckhardt Feb 10 '19 at 08:52
  • Related : [another chess app question](https://stackoverflow.com/questions/54543496/) trying to solve a similar problem. – Remy Lebeau Feb 10 '19 at 08:53
  • This sounds pretty opinion based. Personally I would go for `nullptr` because either a square contains a piece or it doesn't. So it either points to a piece object or it doesn't. It is an easy test and feels unsurprising. I don't consider it "hackey" at all. – Galik Feb 10 '19 at 10:23
  • @NirMH are you suggesting to have a Square {...} class with a (public?) data member like bool contains_piece, and a derived class Occupied_square : Square {...}? Will this create typecasting issues? ie. When a piece is moved from a square, the class of that square (`Occupied_square`) will be demoted to just a plain old `Square`. – Owen Feb 14 '19 at 09:13

4 Answers4

4

A square on your chess board can either contain a piece or be empty. In more abstract terms you want to have either some specific thing or nothing at all. That’s exactly the use case std::optional is made for. You cannot avoid extra machinery anyway because you need something to represent an empty square. In 2019-style C++ an optional type is a perfectly simple and idiomatic solution, especially when it comes from the standard library.

Regarding your other alternatives:

  • struct Square: I agree with your objections, especially the question about what to put into the piece member when the square is empty. In short: struct Square is a naive, super-simple version of an optional.
  • Extending the enums: I agree completely.
  • nullptr: Sometimes a null pointer can be an appropriate representation of “nothing”. Usually you run into the issue when your implementation works with pointers anyway and you don’t want to introduce std::optional<T*> – that would introduce an additional nothing-ish state along with its ambiguity. In general I do agree about avoiding null pointers wherever possible.
besc
  • 2,507
  • 13
  • 10
0

How about keeping a collection of none-empty squares?

typedef std::map<square,piece,chessboard_order,chessboard_allocator> chess_position;

Custome allocator is needed because no more than 64 squares are possible. Moreover the collection shrinks as the game proceeds, because pieces are taken.

Red.Wave
  • 2,790
  • 11
  • 17
0

How about each figure have a position? And store a list of all the figures on the board.

The board would have the logic associated with verifying the rules of movement.

Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
  • You mean something like storing 16 white and 16 black figures , each with 8 pawns (like ,...), 2 bishops () etc. etc.? That would be an interesting way to design the implementation, but not really in the scope of the question. – Owen Feb 14 '19 at 07:42
  • As I see it, you're having a problem with representing an empty state. This way, the problem is non-existent. – Robert Andrzejuk Feb 14 '19 at 10:12
0

Another way is to introduce a Null_Piece (Null Object Pattern)

What you will then need to do is create Base class Piece and classes which will implement the behaviors of the pieces Pawn, Knight, Bishop, Rook, Queen, and King.

NullPiece will implement the same behaviors, but instead there will be no action.

This way every Square on the board can have a piece.

Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31