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. ;-)