Enum, constants, or other to represent chess pieces

Posted on

Problem

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?

Solution

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.

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

Leave a Reply

Your email address will not be published. Required fields are marked *