Chapter 6

Input & Output Functions

Parsing FEN

The first function in io.c is set_board(). Its job is to take a FEN string and turn it into the complete internal position used by ABC.

void set_board(char *fen) {

FEN, or Forsyth-Edwards Notation, is a compact text representation of a chess position. It describes the pieces on the board as well as the side to move, castling rights, and en passant square. This makes it ideal for communicating positions between a chess GUI, a chess engine, and testing tools.

Clearing the Board

Before reading the new position, ABC first clears every valid square of its 0x88 board:

for (int rank = 0; rank < 8; rank++) {
    for (int file = 0; file < 16; file++) {
        int square = rank * 16 + file;
        if (!(square & 0x88)) board[square] = EMPTY;
    }
}

The two loops cover the entire 8 × 16 structure used by the 0x88 representation. The 0x88 test filters out the unused squares, leaving only the 64 real chessboard squares to be cleared.

This is important because set_board() may be called repeatedly. A new FEN must completely replace the previous position rather than leaving pieces from an earlier position behind.

Clearing the Game State

ABC then resets the parts of the position that are not represented by pieces on the board:

side = NONE;
castle = EMPTY;
enpassant = NONE;

repetition_index = 0;
memset(repetition_table, 0ULL, sizeof(repetition_table));

The side to move, castling rights, and en passant square are cleared before the corresponding fields are read from the FEN string. The repetition information is also reset because a newly loaded position starts a new sequence of positions for repetition detection.

Placing the Pieces

The first field of a FEN string describes the board rank by rank, starting from rank 8 and finishing at rank 1. ABC walks through its 0x88 board in the same order while reading the FEN characters.

if ((*fen >= 'a' && *fen <= 'z') || (*fen >= 'A' && *fen <= 'Z')) {
    if (*fen == 'K') king_square[WHITE] = square;
    else if (*fen == 'k') king_square[BLACK] = square;
    board[square] = char_pieces[*fen];
    *fen++;
}

When ABC encounters a letter, it represents a piece. The character is used directly as an index into the char_pieces lookup table, converting the FEN character into the internal piece encoding used by the engine.

The kings receive one additional treatment. Whenever K or k is encountered, ABC records its square in king_square. This keeps the king locations synchronized with the newly loaded board.

FEN also represents consecutive empty squares using digits. ABC converts the digit into an offset and advances through that many board positions:

if (*fen >= '0' && *fen <= '9') {
    int offset = *fen - '0';
    if (!(board[square])) file--;
    file += offset;
    *fen++;
}

A digit therefore allows the FEN parser to skip several empty squares without having to process each one individually. The / character marks the end of a rank, allowing the parser to continue with the next rank.

Side to Move

After the board description, the next FEN field specifies whose turn it is:

*fen++;
side = (*fen == 'w') ? WHITE : BLACK;
fen += 2;

ABC converts w into WHITE and anything other than w into BLACK. The parser then advances to the castling field.

Castling Rights

The next FEN field describes the castling rights. ABC reads each character and sets the corresponding bit in castle:

while (*fen != ' ') {
    switch(*fen) {
        case 'K': castle |= WKC; break;
        case 'Q': castle |= WQC; break;
        case 'k': castle |= BKC; break;
        case 'q': castle |= BQC; break;
        case '-': break;
    }
    *fen++;
}

Because castling rights are stored as bit flags, multiple rights can be combined into the single castle variable. For example, a FEN containing KQkq enables all four rights, while - means that no castling rights remain.

En Passant Square

The final field handled by this function is the en passant target square:

*fen++;
if (*fen != '-') {
    int file = fen[0] - 'a';
    int rank = 8 - (fen[1] - '0');
    enpassant = rank * 16 + file;
} else {
    enpassant = NONE;
}

If the field contains -, there is no en passant square. Otherwise, ABC converts the algebraic coordinate from the FEN string into its corresponding 0x88 square index.

At the end of set_board(), the FEN string has therefore been transformed into the complete internal representation used by ABC: the board contains the correct pieces, both king locations are known, the side to move is set, castling rights are restored, the en passant square is available, and the repetition state has been reset.

This function forms an important boundary between the outside world and the engine itself. A position arrives as human-readable text, while the rest of ABC can work entirely with its compact internal data structures.

Printing the Board

Once a position has been loaded, ABC needs a simple way to inspect it. The print_board() function provides a human-readable representation of the current board and its associated game state directly in the console.

void print_board() {

Displaying the Board

The function first walks through the same 0x88 board representation used internally by the engine:

for (int rank = 0; rank < 8; rank++) {
    for (int file = 0; file < 16; file++) {
        int square = rank * 16 + file;
        if (file == 0) printf(" %d  ", 8 - rank);
        if (!(square & 0x88)) printf("%c ", ascii_pieces[board[square]]);
    }
}

The 0x88 test again filters out the unused positions of the board representation. Each valid square is converted into a character using the ascii_pieces lookup table, allowing the internal piece values to be displayed as familiar chess pieces.

The rank number is printed at the beginning of each row, while the file letters are printed underneath the board. The result is a compact chessboard that can be read directly from the console.

Displaying Castling Rights

After printing the board, the function extracts the four castling flags from the castle variable:

int K = castle & WKC;
int Q = castle & WQC;
int k = castle & BKC;
int q = castle & BQC;

Each bit is tested independently. If a right is available, its corresponding letter is printed; otherwise a - is displayed. This produces the familiar FEN-style representation such as KQkq or K--q.

Displaying Game State

The function then prints several other important pieces of information:

printf("    Side:     %s\n", (side == WHITE) ? "white": "black");
printf("    Castling:  %c%c%c%c\n", K ? 'K' : '-', Q ? 'Q' : '-', k ? 'k' : '-', q ? 'q' : '-');
printf("    Enpassant:   %s\n", (enpassant == NONE)? "no" : square_to_coords[enpassant]);
printf("    King square: %s\n", square_to_coords[king_square[side]]);
printf("    Hash:        %d\n\n", generate_hash_key());

The side to move is displayed as either white or black. The en passant square is converted from its internal 0x88 index back into algebraic notation using square_to_coords.

The current king square and the position hash are also printed. These values are particularly useful when debugging the engine because they allow the programmer to verify that the internal state matches the position shown on the board.

Printing Moves

The last helper in io.c is print_move(). Its purpose is simple: convert the internal representation of a move into the coordinate notation used by UCI and print it to the console.

void print_move(int source, int target, int promoted) {
    char *src = square_to_coords[source];
    char *dst = square_to_coords[target];
    char prom = promoted_pieces[promoted];
    printf("%s%s%c ", src, dst, prom);
}

Internally, ABC represents a move using numerical square indices. The source and target values therefore need to be converted back into algebraic coordinates before the move can be displayed.

char *src = square_to_coords[source];
char *dst = square_to_coords[target];

The square_to_coords lookup table performs this conversion. A source square such as E2 becomes "e2", while a target square such as E4 becomes "e4".

Promotion requires one additional character. The promoted piece value is converted through the promoted_pieces lookup table:

char prom = promoted_pieces[promoted];

For an ordinary move, this value is simply the character associated with the empty promotion value. For a promotion, it becomes q, r, b, or n, producing the standard UCI representation.

Finally, the three components are printed together:

printf("%s%s%c ", src, dst, prom);

A move such as e2e4 can therefore be produced from the internal source and target squares, while a promotion such as e7e8q additionally includes the promoted piece.

This small function completes the basic input and output helpers in io.c. Positions can be read from FEN, the internal board can be inspected in the console, and individual moves can be converted back into the notation expected by the outside world.