Tue, 28 Jul 2026 12:58:49 +0200
add FEN history to the game state
relates to #872
| src/chess/fen.h | file | annotate | diff | comparison | revisions | |
| src/chess/rules.c | file | annotate | diff | comparison | revisions | |
| src/chess/rules.h | file | annotate | diff | comparison | revisions | |
| src/main.c | file | annotate | diff | comparison | revisions |
--- a/src/chess/fen.h Fri Jul 24 15:35:06 2026 +0200 +++ b/src/chess/fen.h Tue Jul 28 12:58:49 2026 +0200 @@ -48,6 +48,7 @@ * * @param str a buffer large enough to hold the FEN string * @param gamestate the current game state + * @see #FEN_MAX_LENGTH */ void fen_compute(char *str, GameState *gamestate);
--- a/src/chess/rules.c Fri Jul 24 15:35:06 2026 +0200 +++ b/src/chess/rules.c Tue Jul 28 12:58:49 2026 +0200 @@ -34,6 +34,7 @@ #include "bishop.h" #include "queen.h" #include "king.h" +#include "fen.h" #include <string.h> #include <stdio.h> @@ -43,6 +44,7 @@ void gamestate_init(GameState *gamestate) { memset(gamestate, 0, sizeof(GameState)); + // TODO: implement feature - game can be started from arbitrary position Board initboard = { {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, @@ -54,25 +56,39 @@ {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} }; memcpy(gamestate->board, initboard, sizeof(Board)); + char fen[FEN_MAX_LENGTH]; + fen_compute(fen, gamestate); + gamestate->fen_start = strdup(fen); } void gamestate_cleanup(GameState *gamestate) { free(gamestate->moves); + free(gamestate->fen_start); + for (unsigned i = 0 ; i < gamestate->movecount ; i++) { + free(gamestate->fen[i]); + } + free(gamestate->fen); gamestate->movecount = gamestate->movecapacity = 0; } static GameState gamestate_copy_sim(GameState *gamestate) { GameState simulation = *gamestate; - // create a new move list for the simulation + /* create new move and position lists for the simulation */ simulation.movecapacity = 4; simulation.movecount = 0; simulation.moves = malloc(4 * sizeof(Move)); + simulation.fen = malloc(4 * sizeof(char*)); - // copy the most recent move if a move was played + /* copy the most recent move and position if a move was played */ if (gamestate->movecount > 0) { + simulation.fen_start = strdup(gamestate->movecount == 1 ? + gamestate->fen_start : gamestate->fen[gamestate->movecount - 2]); simulation.moves[0] = last_move(gamestate); + simulation.fen[0] = strdup(gamestate->fen[gamestate->movecount - 1]); simulation.movecount++; + } else { + simulation.fen_start = strdup(gamestate->fen_start); } return simulation; @@ -199,25 +215,6 @@ } } -static void addmove(GameState* gamestate, Move *data) { - if (gamestate->movecount == gamestate->movecapacity) { - gamestate->movecapacity += 64; /* 32 more full moves */ - gamestate->moves = realloc(gamestate->moves, - gamestate->movecapacity * sizeof(Move)); - } - - Move *move = &gamestate->moves[gamestate->movecount]; - *move = *data; - - /* only if move has no time info, compute it */ - if (move->movetime.tv_sec == 0 && move->movetime.tv_usec == 0) { - calc_movetime(gamestate, move); - } - - /* important: only "add" the move after calculating the time! */ - gamestate->movecount++; -} - char getpiecechr(uint8_t piece) { switch (piece & PIECE_MASK) { case ROOK: return 'R'; @@ -314,8 +311,31 @@ } } - /* add move, even in simulation (checkmate test needs it) */ - addmove(gamestate, move); + /* add move to the moves array and the new position to the FEN array */ + if (gamestate->movecount == gamestate->movecapacity) { + gamestate->movecapacity += 64; /* 32 more full moves */ + gamestate->moves = realloc(gamestate->moves, + gamestate->movecapacity * sizeof(Move)); + gamestate->fen = realloc(gamestate->fen, + gamestate->movecapacity * sizeof(char*)); + } + + /* copy the move data into the game's move array */ + Move *melem = &gamestate->moves[gamestate->movecount]; + *melem = *move; + + /* calculate the FEN and store it in the FEN array */ + char fen[FEN_MAX_LENGTH]; + fen_compute(fen, gamestate); + gamestate->fen[gamestate->movecount] = strdup(fen); + + /* only if move has no time info, compute it */ + if (melem->movetime.tv_sec == 0 && melem->movetime.tv_usec == 0) { + calc_movetime(gamestate, melem); + } + + /* important: only "add" the move after calculating the time! */ + gamestate->movecount++; } void apply_move(GameState *gamestate, Move *move) {
--- a/src/chess/rules.h Fri Jul 24 15:35:06 2026 +0200 +++ b/src/chess/rules.h Tue Jul 28 12:58:49 2026 +0200 @@ -110,15 +110,21 @@ #define PLAYER_NAME_BUFLEN 32 typedef struct { + /** optional name of the white player - only used for PGN exports */ + char wname[PLAYER_NAME_BUFLEN]; + /** optional name of the black player - only used for PGN exports */ + char bname[PLAYER_NAME_BUFLEN]; GameInfo info; Board board; Move* moves; - /** optional name of the white player */ - char wname[PLAYER_NAME_BUFLEN]; - /** optional name of the black player */ - char bname[PLAYER_NAME_BUFLEN]; + /** starting position in FEN notation */ + char *fen_start; + /** array of subsequent positions in FEN notation. + * The capacity and element count are identical to the moves array. + */ + char **fen; /** capacity of the move array */ - unsigned movecapacity; + unsigned int movecapacity; /** number of (half-)moves (counting BOTH colors) */ unsigned int movecount; /** a premove that shall be evaluated next time it's our turn */
--- a/src/main.c Fri Jul 24 15:35:06 2026 +0200 +++ b/src/main.c Tue Jul 28 12:58:49 2026 +0200 @@ -279,9 +279,11 @@ } static void draw_board(GameState *gamestate, uint8_t perspective) { - char fen[FEN_MAX_LENGTH]; - fen_compute(fen, gamestate); - mvaddstr(0, 0, fen); + if (gamestate->movecount == 0) { + mvaddstr(0, 0, gamestate->fen_start); + } else { + mvaddstr(0, 0, gamestate->fen[gamestate->movecount - 1]); + } for (uint8_t y = 0 ; y < 8 ; y++) { for (uint8_t x = 0 ; x < 8 ; x++) {