add FEN history to the game state default tip

Tue, 28 Jul 2026 12:58:49 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 28 Jul 2026 12:58:49 +0200
changeset 157
07cbfc477b22
parent 156
05fc8a454700

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++) {

mercurial