Raúl Benencia


Chess and C, day 2

Yesterday I’ve started a blog post series where about writing a small chess interface in C. I’ve set up the types Color, PieceType, Piece, Square and Board, and talked about the general idea of the game. Finally, I’ve shown some of the logic behind printing and initializing the Board.

One of the problems I’ve talked about yesterday was the fact that I’m hardcoding the initial positions to the inner representation of the board.

Board _pawns(Board b) {
    short white_pawns_row = 1, black_pawns_row = 6, i;

    for (i = 0; i < SIZE; i++)
        b[white_pawns_row][i].piece = new_piece(WHITE, PAWN);

This is quite ugly because I’m tying the initialization to the types and in a future we might find this to be pretty restrictive. Another case where this approach isn’t useful is in the print_board(Board) function. Yesterday what I did was something similar to the following piece of code.

    for (i = 0; i < SIZE; i++) {
        for (j = 0; j < SIZE; j++) {
            print_square(b[i][j]);

Now, let suppose that we want to print the board from the black player perspective. Auch. We might need to code a print_board_backwards(Board) function. Ugly ugly. Sounds like we’re repeating code. So, what do we do? We add a layer of indirection. Let’s introduce the Coord type!

typedef struct {
    char row;
    char col;
} Coord;

Now, after coding some utility functions it’s possible to do something like this:

static Board _kings(Board b) {
    _set_new_piece(b, coord_init("e1"), WHITE, KING);
    _set_new_piece(b, coord_init("e8"), BLACK, KING);

Pretty neat, isn’t it? By the way, yesterday I forgot to make static all the internal functions, so I’ve also fixed that. Anyway, how can this representation help us to, say, use only one function to print the board both forwards and backwards? Well, I’ve implemented the following functions.

/*
 * Returns the next coordinate. Useful for traversing the board forwards.
 */
Coord coord_next(Coord);

/*
 * Returns the previous coordinate. Useful for traversing the board backwards.
 */
Coord coord_prev(Coord);

We’ll see how this functions are used in a minute. Meanwhile, we’ll need to add a new parameter to the board printing function: the side or perspective we will use for the board, i.e. if we are the black player or the white player. Let’s use the already defined Color type! Here is the complete implementation.

void print_board(Board b, Color side) {
    Coord c = _next_coord(coord_null(), side);
    char current_row = coord_get_row(c);

    _print_row_separator();
    while (!coord_is_null(c)) {
        // Print row if it's the first column
        if (_first_column(coord_get_col(c), side))
            printf("%c ", current_row);

        // Print the square
        print_square(board_get_square(b, c));

        // Shall we move forward or backwards?
        c = _next_coord(c, side);

        // If the row changed then we must start a new line
        if (coord_get_row(c) != current_row) {
            printf("|\n");
            _print_row_separator();

            current_row = coord_get_row(c);
        }
    }

    _print_columns(side);
}

The magic of the implementation is in the _next_coord(c, side), which allow us to code the printing function without paying too much attention to the side of the board we’re printing. As you may have guessed, its implementation is quite simple.

static Coord _next_coord(Coord c, Color side)  {
        if (side == WHITE)
            return coord_next(c);
        else
            return coord_prev(c);
}

Finally, here is how the chess board will be printed from a white player perspective.

  +---|---|---|---|---|---|---|---+
8 | R | N | B | Q | K | B | N | R |
  +---|---|---|---|---|---|---|---+
7 | P | P | P | P | P | P | P | P |
  +---|---|---|---|---|---|---|---+
6 |   | / |   | / |   | / |   | / |
  +---|---|---|---|---|---|---|---+
5 | / |   | / |   | / |   | / |   |
  +---|---|---|---|---|---|---|---+
4 |   | / |   | / |   | / |   | / |
  +---|---|---|---|---|---|---|---+
3 | / |   | / |   | / |   | / |   |
  +---|---|---|---|---|---|---|---+
2 | p | p | p | p | p | p | p | p |
  +---|---|---|---|---|---|---|---+
1 | r | n | b | q | k | b | n | r |
  +---|---|---|---|---|---|---|---+
    a   b   c   d   e   f   g   h

And here is the black player perspective.

  +---|---|---|---|---|---|---|---+
1 | r | n | b | k | q | b | n | r |
  +---|---|---|---|---|---|---|---+
2 | p | p | p | p | p | p | p | p |
  +---|---|---|---|---|---|---|---+
3 |   | / |   | / |   | / |   | / |
  +---|---|---|---|---|---|---|---+
4 | / |   | / |   | / |   | / |   |
  +---|---|---|---|---|---|---|---+
5 |   | / |   | / |   | / |   | / |
  +---|---|---|---|---|---|---|---+
6 | / |   | / |   | / |   | / |   |
  +---|---|---|---|---|---|---|---+
7 | P | P | P | P | P | P | P | P |
  +---|---|---|---|---|---|---|---+
8 | R | N | B | K | Q | B | N | R |
  +---|---|---|---|---|---|---|---+
    h   g   f   e   d   c   b   a

Stay tuned for the following blog post of the series! I’ll add a user input system so we can start playing real chess. ;-)