implemented time control

Wed, 09 Apr 2014 11:12:04 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 09 Apr 2014 11:12:04 +0200
changeset 33
866025982aa9
parent 32
8a0b85303ee8
child 34
c4d4b8a8f902

implemented time control

conf.mk file | annotate | diff | comparison | revisions
src/chess/rules.c file | annotate | diff | comparison | revisions
src/chess/rules.h file | annotate | diff | comparison | revisions
src/game.c file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
--- a/conf.mk	Wed Apr 09 09:34:07 2014 +0200
+++ b/conf.mk	Wed Apr 09 11:12:04 2014 +0200
@@ -35,7 +35,7 @@
 CFLAGS     = -O2 -std=gnu99
 CFLAGS_D   = -g -std=gnu99 -Wall -pedantic
 LD         = gcc
-LDFLAGS    = -lncurses
+LDFLAGS    = -lncurses -lrt
 ARFLAGS    = -r
 MKDIRFLAGS = -p
 RMFLAGS    = -f -R
--- a/src/chess/rules.c	Wed Apr 09 09:34:07 2014 +0200
+++ b/src/chess/rules.c	Wed Apr 09 11:12:04 2014 +0200
@@ -47,10 +47,27 @@
     elem->next = NULL;
     elem->move = *move;
     
+    clock_gettime(CLOCK_REALTIME, &(elem->move.timestamp));
+    
     if (gamestate->lastmove) {
+        struct timespec *lasttstamp = &(gamestate->lastmove->move.timestamp);
+        time_t sec = elem->move.timestamp.tv_sec - lasttstamp->tv_sec;
+        long int nanos;
+        if (elem->move.timestamp.tv_nsec < lasttstamp->tv_nsec) {
+            nanos = 1e9L-(lasttstamp->tv_nsec - elem->move.timestamp.tv_nsec);
+            sec--;
+        } else {
+            nanos = elem->move.timestamp.tv_nsec - lasttstamp->tv_nsec;
+        }
+        
+        elem->move.movetime.tv_sec = sec;
+        elem->move.movetime.tv_nsec = nanos;
+        
         gamestate->lastmove->next = elem;
         gamestate->lastmove = elem;
     } else {
+        elem->move.movetime.tv_nsec = 0;
+        elem->move.movetime.tv_sec = 0;
         gamestate->movelist = gamestate->lastmove = elem;
     }
 }
@@ -524,3 +541,59 @@
         return 0;
     }
 }
+
+uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
+        uint8_t color) {
+    if (!gameinfo->timecontrol) {
+        return 0;
+    }
+    
+    if (gamestate->movelist) {
+        uint16_t time = gameinfo->time;
+        long int nanos = 0;
+        
+        MoveList *movelist = color == WHITE ?
+            gamestate->movelist : gamestate->movelist->next;
+        
+        while (movelist) {
+            time += gameinfo->addtime;
+            
+            struct timespec *movetime = &(movelist->move.movetime);
+            if (movetime->tv_sec >= time) {
+                return 0;
+            }
+            
+            time -= movetime->tv_sec;
+            nanos += movetime->tv_nsec;
+            
+            movelist = movelist->next ? movelist->next->next : NULL;
+        }
+        
+        time_t sec;
+        movelist = gamestate->lastmove;
+        if ((movelist->move.piece & COLOR_MASK) != color) {
+            struct timespec *lastmovetstamp = &(movelist->move.timestamp);
+            struct timespec currenttstamp;
+            clock_gettime(CLOCK_REALTIME, &currenttstamp);
+            nanos += currenttstamp.tv_nsec - lastmovetstamp->tv_nsec;
+            sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
+            if (sec >= time) {
+                return 0;
+            }
+            
+            time -= sec;
+        }
+        
+        sec = nanos / 1e9L;
+        
+        if (sec >= time) {
+            return 0;
+        }
+
+        time -= sec;
+        
+        return time;
+    } else {
+        return gameinfo->time;
+    }
+}
--- a/src/chess/rules.h	Wed Apr 09 09:34:07 2014 +0200
+++ b/src/chess/rules.h	Wed Apr 09 11:12:04 2014 +0200
@@ -264,5 +264,18 @@
  */
 void apply_move(GameState *gamestate, Move *move);
 
+
+/**
+ * Returns the remaining time on the clock for the specified player.
+ *
+ * @param gameinfo the information about the game
+ * @param gamestate the current game state
+ * @param color either BLACK or WHITE
+ * @return the remaining time - if time control is disabled, this function
+ * always returns zero
+ */
+uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
+        uint8_t color);
+
 #endif	/* RULES_H */
 
--- a/src/game.c	Wed Apr 09 09:34:07 2014 +0200
+++ b/src/game.c	Wed Apr 09 11:12:04 2014 +0200
@@ -35,21 +35,36 @@
 #include <inttypes.h>
 
 static const uint8_t boardx = 10, boardy = 10;
+static int inputy = 21; /* should be overridden on game startup */
 
-static void draw_time(GameState *gamestate, GameInfo *gameinfo) {
+static int timecontrol(GameState *gamestate, GameInfo *gameinfo) {
     if (gameinfo->timecontrol) {
-        // TODO: correct time display
-        
-        uint16_t whitem = gameinfo->time / 60;
-        uint16_t whites = gameinfo->time % 60;
-        uint16_t blackm = gameinfo->time / 60;
-        uint16_t blacks = gameinfo->time % 60;
-        
+        uint16_t white = remaining_movetime(gameinfo, gamestate, WHITE);
+        uint16_t black = remaining_movetime(gameinfo, gamestate, BLACK);
         mvprintw(boardy+4, boardx-1,
-            "White time: %4" PRIu16 ":%02" PRIu16, whitem, whites);
+            "White time: %4" PRIu16 ":%02" PRIu16,
+            white / 60, white % 60);
         mvprintw(boardy+5, boardx-1,
-            "Black time: %4" PRIu16 ":%02" PRIu16, blackm, blacks);
+            "Black time: %4" PRIu16 ":%02" PRIu16,
+            black / 60, black % 60);
+    
+        if (white == 0) {
+            move(inputy, 0);
+            printw("Time is over - Black wins!");
+            clrtobot();
+            refresh();
+            return 1;
+        }
+        if (black == 0) {
+            move(inputy, 0);
+            printw("Time is over - White wins!");
+            clrtobot();
+            refresh();
+            return 1;
+        }
     }
+    
+    return 0;
 }
 
 static void draw_board(GameState *gamestate) {
@@ -156,16 +171,18 @@
     size_t bufpos = 0;
     char movestr[buflen];
     
-    int inputy = getmaxy(stdscr) - 6;
+    flushinp();
     while (1) {
-        draw_time(gamestate, gameinfo);
+        if (timecontrol(gamestate, gameinfo)) {
+            return 1;
+        }
+        
         move(inputy, 0);
         printw(
             "Use chess notation to enter your move.\n"
             "Or type 'surr' to surrender or 'remis' to end with remis.\n\n"
             "Type your move: ");
         clrtoeol();
-        refresh();
         
         if (asyncgetnstr(movestr, &bufpos, buflen)) {
             if (strncmp(movestr, "surr", buflen) == 0) {
@@ -210,15 +227,21 @@
     }
 }
 
-static int sendmove(GameState *gamestate, int opponent) {
+static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
     
     const size_t buflen = 8;
+    size_t bufpos = 0;
     char movestr[buflen];
     _Bool remisrejected = FALSE;
     uint8_t code;
     
-    int inputy = getmaxy(stdscr) - 6;
+    flushinp();
     while (1) {
+        if (timecontrol(gamestate, gameinfo)) {
+            net_send_code(opponent, NETCODE_TIMEOVER);
+            return 1;
+        }
+        
         move(inputy, 0);
         if (remisrejected) {
             printw(
@@ -232,68 +255,69 @@
                 "Type your move: ");
         }
         clrtoeol();
-        refresh();
-        getnstr(movestr, buflen);
-
-        if (strncmp(movestr, "surr", buflen) == 0) {
-            printw("You surrendered!");
-            clrtoeol();
-            refresh();
-            net_send_code(opponent, NETCODE_SURRENDER);
-            return 1;
-        } else if (strncmp(movestr, "remis", buflen) == 0) {
-            if (!remisrejected) {
-                net_send_code(opponent, NETCODE_REMIS);
-                printw("Remis offer sent - waiting for acceptance...");
+        
+        if (asyncgetnstr(movestr, &bufpos, buflen)) {
+            if (strncmp(movestr, "surr", buflen) == 0) {
+                printw("You surrendered!");
+                clrtoeol();
                 refresh();
-                if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
-                    printw("\rRemis accepted!");
-                    clrtoeol();
+                net_send_code(opponent, NETCODE_SURRENDER);
+                return 1;
+            } else if (strncmp(movestr, "remis", buflen) == 0) {
+                if (!remisrejected) {
+                    net_send_code(opponent, NETCODE_REMIS);
+                    printw("Remis offer sent - waiting for acceptance...");
                     refresh();
-                    return 1;
-                } else {
-                    remisrejected = TRUE;
-                }
-            }
-        } else {
-            Move move;
-            int eval_result = eval_move(gamestate, movestr, &move);
-            switch (eval_result) {
-            case VALID_MOVE_SYNTAX:
-                net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
-                code = net_recieve_code(opponent);
-                move.check = code == NETCODE_CHECK;
-                gamestate->checkmate = code == NETCODE_CHECKMATE;
-                gamestate->stalemate = code == NETCODE_STALEMATE;
-                if (code == NETCODE_DECLINE) {
-                    printw("Invalid move.");
-                } else {
-                    apply_move(gamestate, &move);
-                    if (gamestate->checkmate) {
-                        printw("Checkmate!");
+                    if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
+                        printw("\rRemis accepted!");
                         clrtoeol();
-                        return 1;
-                    } else if (gamestate->stalemate) {
-                        printw("Stalemate!");
-                        clrtoeol();
+                        refresh();
                         return 1;
                     } else {
-                        return 0;
+                        remisrejected = TRUE;
                     }
                 }
-                break;
-            default:
-                eval_move_failed_msg(eval_result);
+            } else {
+                Move move;
+                int eval_result = eval_move(gamestate, movestr, &move);
+                switch (eval_result) {
+                case VALID_MOVE_SYNTAX:
+                    net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
+                    code = net_recieve_code(opponent);
+                    move.check = code == NETCODE_CHECK;
+                    gamestate->checkmate = code == NETCODE_CHECKMATE;
+                    gamestate->stalemate = code == NETCODE_STALEMATE;
+                    if (code == NETCODE_DECLINE) {
+                        printw("Invalid move.");
+                    } else {
+                        apply_move(gamestate, &move);
+                        if (gamestate->checkmate) {
+                            printw("Checkmate!");
+                            clrtoeol();
+                            return 1;
+                        } else if (gamestate->stalemate) {
+                            printw("Stalemate!");
+                            clrtoeol();
+                            return 1;
+                        } else {
+                            return 0;
+                        }
+                    }
+                    break;
+                default:
+                    eval_move_failed_msg(eval_result);
+                }
+                clrtoeol();
             }
-            clrtoeol();
         }
     }
 }
 
-static int recvmove(GameState *gamestate, int opponent) {
+static int recvmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
     
-    int inputy = getmaxy(stdscr) - 6;
     while (1) {
+        timecontrol(gamestate, gameinfo);
+        
         move(inputy, 0);
         printw("Awaiting opponent move...");
         clrtoeol();
@@ -304,44 +328,48 @@
         
         Move move;
         switch (code) {
-            case NETCODE_SURRENDER:
-                printw("\rYour opponent surrendered!");
+        case NETCODE_TIMEOVER:
+            printw("\rYour opponent's time ran out - you win!");
+            clrtoeol();
+            return 1;
+        case NETCODE_SURRENDER:
+            printw("\rYour opponent surrendered!");
+            clrtoeol();
+            return 1;
+        case NETCODE_REMIS:
+            if (prompt_yesno(
+                "\rYour opponent offers remis - do you accept")) {
+                printw("\rRemis accepted!");
                 clrtoeol();
+                net_send_code(opponent, NETCODE_ACCEPT);
                 return 1;
-            case NETCODE_REMIS:
-                if (prompt_yesno(
-                    "\rYour opponent offers remis - do you accept")) {
-                    printw("\rRemis accepted!");
+            } else {
+                net_send_code(opponent, NETCODE_DECLINE);
+            }
+            break;
+        case NETCODE_MOVE:
+            net_recieve_data(opponent, &move, sizeof(Move));
+            if (validate_move(gamestate, &move)) {
+                apply_move(gamestate, &move);
+                if (move.check) {
+                    net_send_code(opponent, NETCODE_CHECK);
+                } else if (gamestate->checkmate) {
+                    net_send_code(opponent, NETCODE_CHECKMATE);
+                    printw("\rCheckmate!");
                     clrtoeol();
-                    net_send_code(opponent, NETCODE_ACCEPT);
+                    return 1;
+                } else if (gamestate->stalemate) {
+                    net_send_code(opponent, NETCODE_STALEMATE);
+                    printw("\rStalemate!");
+                    clrtoeol();
                     return 1;
                 } else {
-                    net_send_code(opponent, NETCODE_DECLINE);
+                    net_send_code(opponent, NETCODE_ACCEPT);
                 }
-                break;
-            case NETCODE_MOVE:
-                net_recieve_data(opponent, &move, sizeof(Move));
-                if (validate_move(gamestate, &move)) {
-                    apply_move(gamestate, &move);
-                    if (move.check) {
-                        net_send_code(opponent, NETCODE_CHECK);
-                    } else if (gamestate->checkmate) {
-                        net_send_code(opponent, NETCODE_CHECKMATE);
-                        printw("\rCheckmate!");
-                        clrtoeol();
-                        return 1;
-                    } else if (gamestate->stalemate) {
-                        net_send_code(opponent, NETCODE_STALEMATE);
-                        printw("\rStalemate!");
-                        clrtoeol();
-                        return 1;
-                    } else {
-                        net_send_code(opponent, NETCODE_ACCEPT);
-                    }
-                    return 0;
-                } else {
-                    net_send_code(opponent, NETCODE_DECLINE);
-                }
+                return 0;
+            } else {
+                net_send_code(opponent, NETCODE_DECLINE);
+            }
         }
     }
 }
@@ -361,6 +389,8 @@
 }
 
 void game_start_singlemachine(Settings *settings) {
+    inputy = getmaxy(stdscr) - 6;
+    
     GameState gamestate;
     memset(&gamestate, 0, sizeof(GameState));
     init_board(&gamestate);
@@ -385,10 +415,11 @@
 }
 
 void game_start(Settings *settings, int opponent) {
+    inputy = getmaxy(stdscr) - 6;
+    
     _Bool myturn = is_server(settings) ==
         (settings->gameinfo.servercolor == WHITE);
     
-    // TODO: time limit
     GameState gamestate;
     memset(&gamestate, 0, sizeof(GameState));
     init_board(&gamestate);
@@ -399,10 +430,9 @@
         clear();
         draw_board(&gamestate);
         if (myturn) {
-            running = !sendmove(&gamestate, opponent);
+            running = !sendmove(&gamestate, &(settings->gameinfo), opponent);
         } else {
-            running = !recvmove(&gamestate, opponent);
-            flushinp(); // flush any input the user hacked in while waiting
+            running = !recvmove(&gamestate, &(settings->gameinfo), opponent);
         }
         myturn ^= TRUE;
     }  while (running);
@@ -415,5 +445,7 @@
     mvaddstr(getmaxy(stdscr)-1, 0,
         "Game has ended. Press any key to leave...");
     refresh();
+    cbreak();
+    flushinp();
     getch();
 }
--- a/src/main.c	Wed Apr 09 09:34:07 2014 +0200
+++ b/src/main.c	Wed Apr 09 11:12:04 2014 +0200
@@ -116,7 +116,7 @@
     printw("Game details\n");
     attroff(A_UNDERLINE);
     printw("  Server:     %s\n  Client:     %s\n",
-        serverwhite?"white":"black", serverwhite?"black":"White"
+        serverwhite?"White":"Black", serverwhite?"Black":"White"
     );
     if (gameinfo->timecontrol) {
         if (gameinfo->time % 60) {
--- a/src/network.h	Wed Apr 09 09:34:07 2014 +0200
+++ b/src/network.h	Wed Apr 09 11:12:04 2014 +0200
@@ -46,8 +46,9 @@
 #define NETCODE_CHECK 0x23
 #define NETCODE_CHECKMATE 0x24
 #define NETCODE_STALEMATE 0x25
+#define NETCODE_TIMEOVER 0x26
 
-#define NETCODE_VERSION 10
+#define NETCODE_VERSION 11
 
 typedef struct {
     int fd; /* -1, if we are the client */

mercurial