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
     1.1 --- a/conf.mk	Wed Apr 09 09:34:07 2014 +0200
     1.2 +++ b/conf.mk	Wed Apr 09 11:12:04 2014 +0200
     1.3 @@ -35,7 +35,7 @@
     1.4  CFLAGS     = -O2 -std=gnu99
     1.5  CFLAGS_D   = -g -std=gnu99 -Wall -pedantic
     1.6  LD         = gcc
     1.7 -LDFLAGS    = -lncurses
     1.8 +LDFLAGS    = -lncurses -lrt
     1.9  ARFLAGS    = -r
    1.10  MKDIRFLAGS = -p
    1.11  RMFLAGS    = -f -R
     2.1 --- a/src/chess/rules.c	Wed Apr 09 09:34:07 2014 +0200
     2.2 +++ b/src/chess/rules.c	Wed Apr 09 11:12:04 2014 +0200
     2.3 @@ -47,10 +47,27 @@
     2.4      elem->next = NULL;
     2.5      elem->move = *move;
     2.6      
     2.7 +    clock_gettime(CLOCK_REALTIME, &(elem->move.timestamp));
     2.8 +    
     2.9      if (gamestate->lastmove) {
    2.10 +        struct timespec *lasttstamp = &(gamestate->lastmove->move.timestamp);
    2.11 +        time_t sec = elem->move.timestamp.tv_sec - lasttstamp->tv_sec;
    2.12 +        long int nanos;
    2.13 +        if (elem->move.timestamp.tv_nsec < lasttstamp->tv_nsec) {
    2.14 +            nanos = 1e9L-(lasttstamp->tv_nsec - elem->move.timestamp.tv_nsec);
    2.15 +            sec--;
    2.16 +        } else {
    2.17 +            nanos = elem->move.timestamp.tv_nsec - lasttstamp->tv_nsec;
    2.18 +        }
    2.19 +        
    2.20 +        elem->move.movetime.tv_sec = sec;
    2.21 +        elem->move.movetime.tv_nsec = nanos;
    2.22 +        
    2.23          gamestate->lastmove->next = elem;
    2.24          gamestate->lastmove = elem;
    2.25      } else {
    2.26 +        elem->move.movetime.tv_nsec = 0;
    2.27 +        elem->move.movetime.tv_sec = 0;
    2.28          gamestate->movelist = gamestate->lastmove = elem;
    2.29      }
    2.30  }
    2.31 @@ -524,3 +541,59 @@
    2.32          return 0;
    2.33      }
    2.34  }
    2.35 +
    2.36 +uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
    2.37 +        uint8_t color) {
    2.38 +    if (!gameinfo->timecontrol) {
    2.39 +        return 0;
    2.40 +    }
    2.41 +    
    2.42 +    if (gamestate->movelist) {
    2.43 +        uint16_t time = gameinfo->time;
    2.44 +        long int nanos = 0;
    2.45 +        
    2.46 +        MoveList *movelist = color == WHITE ?
    2.47 +            gamestate->movelist : gamestate->movelist->next;
    2.48 +        
    2.49 +        while (movelist) {
    2.50 +            time += gameinfo->addtime;
    2.51 +            
    2.52 +            struct timespec *movetime = &(movelist->move.movetime);
    2.53 +            if (movetime->tv_sec >= time) {
    2.54 +                return 0;
    2.55 +            }
    2.56 +            
    2.57 +            time -= movetime->tv_sec;
    2.58 +            nanos += movetime->tv_nsec;
    2.59 +            
    2.60 +            movelist = movelist->next ? movelist->next->next : NULL;
    2.61 +        }
    2.62 +        
    2.63 +        time_t sec;
    2.64 +        movelist = gamestate->lastmove;
    2.65 +        if ((movelist->move.piece & COLOR_MASK) != color) {
    2.66 +            struct timespec *lastmovetstamp = &(movelist->move.timestamp);
    2.67 +            struct timespec currenttstamp;
    2.68 +            clock_gettime(CLOCK_REALTIME, &currenttstamp);
    2.69 +            nanos += currenttstamp.tv_nsec - lastmovetstamp->tv_nsec;
    2.70 +            sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
    2.71 +            if (sec >= time) {
    2.72 +                return 0;
    2.73 +            }
    2.74 +            
    2.75 +            time -= sec;
    2.76 +        }
    2.77 +        
    2.78 +        sec = nanos / 1e9L;
    2.79 +        
    2.80 +        if (sec >= time) {
    2.81 +            return 0;
    2.82 +        }
    2.83 +
    2.84 +        time -= sec;
    2.85 +        
    2.86 +        return time;
    2.87 +    } else {
    2.88 +        return gameinfo->time;
    2.89 +    }
    2.90 +}
     3.1 --- a/src/chess/rules.h	Wed Apr 09 09:34:07 2014 +0200
     3.2 +++ b/src/chess/rules.h	Wed Apr 09 11:12:04 2014 +0200
     3.3 @@ -264,5 +264,18 @@
     3.4   */
     3.5  void apply_move(GameState *gamestate, Move *move);
     3.6  
     3.7 +
     3.8 +/**
     3.9 + * Returns the remaining time on the clock for the specified player.
    3.10 + *
    3.11 + * @param gameinfo the information about the game
    3.12 + * @param gamestate the current game state
    3.13 + * @param color either BLACK or WHITE
    3.14 + * @return the remaining time - if time control is disabled, this function
    3.15 + * always returns zero
    3.16 + */
    3.17 +uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
    3.18 +        uint8_t color);
    3.19 +
    3.20  #endif	/* RULES_H */
    3.21  
     4.1 --- a/src/game.c	Wed Apr 09 09:34:07 2014 +0200
     4.2 +++ b/src/game.c	Wed Apr 09 11:12:04 2014 +0200
     4.3 @@ -35,21 +35,36 @@
     4.4  #include <inttypes.h>
     4.5  
     4.6  static const uint8_t boardx = 10, boardy = 10;
     4.7 +static int inputy = 21; /* should be overridden on game startup */
     4.8  
     4.9 -static void draw_time(GameState *gamestate, GameInfo *gameinfo) {
    4.10 +static int timecontrol(GameState *gamestate, GameInfo *gameinfo) {
    4.11      if (gameinfo->timecontrol) {
    4.12 -        // TODO: correct time display
    4.13 -        
    4.14 -        uint16_t whitem = gameinfo->time / 60;
    4.15 -        uint16_t whites = gameinfo->time % 60;
    4.16 -        uint16_t blackm = gameinfo->time / 60;
    4.17 -        uint16_t blacks = gameinfo->time % 60;
    4.18 -        
    4.19 +        uint16_t white = remaining_movetime(gameinfo, gamestate, WHITE);
    4.20 +        uint16_t black = remaining_movetime(gameinfo, gamestate, BLACK);
    4.21          mvprintw(boardy+4, boardx-1,
    4.22 -            "White time: %4" PRIu16 ":%02" PRIu16, whitem, whites);
    4.23 +            "White time: %4" PRIu16 ":%02" PRIu16,
    4.24 +            white / 60, white % 60);
    4.25          mvprintw(boardy+5, boardx-1,
    4.26 -            "Black time: %4" PRIu16 ":%02" PRIu16, blackm, blacks);
    4.27 +            "Black time: %4" PRIu16 ":%02" PRIu16,
    4.28 +            black / 60, black % 60);
    4.29 +    
    4.30 +        if (white == 0) {
    4.31 +            move(inputy, 0);
    4.32 +            printw("Time is over - Black wins!");
    4.33 +            clrtobot();
    4.34 +            refresh();
    4.35 +            return 1;
    4.36 +        }
    4.37 +        if (black == 0) {
    4.38 +            move(inputy, 0);
    4.39 +            printw("Time is over - White wins!");
    4.40 +            clrtobot();
    4.41 +            refresh();
    4.42 +            return 1;
    4.43 +        }
    4.44      }
    4.45 +    
    4.46 +    return 0;
    4.47  }
    4.48  
    4.49  static void draw_board(GameState *gamestate) {
    4.50 @@ -156,16 +171,18 @@
    4.51      size_t bufpos = 0;
    4.52      char movestr[buflen];
    4.53      
    4.54 -    int inputy = getmaxy(stdscr) - 6;
    4.55 +    flushinp();
    4.56      while (1) {
    4.57 -        draw_time(gamestate, gameinfo);
    4.58 +        if (timecontrol(gamestate, gameinfo)) {
    4.59 +            return 1;
    4.60 +        }
    4.61 +        
    4.62          move(inputy, 0);
    4.63          printw(
    4.64              "Use chess notation to enter your move.\n"
    4.65              "Or type 'surr' to surrender or 'remis' to end with remis.\n\n"
    4.66              "Type your move: ");
    4.67          clrtoeol();
    4.68 -        refresh();
    4.69          
    4.70          if (asyncgetnstr(movestr, &bufpos, buflen)) {
    4.71              if (strncmp(movestr, "surr", buflen) == 0) {
    4.72 @@ -210,15 +227,21 @@
    4.73      }
    4.74  }
    4.75  
    4.76 -static int sendmove(GameState *gamestate, int opponent) {
    4.77 +static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
    4.78      
    4.79      const size_t buflen = 8;
    4.80 +    size_t bufpos = 0;
    4.81      char movestr[buflen];
    4.82      _Bool remisrejected = FALSE;
    4.83      uint8_t code;
    4.84      
    4.85 -    int inputy = getmaxy(stdscr) - 6;
    4.86 +    flushinp();
    4.87      while (1) {
    4.88 +        if (timecontrol(gamestate, gameinfo)) {
    4.89 +            net_send_code(opponent, NETCODE_TIMEOVER);
    4.90 +            return 1;
    4.91 +        }
    4.92 +        
    4.93          move(inputy, 0);
    4.94          if (remisrejected) {
    4.95              printw(
    4.96 @@ -232,68 +255,69 @@
    4.97                  "Type your move: ");
    4.98          }
    4.99          clrtoeol();
   4.100 -        refresh();
   4.101 -        getnstr(movestr, buflen);
   4.102 -
   4.103 -        if (strncmp(movestr, "surr", buflen) == 0) {
   4.104 -            printw("You surrendered!");
   4.105 -            clrtoeol();
   4.106 -            refresh();
   4.107 -            net_send_code(opponent, NETCODE_SURRENDER);
   4.108 -            return 1;
   4.109 -        } else if (strncmp(movestr, "remis", buflen) == 0) {
   4.110 -            if (!remisrejected) {
   4.111 -                net_send_code(opponent, NETCODE_REMIS);
   4.112 -                printw("Remis offer sent - waiting for acceptance...");
   4.113 +        
   4.114 +        if (asyncgetnstr(movestr, &bufpos, buflen)) {
   4.115 +            if (strncmp(movestr, "surr", buflen) == 0) {
   4.116 +                printw("You surrendered!");
   4.117 +                clrtoeol();
   4.118                  refresh();
   4.119 -                if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   4.120 -                    printw("\rRemis accepted!");
   4.121 -                    clrtoeol();
   4.122 +                net_send_code(opponent, NETCODE_SURRENDER);
   4.123 +                return 1;
   4.124 +            } else if (strncmp(movestr, "remis", buflen) == 0) {
   4.125 +                if (!remisrejected) {
   4.126 +                    net_send_code(opponent, NETCODE_REMIS);
   4.127 +                    printw("Remis offer sent - waiting for acceptance...");
   4.128                      refresh();
   4.129 -                    return 1;
   4.130 -                } else {
   4.131 -                    remisrejected = TRUE;
   4.132 -                }
   4.133 -            }
   4.134 -        } else {
   4.135 -            Move move;
   4.136 -            int eval_result = eval_move(gamestate, movestr, &move);
   4.137 -            switch (eval_result) {
   4.138 -            case VALID_MOVE_SYNTAX:
   4.139 -                net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
   4.140 -                code = net_recieve_code(opponent);
   4.141 -                move.check = code == NETCODE_CHECK;
   4.142 -                gamestate->checkmate = code == NETCODE_CHECKMATE;
   4.143 -                gamestate->stalemate = code == NETCODE_STALEMATE;
   4.144 -                if (code == NETCODE_DECLINE) {
   4.145 -                    printw("Invalid move.");
   4.146 -                } else {
   4.147 -                    apply_move(gamestate, &move);
   4.148 -                    if (gamestate->checkmate) {
   4.149 -                        printw("Checkmate!");
   4.150 +                    if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   4.151 +                        printw("\rRemis accepted!");
   4.152                          clrtoeol();
   4.153 -                        return 1;
   4.154 -                    } else if (gamestate->stalemate) {
   4.155 -                        printw("Stalemate!");
   4.156 -                        clrtoeol();
   4.157 +                        refresh();
   4.158                          return 1;
   4.159                      } else {
   4.160 -                        return 0;
   4.161 +                        remisrejected = TRUE;
   4.162                      }
   4.163                  }
   4.164 -                break;
   4.165 -            default:
   4.166 -                eval_move_failed_msg(eval_result);
   4.167 +            } else {
   4.168 +                Move move;
   4.169 +                int eval_result = eval_move(gamestate, movestr, &move);
   4.170 +                switch (eval_result) {
   4.171 +                case VALID_MOVE_SYNTAX:
   4.172 +                    net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
   4.173 +                    code = net_recieve_code(opponent);
   4.174 +                    move.check = code == NETCODE_CHECK;
   4.175 +                    gamestate->checkmate = code == NETCODE_CHECKMATE;
   4.176 +                    gamestate->stalemate = code == NETCODE_STALEMATE;
   4.177 +                    if (code == NETCODE_DECLINE) {
   4.178 +                        printw("Invalid move.");
   4.179 +                    } else {
   4.180 +                        apply_move(gamestate, &move);
   4.181 +                        if (gamestate->checkmate) {
   4.182 +                            printw("Checkmate!");
   4.183 +                            clrtoeol();
   4.184 +                            return 1;
   4.185 +                        } else if (gamestate->stalemate) {
   4.186 +                            printw("Stalemate!");
   4.187 +                            clrtoeol();
   4.188 +                            return 1;
   4.189 +                        } else {
   4.190 +                            return 0;
   4.191 +                        }
   4.192 +                    }
   4.193 +                    break;
   4.194 +                default:
   4.195 +                    eval_move_failed_msg(eval_result);
   4.196 +                }
   4.197 +                clrtoeol();
   4.198              }
   4.199 -            clrtoeol();
   4.200          }
   4.201      }
   4.202  }
   4.203  
   4.204 -static int recvmove(GameState *gamestate, int opponent) {
   4.205 +static int recvmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
   4.206      
   4.207 -    int inputy = getmaxy(stdscr) - 6;
   4.208      while (1) {
   4.209 +        timecontrol(gamestate, gameinfo);
   4.210 +        
   4.211          move(inputy, 0);
   4.212          printw("Awaiting opponent move...");
   4.213          clrtoeol();
   4.214 @@ -304,44 +328,48 @@
   4.215          
   4.216          Move move;
   4.217          switch (code) {
   4.218 -            case NETCODE_SURRENDER:
   4.219 -                printw("\rYour opponent surrendered!");
   4.220 +        case NETCODE_TIMEOVER:
   4.221 +            printw("\rYour opponent's time ran out - you win!");
   4.222 +            clrtoeol();
   4.223 +            return 1;
   4.224 +        case NETCODE_SURRENDER:
   4.225 +            printw("\rYour opponent surrendered!");
   4.226 +            clrtoeol();
   4.227 +            return 1;
   4.228 +        case NETCODE_REMIS:
   4.229 +            if (prompt_yesno(
   4.230 +                "\rYour opponent offers remis - do you accept")) {
   4.231 +                printw("\rRemis accepted!");
   4.232                  clrtoeol();
   4.233 +                net_send_code(opponent, NETCODE_ACCEPT);
   4.234                  return 1;
   4.235 -            case NETCODE_REMIS:
   4.236 -                if (prompt_yesno(
   4.237 -                    "\rYour opponent offers remis - do you accept")) {
   4.238 -                    printw("\rRemis accepted!");
   4.239 +            } else {
   4.240 +                net_send_code(opponent, NETCODE_DECLINE);
   4.241 +            }
   4.242 +            break;
   4.243 +        case NETCODE_MOVE:
   4.244 +            net_recieve_data(opponent, &move, sizeof(Move));
   4.245 +            if (validate_move(gamestate, &move)) {
   4.246 +                apply_move(gamestate, &move);
   4.247 +                if (move.check) {
   4.248 +                    net_send_code(opponent, NETCODE_CHECK);
   4.249 +                } else if (gamestate->checkmate) {
   4.250 +                    net_send_code(opponent, NETCODE_CHECKMATE);
   4.251 +                    printw("\rCheckmate!");
   4.252                      clrtoeol();
   4.253 -                    net_send_code(opponent, NETCODE_ACCEPT);
   4.254 +                    return 1;
   4.255 +                } else if (gamestate->stalemate) {
   4.256 +                    net_send_code(opponent, NETCODE_STALEMATE);
   4.257 +                    printw("\rStalemate!");
   4.258 +                    clrtoeol();
   4.259                      return 1;
   4.260                  } else {
   4.261 -                    net_send_code(opponent, NETCODE_DECLINE);
   4.262 +                    net_send_code(opponent, NETCODE_ACCEPT);
   4.263                  }
   4.264 -                break;
   4.265 -            case NETCODE_MOVE:
   4.266 -                net_recieve_data(opponent, &move, sizeof(Move));
   4.267 -                if (validate_move(gamestate, &move)) {
   4.268 -                    apply_move(gamestate, &move);
   4.269 -                    if (move.check) {
   4.270 -                        net_send_code(opponent, NETCODE_CHECK);
   4.271 -                    } else if (gamestate->checkmate) {
   4.272 -                        net_send_code(opponent, NETCODE_CHECKMATE);
   4.273 -                        printw("\rCheckmate!");
   4.274 -                        clrtoeol();
   4.275 -                        return 1;
   4.276 -                    } else if (gamestate->stalemate) {
   4.277 -                        net_send_code(opponent, NETCODE_STALEMATE);
   4.278 -                        printw("\rStalemate!");
   4.279 -                        clrtoeol();
   4.280 -                        return 1;
   4.281 -                    } else {
   4.282 -                        net_send_code(opponent, NETCODE_ACCEPT);
   4.283 -                    }
   4.284 -                    return 0;
   4.285 -                } else {
   4.286 -                    net_send_code(opponent, NETCODE_DECLINE);
   4.287 -                }
   4.288 +                return 0;
   4.289 +            } else {
   4.290 +                net_send_code(opponent, NETCODE_DECLINE);
   4.291 +            }
   4.292          }
   4.293      }
   4.294  }
   4.295 @@ -361,6 +389,8 @@
   4.296  }
   4.297  
   4.298  void game_start_singlemachine(Settings *settings) {
   4.299 +    inputy = getmaxy(stdscr) - 6;
   4.300 +    
   4.301      GameState gamestate;
   4.302      memset(&gamestate, 0, sizeof(GameState));
   4.303      init_board(&gamestate);
   4.304 @@ -385,10 +415,11 @@
   4.305  }
   4.306  
   4.307  void game_start(Settings *settings, int opponent) {
   4.308 +    inputy = getmaxy(stdscr) - 6;
   4.309 +    
   4.310      _Bool myturn = is_server(settings) ==
   4.311          (settings->gameinfo.servercolor == WHITE);
   4.312      
   4.313 -    // TODO: time limit
   4.314      GameState gamestate;
   4.315      memset(&gamestate, 0, sizeof(GameState));
   4.316      init_board(&gamestate);
   4.317 @@ -399,10 +430,9 @@
   4.318          clear();
   4.319          draw_board(&gamestate);
   4.320          if (myturn) {
   4.321 -            running = !sendmove(&gamestate, opponent);
   4.322 +            running = !sendmove(&gamestate, &(settings->gameinfo), opponent);
   4.323          } else {
   4.324 -            running = !recvmove(&gamestate, opponent);
   4.325 -            flushinp(); // flush any input the user hacked in while waiting
   4.326 +            running = !recvmove(&gamestate, &(settings->gameinfo), opponent);
   4.327          }
   4.328          myturn ^= TRUE;
   4.329      }  while (running);
   4.330 @@ -415,5 +445,7 @@
   4.331      mvaddstr(getmaxy(stdscr)-1, 0,
   4.332          "Game has ended. Press any key to leave...");
   4.333      refresh();
   4.334 +    cbreak();
   4.335 +    flushinp();
   4.336      getch();
   4.337  }
     5.1 --- a/src/main.c	Wed Apr 09 09:34:07 2014 +0200
     5.2 +++ b/src/main.c	Wed Apr 09 11:12:04 2014 +0200
     5.3 @@ -116,7 +116,7 @@
     5.4      printw("Game details\n");
     5.5      attroff(A_UNDERLINE);
     5.6      printw("  Server:     %s\n  Client:     %s\n",
     5.7 -        serverwhite?"white":"black", serverwhite?"black":"White"
     5.8 +        serverwhite?"White":"Black", serverwhite?"Black":"White"
     5.9      );
    5.10      if (gameinfo->timecontrol) {
    5.11          if (gameinfo->time % 60) {
     6.1 --- a/src/network.h	Wed Apr 09 09:34:07 2014 +0200
     6.2 +++ b/src/network.h	Wed Apr 09 11:12:04 2014 +0200
     6.3 @@ -46,8 +46,9 @@
     6.4  #define NETCODE_CHECK 0x23
     6.5  #define NETCODE_CHECKMATE 0x24
     6.6  #define NETCODE_STALEMATE 0x25
     6.7 +#define NETCODE_TIMEOVER 0x26
     6.8  
     6.9 -#define NETCODE_VERSION 10
    6.10 +#define NETCODE_VERSION 11
    6.11  
    6.12  typedef struct {
    6.13      int fd; /* -1, if we are the client */

mercurial