add API for calculating the clock for a specific move + fixes wrong handling of move time and increment in move 1

Thu, 16 Apr 2026 19:29:11 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 16 Apr 2026 19:29:11 +0200
changeset 99
231a79d93c0c
parent 98
9cb41383540f
child 100
685af47592b5

add API for calculating the clock for a specific move + fixes wrong handling of move time and increment in move 1

relates to #815

src/chess/rules.c file | annotate | diff | comparison | revisions
src/chess/rules.h file | annotate | diff | comparison | revisions
--- a/src/chess/rules.c	Sat Apr 04 13:25:47 2026 +0200
+++ b/src/chess/rules.c	Thu Apr 16 19:29:11 2026 +0200
@@ -158,7 +158,7 @@
     move->timestamp.tv_sec = curtimestamp.tv_sec;
     move->timestamp.tv_usec = (int32_t) curtimestamp.tv_usec;
     
-    if (gamestate->movecount > 0) {
+    if (gamestate->movecount > 1) {
         struct movetimeval lasttstamp = last_move(gamestate).timestamp;
         uint64_t sec = curtimestamp.tv_sec - lasttstamp.tv_sec;
         suseconds_t micros;
@@ -172,6 +172,7 @@
         move->movetime.tv_sec = sec;
         move->movetime.tv_usec = (int32_t) micros;
     } else {
+        /* no move time for the first move of both white and black */
         move->movetime.tv_usec = 0;
         move->movetime.tv_sec = 0;
     }
@@ -769,51 +770,49 @@
 
 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
         uint8_t color) {
+    unsigned move_number = gamestate->movecount;
+    if (color == BLACK) {
+        move_number |= 1;
+    } else {
+        move_number = (move_number + 1) & ~1;
+    }
+    return remaining_movetime2(gameinfo, gamestate, move_number);
+}
+
+uint16_t remaining_movetime2(GameInfo *gameinfo, GameState *gamestate,
+        unsigned move_number) {
     if (!gameinfo->timecontrol) {
         return 0;
     }
-    
-    if (gamestate->movecount > 0) {
-        uint16_t time = gameinfo->time;
-        suseconds_t micros = 0;
+
+    unsigned total_time = gameinfo->time;
+    unsigned used_time = 0;
+    suseconds_t micros = 0;
+
+    /* go through all already played moves */
+    unsigned first_move = move_number % 2;
+    unsigned next_move = move_number;
+    if (next_move > gamestate->movecount) next_move = gamestate->movecount;
+    for (unsigned i = first_move ; i < next_move ; i += 2) {
+        used_time += gamestate->moves[i].movetime.tv_sec;
+        micros += gamestate->moves[i].movetime.tv_usec;
+        /* add increments starting with move 2 */
+        if (i > 1) total_time += gameinfo->addtime;
+    }
 
-        for (unsigned i = color == WHITE ? 0 : 1
-                ; i < gamestate->movecount ; i += 2) {
-            time += gameinfo->addtime;
-            
-            if (gamestate->moves[i].movetime.tv_sec >= time) {
-                return 0;
-            }
-            
-            time -= gamestate->moves[i].movetime.tv_sec;
-            micros += gamestate->moves[i].movetime.tv_usec;
-        }
-        
-        time_t sec;
-        Move *lastmove = &last_move(gamestate);
-        if ((lastmove->piece & COLOR_MASK) != color) {
-            struct movetimeval lastmovetstamp = lastmove->timestamp;
-            struct timeval currenttstamp;
-            gettimeofday(&currenttstamp, NULL);
-            micros += currenttstamp.tv_usec - lastmovetstamp.tv_usec;
-            sec = currenttstamp.tv_sec - lastmovetstamp.tv_sec;
-            if (sec >= time) {
-                return 0;
-            }
-            
-            time -= sec;
-        }
-        
-        sec = micros / 1000000;
-        
-        if (sec >= time) {
-            return 0;
-        }
+    /* when the player is currently playing, count down the clock */
+    if (is_game_running(gamestate) && move_number > 1 &&
+            move_number == gamestate->movecount) {
+        struct movetimeval lastmovetstamp =
+            gamestate->moves[move_number - 1].timestamp;
+        struct timeval currenttstamp;
+        gettimeofday(&currenttstamp, NULL);
+        used_time += currenttstamp.tv_sec - lastmovetstamp.tv_sec;
+        micros += currenttstamp.tv_usec - lastmovetstamp.tv_usec;
+    }
 
-        time -= sec;
-        
-        return time;
-    } else {
-        return gameinfo->time;
-    }
+    /* apply the microseconds */
+    used_time += micros / 1000000;
+
+    return used_time >= total_time ? 0 : total_time - used_time;
 }
--- a/src/chess/rules.h	Sat Apr 04 13:25:47 2026 +0200
+++ b/src/chess/rules.h	Thu Apr 16 19:29:11 2026 +0200
@@ -231,6 +231,18 @@
  */
 void apply_move(GameState *gamestate, Move *move);
 
+/**
+ * Returns the remaining time on the clock for the specified
+ * half-move number.
+ *
+ * @param gameinfo the information about the game
+ * @param gamestate the current game state
+ * @param move_number the half-move that is now going to be played
+ * @return the remaining time - if time control is disabled, this function
+ * always returns zero
+ */
+uint16_t remaining_movetime2(GameInfo *gameinfo, GameState *gamestate,
+        unsigned move_number);
 
 /**
  * Returns the remaining time on the clock for the specified player.

mercurial