19
\$\begingroup\$

I'm learning C# and I decided to write a chess program to help me practice the concepts I'm learning in my book. I started the Board class tonight, which is going to handle 1) the board state and 2) checking if moves are legal.

I feel like I need to give careful consideration to how I represent the board. I started with a list of constants:

// CONSTANTS
private const sbyte EMPTYSQUARE = 0;
private const sbyte WKING = 1;
private const sbyte WQUEEN = 2;
private const sbyte WROOK = 3;
private const sbyte WBISHOP = 4;
private const sbyte WKNIGHT = 5;
private const sbyte WPAWN = 6;
private const sbyte BKING = 7;
private const sbyte BQUEEN = 8;
private const sbyte BROOK = 9;
private const sbyte BBISHOP = 10;
private const sbyte BKNIGHT = 11;
private const sbyte BPAWN = 12;

private const sbyte GAMEINPROGRESS = 0;
private const sbyte CHECKMATEWHITE = 1;
private const sbyte CHECKMATEBLACK = 2;
private const sbyte NOLEGALMOVESDRAW = 3;
private const sbyte THREEMOVEDRAW = 4;
private const sbyte FIFTYMOVEDRAW = 5;
private const sbyte WHITERESIGNED = 6;
private const sbyte BLACKRESIGNED = 7;

// VARIABLES
private sbyte[,] board =
{
    {9, 11, 10, 8,  7,  10, 11, 9},
    {12,12, 12, 12, 12, 12, 12,12},
    {0, 0,  0,  0,  0,  0,  0,  0},
    {0, 0,  0,  0,  0,  0,  0,  0},
    {0, 0,  0,  0,  0,  0,  0,  0},
    {0, 0,  0,  0,  0,  0,  0,  0},
    {6, 6,  6,  6,  6,  6,  6,  6},
    {3, 5,  4,  2,  1,  4,  5,  3}
};

Then I realized I could use an enum here, which I hear is good practice so you don't go mixing up your "magic numbers":

private enum Piece
{
    EMPTY, WKING, WQUEEN, WROOK, WBISHOP, WKNIGHT, WPAWN, BKING, BQUEEN, BROOK, BBISHOP, BKNIGHT, BPAWN
}

private enum State
{
    GAMEINPROGRESS, CHECKMATEWHITE, CHECKMATEBLACK, NOLEGALMOVEDRAW, THREEMOVEDRAW, FIFTYMOVEDRAW, WHITERESIGNED, BLACKRESIGNED
}

I then go to declare the board array and I had to use this ugly-looking monstrosity:

private sbyte[,] board =
{
    {(sbyte)Piece.BROOK, (sbyte)Piece.BKNIGHT, (sbyte)Piece.BBISHOP, (sbyte)Piece.BQUEEN, (sbyte)Piece.BKING, (sbyte)Piece.BBISHOP, (sbyte)Piece.BKNIGHT, (sbyte)Piece.BROOK},
    {(sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN, (sbyte)Piece.BPAWN},
    {(sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY},
    {(sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY},
    {(sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY},
    {(sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY, (sbyte)Piece.EMPTY},
    {(sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN, (sbyte)Piece.WPAWN},
    {(sbyte)Piece.WROOK, (sbyte)Piece.WKNIGHT, (sbyte)Piece.WBISHOP, (sbyte)Piece.WQUEEN, (sbyte)Piece.WKING, (sbyte)Piece.WBISHOP, (sbyte)Piece.WKNIGHT, (sbyte)Piece.WROOK}
};

It's really making me second-guess whether or not I want to keep using enum. That's a lot of typing and typecasting just to program in one chess board.

What do you think? Should I go constants or enums?

And while we're at it, what do you think of my choice of data structure for a chess board? Are there faster structures? Are there smaller structures? (I only need to store numbers 0 through 12) Should I enlarge the array to help me compute off-the-board knight moves? Should I be using a different data structure entirely?

\$\endgroup\$
  • 7
    \$\begingroup\$ I'd suggest making a class of Piece and extending that for each type. Then making instances of each piece needed. Each piece type is a pretty significant part of chess. You can make Piece an abstract class with a move method. Then override that in each piece type \$\endgroup\$ – Evorlor Dec 15 '14 at 15:07
  • 4
    \$\begingroup\$ This is crying out for a couple of uses of a Factory design pattern: ChessPieceFactory to make the pieces and ChessBoardFactory to put them in the right places. \$\endgroup\$ – Jon Story Dec 15 '14 at 16:06
  • \$\begingroup\$ One of the reasons Enums were added to Java was to improve on the verbose and type-unsafe static final int idiom. Unfortunately they weren't added until JDK 1.5, so there's a lot of legacy code (i.e. the Java standard library itself) that uses the old way, and people have a habit of copying it. C# added them from the start to avoid this. \$\endgroup\$ – Pharap Dec 15 '14 at 17:37
  • \$\begingroup\$ Did anybody want to give the Factory Design Pattern a shot? I'm curious to see what that would look like, and what the advantages of it are. \$\endgroup\$ – AdmiralThrawn Dec 25 '14 at 0:12
11
\$\begingroup\$

I think it definitely makes sense to use an object hierarchy here. What's the difference between a black and a white pawn? Its colour! (which will affect which way it can move relative to the board).

All pieces must have exactly one colour, and we have two to chose from:

public enum PieceColour
{
    White = 0,
    Black = 1
}

As we've already reasoned, all pieces have to have a colour and unless you've got a defective set, none of the pieces can change colour midway through. Sounds like we have an invariant that we want all pieces to obey. We can lock that up with a base class:

public abstract class Piece
{
    private readonly PieceColour colour;

    public PieceColour Colour { get { return colour; } }

    protected Piece(PieceColour colour)
    {
        this.colour = colour;
    }
}

By making the piece class abstract, we are saying "you can't have a generic Piece, you have to have a specialized subclass". Let's see what that some of those might look like:

public class Pawn : Piece
{
    public Pawn(PieceColour colour)
        : base(colour)
    {
    }
}

public class Rook : Piece
{
    public Rook(PieceColour colour)
        : base(colour)
    {
    }
}

public class Knight : Piece
{
    public Knight(PieceColour colour)
        : base(colour)
    {
    }
}

Now you can start playing around with a board. To be completely honest, I haven't thought enough about it to figure out how I'd really want it to look but something like the following would be enough to get along with. Tuple<int, int> is just a convenient wrapper around two ints (the coordinates of a space).

public class Board
{
    private Dictionary<Tuple<int, int>, Piece> currentState;

    public static Board CreateNewBoard()
    {
        var board = new Board();

        board.currentState = new Dictionary<Tuple<int, int>, Piece> 
        {                      // x, y
            { new Tuple<int, int>(0, 0), new Rook(PieceColour.White) },
            { new Tuple<int, int>(1, 0), new Knight(PieceColour.White) }
            // etc. 
        };

        return board;
    }
}

}

Any common behaviour you should find behind all pieces you should add it to the base class Piece, for example they can all move...

Edit

I thought the above made it clear that I think there should be an abstract method on the piece class that deals with movement. I didn't commit to making a suggestion on the signature of the method because I haven't seen what you're doing currently. I have the impression OP intends on "moving pieces" entirely within the board class.

\$\endgroup\$
  • 4
    \$\begingroup\$ Extracting color to an enum is a good suggestions, but if there is no move method in the Piece class then I'd not use inheritance there, just another enum. I'd really like to see an abstract move method in the Piece class though, so that each piece can handle it's own movement. \$\endgroup\$ – Simon Forsberg Dec 15 '14 at 16:11
  • \$\begingroup\$ That's what I was alluding to in the comment at the bottom. "For example, they can all move..." \$\endgroup\$ – RobH Dec 15 '14 at 16:16
  • 1
    \$\begingroup\$ Yes, I understood that. I just wanted to add that without that method, the class shouldn't be abstract. \$\endgroup\$ – Simon Forsberg Dec 15 '14 at 16:29
7
\$\begingroup\$

Using enums instead of constants is a good fit if you deal with a familiy of something, like you do with Piece.

As we don't know how the remaining code looks like, it is only a guessing what would be the best fit for your case at all.

If your Piece'es also need some logic (properties or methods), you should go with what @Evorlor has commented.

For using the enum with your board you can simply change the datatype of the board

private Piece[,] board =
{
    {Piece.BROOK, Piece.BKNIGHT, Piece.BBISHOP, Piece.BQUEEN, Piece.BKING, Piece.BBISHOP, Piece.BKNIGHT, Piece.BROOK},
    {Piece.BPAWN, Piece.BPAWN, Piece.BPAWN, Piece.BPAWN, Piece.BPAWN, Piece.BPAWN, Piece.BPAWN, Piece.BPAWN},
    {Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY},
    {Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY},
    {Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY},
    {Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY, Piece.EMPTY},
    {Piece.WPAWN, Piece.WPAWN, Piece.WPAWN, Piece.WPAWN, Piece.WPAWN, Piece.WPAWN, Piece.WPAWN, Piece.WPAWN},
    {Piece.WROOK, Piece.WKNIGHT, Piece.WBISHOP, Piece.WQUEEN, Piece.WKING, Piece.WBISHOP, Piece.WKNIGHT, Piece.WROOK}
};

no typecasting is needed.

What you shouldn't do is shortening variable names or enums. Also, using PascalCasing casing, as one should use for public enums, would increase readability IMHO. So e.g. BKNIGHT should be BlackKnight

\$\endgroup\$
  • 3
    \$\begingroup\$ Personally I think the piece colour and piece type should be separate, and then piece itself would be a struct combining both. Good call on the PascalCasing though: msdn.microsoft.com/en-gb/library/4x252001%28v=vs.71%29.aspx \$\endgroup\$ – Pharap Dec 15 '14 at 17:44
  • \$\begingroup\$ Another possible enhancement to this route would be to make it a flags enum, where the first bit is the existence of the piece, the second is the color, and the remaining are the piece. I would never do this, but would favor objects. Still, it might be good to know. \$\endgroup\$ – Magus Dec 15 '14 at 23:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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