From cbe16aba8a342deda9c0442cd726db3e7ebfaa81 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 25 Aug 2025 20:10:38 -0400 Subject: [PATCH] Add board to fen function --- binding/python_c_ffi.py | 10 +++++- engine/include/fen.h | 1 + engine/src/fen.c | 74 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/binding/python_c_ffi.py b/binding/python_c_ffi.py index 32bb0bf..0d46501 100644 --- a/binding/python_c_ffi.py +++ b/binding/python_c_ffi.py @@ -62,6 +62,8 @@ FFI_SPEC = { "load_fen": ((C.POINTER(Board), C.c_char_p), C.c_int), "gen_pseudo_moves": ((C.POINTER(Board), C.POINTER(Move), C.c_bool), C.c_int), "apply_move_on_copy": ((C.POINTER(Board), C.POINTER(Board), Move), C.c_bool), + "board_to_fen": ((C.POINTER(Board), C.c_char_p, C.c_size_t), None), + # AI Methods "ai_find_best_move_with_window": ((C.POINTER(Board), C.c_int, C.c_int, C.POINTER(Move)), C.c_int) } @@ -151,7 +153,13 @@ class ChessFFI: def load_fen(self, board, fen_string): return self._c_load_fen(board, fen_string.encode("ascii")) - + + + def board_to_fen(self, board, size=256): + buf = C.create_string_buffer(size) + self._c_board_to_fen(board, buf, size) + return buf.value.decode("ascii") + def gen_pseudo_moves(self, board, captures_only=False, cap=256): buf = (Move * cap)() diff --git a/engine/include/fen.h b/engine/include/fen.h index 1decd7c..b2fa21a 100644 --- a/engine/include/fen.h +++ b/engine/include/fen.h @@ -1 +1,2 @@ int load_fen(); +void board_to_fen(struct Board *board, char *out, size_t out_sz); diff --git a/engine/src/fen.c b/engine/src/fen.c index 2485ca9..6c63450 100644 --- a/engine/src/fen.c +++ b/engine/src/fen.c @@ -10,6 +10,11 @@ int square_index(int file, int rank) { return rank*8 + file; } +static const char PIECE_CH[12] = { + 'P','N','B','R','Q','K', + 'p','n','b','r','q','k' +}; + int char_to_piece_index(char c) { switch (c) { case 'P': return P; case 'N': return N; case 'B': return B; @@ -117,4 +122,73 @@ int load_fen(struct Board *board, const char *fen) { board->occ[BOTH] = board->occ[WHITE] | board->occ[BLACK]; return 0; +} + +void board_to_fen(const struct Board *board, char *out, size_t out_sz) { + char fen[256]; + char *p = fen; + + // 1. Piece placement + for (int rank = 7; rank >= 0; rank--) { + int empty = 0; + for (int file = 0; file < 8; file++) { + int sq = rank * 8 + file; + char piece = 0; + + for (int i = 0; i < 12; i++) { + if (board->pieces[i] & (1ULL << sq)) { + piece = PIECE_CH[i]; + break; + } + } + + if (piece) { + if (empty > 0) { + *p++ = '0' + empty; + empty = 0; + } + *p++ = piece; + } else { + empty++; + } + } + if (empty > 0) *p++ = '0' + empty; + if (rank > 0) *p++ = '/'; + } + + // 2. Side to move + *p++ = ' '; + *p++ = (board->side_to_move == WHITE) ? 'w' : 'b'; + + // 3. Castling rights + *p++ = ' '; + bool any = false; + if (board->castling_rights & CASTLE_WK) { *p++ = 'K'; any = true; } + if (board->castling_rights & CASTLE_WQ) { *p++ = 'Q'; any = true; } + if (board->castling_rights & CASTLE_BK) { *p++ = 'k'; any = true; } + if (board->castling_rights & CASTLE_BQ) { *p++ = 'q'; any = true; } + if (!any) *p++ = '-'; + + // 4. En passant + *p++ = ' '; + if (board->ep_square >= 0) { + int file = board->ep_square % 8; + int rank = board->ep_square / 8; + *p++ = 'a' + file; + *p++ = '1' + rank; + } else { + *p++ = '-'; + } + + // 5. Halfmove clock + p += sprintf(p, " %d", board->halfmove_clock); + + // 6. Fullmove number + p += sprintf(p, " %d", board->fullmove_number); + + *p = '\0'; + + // Copy safely into output buffer + strncpy(out, fen, out_sz); + out[out_sz - 1] = '\0'; } \ No newline at end of file