Skip to content

How to Enumerate Games via Lightweight Metadata

This guide shows how to efficiently iterate through and display game records in a libscid database using lightweight metadata inspection functions rather than incurring the overhead of materialising full game entities.


1. Overview of Symbols

  • scid_database_game_number_get: Retrieves the ordinal number of a game in the database.
  • scid_database_game_deleted_get: Checks whether a game has been marked as deleted (tombstoned).
  • scid_database_game_tag_get: Queries header tags directly from the database index without loading the game.
  • scid_database_game_date_get: Extracts the date string associated with the game record.
  • scid_database_game_result_get: Retrieves the game result descriptor ("1-0", "0-1", "1/2-1/2", "*").
  • scid_database_game_eco_get: Reads the cached ECO opening code.
  • scid_database_game_halfmove_count_get: Retrieves the total number of plies (half-moves) played.

2. Complete Recipe

#include <scid/scid.h>

#include <stdio.h>
#include <string.h>

static int
check(
    scid_error  error,
    const char* call)
{
    if (error == SCID_OK)
    {
        return 1;
    }

    fprintf(stderr, "%s failed with scid_error %hu\n", call, error);
    return 0;
}


static int
text_equals(
    const char* text,
    size_t      text_size,
    const char* expected)
{
    return text_size == strlen(expected) && strncmp(text, expected, text_size) == 0;
}


static int
add_pgn(
    scid_database* database,
    const char*    pgn,
    const char*    flags)
{
    const char*    start_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
    scid_game*     game = NULL;
    scid_position* position = NULL;
    char           diagnostic[1024];
    size_t         diagnostic_size = 0;

    if (!check(
            scid_position_create_from_fen(start_fen, &position), "scid_position_create_from_fen") ||
        !check(
            scid_game_create(
                position, pgn, strlen(pgn), &game, diagnostic, sizeof(diagnostic),
                &diagnostic_size),
            "scid_game_create"))
    {
        fprintf(stderr, "%.*s\n", (int)diagnostic_size, diagnostic);
        scid_position_free(position);
        scid_game_free(game);
        return 0;
    }

    if (!check(scid_database_game_add(database, game, flags), "scid_database_game_add"))
    {
        scid_position_free(position);
        scid_game_free(game);
        return 0;
    }

    scid_position_free(position);
    scid_game_free(game);
    return 1;
}


static int
print_game_row(
    scid_database* database,
    size_t         index)
{
    char          event[128];
    char          white[128];
    char          black[128];
    char          date[32];
    char          result[16];
    char          eco_text[SCID_ECO_STRING_CAPACITY];
    size_t        number = 0;
    size_t        halfmoves = 0;
    size_t        event_size = 0;
    size_t        white_size = 0;
    size_t        black_size = 0;
    size_t        date_size = 0;
    size_t        result_size = 0;
    size_t        eco_size = 0;
    scid_eco_code eco = SCID_ECO_NONE;
    int           deleted = 0;

    if (!check(
            scid_database_game_number_get(database, index, &number),
            "scid_database_game_number_get") ||
        !check(
            scid_database_game_deleted_get(database, index, &deleted),
            "scid_database_game_deleted_get") ||
        !check(
            scid_database_game_tag_get(database, index, "Event", event, sizeof(event), &event_size),
            "scid_database_game_tag_get") ||
        !check(
            scid_database_game_tag_get(database, index, "White", white, sizeof(white), &white_size),
            "scid_database_game_tag_get") ||
        !check(
            scid_database_game_tag_get(database, index, "Black", black, sizeof(black), &black_size),
            "scid_database_game_tag_get") ||
        !check(
            scid_database_game_date_get(database, index, date, sizeof(date), &date_size),
            "scid_database_game_date_get") ||
        !check(
            scid_database_game_result_get(database, index, result, sizeof(result), &result_size),
            "scid_database_game_result_get") ||
        !check(scid_database_game_eco_get(database, index, &eco), "scid_database_game_eco_get") ||
        !check(
            scid_eco_code_to_string(
                eco, SCID_ECO_FORMAT_BASIC, eco_text, sizeof(eco_text), &eco_size),
            "scid_eco_code_to_string") ||
        !check(
            scid_database_game_halfmove_count_get(database, index, &halfmoves),
            "scid_database_game_halfmove_count_get"))
    {
        return 0;
    }

    printf(
        "%zu | %.*s | %.*s - %.*s | %.*s | %.*s | %.*s | %zu halfmoves | deleted=%d\n", number,
        (int)event_size, event, (int)white_size, white, (int)black_size, black, (int)date_size,
        date, (int)result_size, result, (int)eco_size, eco_text, halfmoves, deleted);

    return 1;
}


int
main(void)
{
    const char*    first_pgn = "[Event \"Archive One\"]\n"
                               "[Site \"Toronto\"]\n"
                               "[Date \"2024.06.14\"]\n"
                               "[White \"Alpha\"]\n"
                               "[Black \"Beta\"]\n"
                               "[Result \"1-0\"]\n"
                               "[ECO \"C20\"]\n"
                               "\n"
                               "1. e4 e5 2. Nf3 1-0\n";
    const char*    second_pgn = "[Event \"Archive Two\"]\n"
                                "[Site \"Vancouver\"]\n"
                                "[Date \"2025.01.02\"]\n"
                                "[White \"Gamma\"]\n"
                                "[Black \"Delta\"]\n"
                                "[Result \"0-1\"]\n"
                                "[ECO \"D00\"]\n"
                                "\n"
                                "1. d4 d5 0-1\n";
    scid_database* database = NULL;
    char           text[128];
    size_t         count = 0;
    size_t         text_size = 0;
    int            deleted = 0;

    if (!check(scid_database_create_memory("listing", &database), "scid_database_create_memory") ||
        !add_pgn(database, first_pgn, "D") || !add_pgn(database, second_pgn, "M") ||
        !check(scid_database_game_count_get(database, &count), "scid_database_game_count_get") ||
        count != 2)
    {
        scid_database_free(database);
        return 1;
    }

    for (size_t index = 0; index < count; ++index)
    {
        if (!print_game_row(database, index))
        {
            scid_database_free(database);
            return 1;
        }
    }


    if (!check(
            scid_database_game_tag_get(database, 0, "Event", text, sizeof(text), &text_size),
            "scid_database_game_tag_get") ||
        !text_equals(text, text_size, "Archive One") ||
        !check(
            scid_database_game_deleted_get(database, 0, &deleted),
            "scid_database_game_deleted_get") ||
        deleted != 1 ||
        !check(
            scid_database_game_tag_get(database, 1, "Event", text, sizeof(text), &text_size),
            "scid_database_game_tag_get") ||
        !text_equals(text, text_size, "Archive Two"))
    {
        scid_database_free(database);
        return 1;
    }

    scid_database_free(database);
    return 0;
}

3. Key Concepts and Patterns

Lightweight Index Queries

When building game list tables or paginating through large collections containing millions of games, calling scid_database_game_get for every row is inefficient. Instead, query the metadata index directly:

for (size_t index = 1; index <= game_count; ++index)
{
    char white[128], black[128], date[32], result[16];
    size_t white_size = 0, black_size = 0, date_size = 0, result_size = 0;
    size_t halfmoves = 0;
    int deleted = 0;

    scid_database_game_deleted_get(database, index, &deleted);
    scid_database_game_tag_get(database, index, "White", white, sizeof(white), &white_size);
    scid_database_game_tag_get(database, index, "Black", black, sizeof(black), &black_size);
    scid_database_game_date_get(database, index, date, sizeof(date), &date_size);
    scid_database_game_result_get(database, index, result, sizeof(result), &result_size);
    scid_database_game_halfmove_count_get(database, index, &halfmoves);

    printf("%zu: %.*s vs %.*s [%.*s] (Plies: %zu)%s\n",
        index, (int)white_size, white, (int)black_size, black,
        (int)result_size, result, halfmoves, deleted ? " [DELETED]" : "");
}