Skip to content

Latest commit

 

History

History
131 lines (109 loc) · 4.99 KB

File metadata and controls

131 lines (109 loc) · 4.99 KB

Fairy-Stockfish DLL / SO (C API)

This describes the native shared library built from src/ffishdll.cpp. It exposes a C ABI, making it easy to consume from C, C++, Rust, Python (ctypes), C#, etc., on both Linux and Windows (MinGW).

What it provides

  • Move generation and legality checking for all variants enabled by Fairy-Stockfish.
  • FEN parsing/validation, SAN conversion, result detection, pockets/hand pieces, variant metadata.
  • Uses the same variant definitions as the engine (variants.ini can be loaded at runtime).
  • No Emscripten; pure native GCC/Clang/MinGW build.

Building

From src/:

# Linux / macOS (produces libffish.so)
make -f Makefile_dll

# Windows with MinGW (produces ffish.dll)
make -f Makefile_dll EXE=ffish.dll

Notes:

  • The makefile adds -DNNUE_EMBEDDING_OFF so it does not embed an NNUE file. If you want NNUE embedded, remove that flag and ensure the .nnue is available.
  • It builds with -DLARGEBOARDS -DPRECOMPUTED_MAGICS -DALLVARS to enable all variants and large boards.

Linking in your project

Linux

g++ example_dll_usage.cpp -L/path/to/src -lffish -Wl,-rpath,/path/to/src -std=c++17 -O2 -o example

Ensure libffish.so is on your runtime path (use rpath as above or set LD_LIBRARY_PATH).

Windows (MinGW)

# after building ffish.dll and libffish.dll.a in src/
g++ example_dll_usage.cpp -Lpath\\to\\src -lffish -std=c++17 -O2 -o example.exe

Ensure ffish.dll is beside the executable or on PATH. Link against the import library libffish.dll.a generated by Makefile_dll.

C API overview

Key exported functions (see src/ffishdll.cpp):

  • Initialization: fsf_init()
  • Board lifecycle: fsf_new_board(variant, fen, is960), fsf_free_board
  • Moves: fsf_legal_moves, fsf_legal_moves_san, fsf_number_legal_moves, fsf_push, fsf_push_san, fsf_pop
  • FEN/SAN: fsf_fen, fsf_san_move, fsf_variation_san
  • State: fsf_is_game_over, fsf_result, fsf_turn, fsf_fullmove_number, fsf_halfmove_clock, fsf_game_ply
  • Material/hand: fsf_has_insufficient_material, fsf_is_insufficient_material, fsf_checked_pieces, fsf_is_check, fsf_is_capture, fsf_pocket
  • Variant/meta: fsf_available_variants, fsf_starting_fen, fsf_captures_to_hand, fsf_validate_fen, fsf_board_variant
  • Options/engine info: fsf_info, fsf_set_option_str/int/bool
  • Memory release for returned strings: fsf_free

All returned const char* strings are heap-allocated; free them with fsf_free.

Minimal usage (see src/example_dll_usage.cpp)

fsf_init();
fsf_board b = fsf_new_board("chess", nullptr, false);
const char* moves = fsf_legal_moves(b);
printf("Legal moves: %s\n", moves);
fsf_free(moves);
fsf_push(b, "e2e4");
const char* fen = fsf_fen(b, false, 0);
printf("FEN: %s\n", fen);
fsf_free(fen);
fsf_free_board(b);

Loading custom variants

Read your variants.ini content into a string and call:

fsf_load_variant_config(iniContent);

This reconfigures the variant list (mirrors the behavior in ffish.js).

Threading

fsf_init() is protected with std::call_once, but board operations themselves are not internally synchronized. Use separate boards per thread or provide your own locking if sharing.

Variant initialization/configuration touches global engine state. In particular, board creation (fsf_new_board) and fsf_load_variant_config are serialized internally; do not assume independent per-thread variant state.

Complete usage example

#include <cstdio>
extern "C" {
  typedef void* fsf_board;
  void fsf_init();
  fsf_board fsf_new_board(const char* variant, const char* fen, bool is960);
  void fsf_free_board(fsf_board);
  const char* fsf_legal_moves(fsf_board);
  const char* fsf_fen(fsf_board, bool showPromoted, int countStarted);
  bool fsf_push(fsf_board, const char* uciMove);
  void fsf_pop(fsf_board);
  int fsf_validate_fen(const char* fen, const char* variant, bool is960);
  const char* fsf_available_variants();
  const char* fsf_starting_fen(const char* variant);
  void fsf_free(const char* p);
}

int main() {
  fsf_init();

  const char* variants = fsf_available_variants();
  std::printf("Variants: %s\n", variants);
  fsf_free(variants);

  const char* start = fsf_starting_fen("chess");
  fsf_board b = fsf_new_board("chess", start, false);
  fsf_free(start);

  const char* moves = fsf_legal_moves(b);
  std::printf("Legal (UCI): %s\n", moves);
  fsf_free(moves);

  fsf_push(b, "e2e4");
  const char* fen = fsf_fen(b, false, 0);
  std::printf("After e2e4: %s\n", fen);
  fsf_free(fen);

  fsf_pop(b);
  fen = fsf_fen(b, false, 0);
  std::printf("After undo: %s\n", fen);
  fsf_free(fen);

  int ok = fsf_validate_fen("8/8/8/8/8/8/8/8 w - - 0 1", "chess", false);
  std::printf("Validate empty-board FEN (expect error): %d\n", ok);

  fsf_free_board(b);
  return 0;
}

Build:

  • Linux: g++ demo.cpp -L/path/to/src -lffish -Wl,-rpath,/path/to/src -std=c++17 -O2 -o demo
  • Windows (MinGW): g++ demo.cpp -Lpath\\to\\src -lffish -std=c++17 -O2 -o demo.exe (ensure ffish.dll is next to the exe or on PATH)