# HG changeset patch # User Mike Becker # Date 1397034724 -7200 # Node ID 866025982aa9e56fea4f445f90534c6fd89cb1a8 # Parent 8a0b85303ee8887ff6ce4f890f69a5b05853ce0b implemented time control diff -r 8a0b85303ee8 -r 866025982aa9 conf.mk --- 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 diff -r 8a0b85303ee8 -r 866025982aa9 src/chess/rules.c --- 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, ¤ttstamp); + 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; + } +} diff -r 8a0b85303ee8 -r 866025982aa9 src/chess/rules.h --- 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 */ diff -r 8a0b85303ee8 -r 866025982aa9 src/game.c --- 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 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(); } diff -r 8a0b85303ee8 -r 866025982aa9 src/main.c --- 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) { diff -r 8a0b85303ee8 -r 866025982aa9 src/network.h --- 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 */