src/game.c

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
permissions
-rw-r--r--

implemented time control

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2014 Mike Becker. All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  *
    28  */
    30 #include "game.h"
    31 #include "network.h"
    32 #include "input.h"
    33 #include <ncurses.h>
    34 #include <string.h>
    35 #include <inttypes.h>
    37 static const uint8_t boardx = 10, boardy = 10;
    38 static int inputy = 21; /* should be overridden on game startup */
    40 static int timecontrol(GameState *gamestate, GameInfo *gameinfo) {
    41     if (gameinfo->timecontrol) {
    42         uint16_t white = remaining_movetime(gameinfo, gamestate, WHITE);
    43         uint16_t black = remaining_movetime(gameinfo, gamestate, BLACK);
    44         mvprintw(boardy+4, boardx-1,
    45             "White time: %4" PRIu16 ":%02" PRIu16,
    46             white / 60, white % 60);
    47         mvprintw(boardy+5, boardx-1,
    48             "Black time: %4" PRIu16 ":%02" PRIu16,
    49             black / 60, black % 60);
    51         if (white == 0) {
    52             move(inputy, 0);
    53             printw("Time is over - Black wins!");
    54             clrtobot();
    55             refresh();
    56             return 1;
    57         }
    58         if (black == 0) {
    59             move(inputy, 0);
    60             printw("Time is over - White wins!");
    61             clrtobot();
    62             refresh();
    63             return 1;
    64         }
    65     }
    67     return 0;
    68 }
    70 static void draw_board(GameState *gamestate) {
    71     for (uint8_t y = 0 ; y < 8 ; y++) {
    72         for (uint8_t x = 0 ; x < 8 ; x++) {
    73             uint8_t col = gamestate->board[y][x] & COLOR_MASK;
    74             uint8_t piece = gamestate->board[y][x] & PIECE_MASK;
    75             char piecec;
    76             if (piece) {
    77                 piecec = piece == PAWN ? 'P' : getpiecechr(piece);
    78             } else {
    79                 piecec = ' ';
    80             }
    82             attrset((col == WHITE ? A_BOLD : A_DIM) |
    83                 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW));
    85             int cy = gamestate->mycolor == WHITE ? boardy-y : boardy-7+y;
    86             int cx = gamestate->mycolor == WHITE ? boardx+x*3 : boardx+21-x*3;
    87             mvaddch(cy, cx, ' ');
    88             mvaddch(cy, cx+1, piecec);
    89             mvaddch(cy, cx+2, ' ');
    90         }
    91     }
    93     attrset(A_NORMAL);
    94     for (uint8_t i = 0 ; i < 8 ; i++) {
    95         int x = gamestate->mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
    96         int y = gamestate->mycolor == WHITE ? boardy-i : boardy-7+i;
    97         mvaddch(boardy+1, x, 'a'+i);
    98         mvaddch(y, boardx-2, '1'+i);
    99     }
   101     /* move log */
   102     // TODO: introduce window to avoid bugs with a long move log
   103     uint8_t logy = 0;
   104     const uint8_t logx = boardx + 30;
   105     int logi = 1;
   106     MoveList *logelem = gamestate->movelist;
   108     while (logelem) {
   109         logi++;
   110         if (logi % 2 == 0) {
   111             if ((logi - 2) % 4 == 0) {
   112                 logy++;
   113                 move(logy, logx);
   114             }
   115             printw("%d. ", logi / 2);
   116         }
   118         if (logelem) {
   119             Move move = logelem->move;
   120             if ((move.piece&PIECE_MASK) == KING &&
   121                 abs(move.tofile-move.fromfile) == 2) {
   122                 addstr(move.tofile==fileidx('c')?"O-O-O":"O-O");
   123             } else {
   124                 char logstr[] = {
   125                     getpiecechr(move.piece),
   126                     filechr(move.fromfile), rowchr(move.fromrow),
   127                     move.capture ? 'x':'\0',
   128                     filechr(move.tofile), rowchr(move.torow),
   129                     move.check ? '+' : (move.promotion ? '=' : '\0'),
   130                     move.promotion ? getpiecechr(move.promotion) : '\0'
   131                 };
   132                 for (int stri = 0 ; stri < sizeof(logstr) ; stri++) {
   133                     if (logstr[stri]) {
   134                         addch(logstr[stri]);
   135                     }
   136                 }
   137             }
   138             if (!logelem->next) {
   139                 if (gamestate->checkmate) {
   140                     addstr("\b#");
   141                 } else if (gamestate->stalemate) {
   142                     addstr(" stalemate");
   143                 }
   144             }
   145             addch(' ');
   147             logelem = logelem->next;
   148         }
   149     }
   150 }
   152 static void eval_move_failed_msg(int code) {
   153     switch (code) {
   154     case AMBIGUOUS_MOVE:
   155         printw("Ambiguous move - please specify the piece to move.");
   156         break;
   157     case INVALID_POSITION:
   158         printw("Cannot find the piece that shall be moved.");
   159         break;
   160     case NEED_PROMOTION:
   161         printw("You need to promote the pawn (append \"=Q\" e.g.)!");
   162         break;
   163     default:
   164         printw("Can't interpret move - please use algebraic notation.");
   165     }
   166 }
   168 static int domove_singlemachine(GameState *gamestate, GameInfo *gameinfo) {
   170     const size_t buflen = 8;
   171     size_t bufpos = 0;
   172     char movestr[buflen];
   174     flushinp();
   175     while (1) {
   176         if (timecontrol(gamestate, gameinfo)) {
   177             return 1;
   178         }
   180         move(inputy, 0);
   181         printw(
   182             "Use chess notation to enter your move.\n"
   183             "Or type 'surr' to surrender or 'remis' to end with remis.\n\n"
   184             "Type your move: ");
   185         clrtoeol();
   187         if (asyncgetnstr(movestr, &bufpos, buflen)) {
   188             if (strncmp(movestr, "surr", buflen) == 0) {
   189                 printw("%s surrendered!",
   190                     gamestate->mycolor==WHITE?"White":"Black");
   191                 clrtoeol();
   192                 refresh();
   193                 return 1;
   194             } else if (strncmp(movestr, "remis", buflen) == 0) {
   195                 printw("Game ends remis.");
   196                 clrtoeol();
   197                 refresh();
   198                 return 1;
   199             } else {
   200                 Move move;
   201                 int eval_result = eval_move(gamestate, movestr, &move);
   202                 switch (eval_result) {
   203                 case VALID_MOVE_SYNTAX:
   204                     if (validate_move(gamestate, &move)) {
   205                         apply_move(gamestate, &move);
   206                         if (gamestate->checkmate) {
   207                             printw("Checkmate!");
   208                             clrtoeol();
   209                             return 1;
   210                         } else if (gamestate->stalemate) {
   211                             printw("Stalemate!");
   212                             clrtoeol();
   213                             return 1;
   214                         } else {
   215                             return 0;
   216                         }
   217                     } else {
   218                         printw("Invalid move.");
   219                     }
   220                     break;
   221                 default:
   222                     eval_move_failed_msg(eval_result);
   223                 }
   224                 clrtoeol();
   225             }
   226         }
   227     }
   228 }
   230 static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
   232     const size_t buflen = 8;
   233     size_t bufpos = 0;
   234     char movestr[buflen];
   235     _Bool remisrejected = FALSE;
   236     uint8_t code;
   238     flushinp();
   239     while (1) {
   240         if (timecontrol(gamestate, gameinfo)) {
   241             net_send_code(opponent, NETCODE_TIMEOVER);
   242             return 1;
   243         }
   245         move(inputy, 0);
   246         if (remisrejected) {
   247             printw(
   248                 "Use chess notation to enter your move.\n"
   249                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   250                 "Type your move: ");
   251         } else {
   252             printw(
   253                 "Use chess notation to enter your move.\n"
   254                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   255                 "Type your move: ");
   256         }
   257         clrtoeol();
   259         if (asyncgetnstr(movestr, &bufpos, buflen)) {
   260             if (strncmp(movestr, "surr", buflen) == 0) {
   261                 printw("You surrendered!");
   262                 clrtoeol();
   263                 refresh();
   264                 net_send_code(opponent, NETCODE_SURRENDER);
   265                 return 1;
   266             } else if (strncmp(movestr, "remis", buflen) == 0) {
   267                 if (!remisrejected) {
   268                     net_send_code(opponent, NETCODE_REMIS);
   269                     printw("Remis offer sent - waiting for acceptance...");
   270                     refresh();
   271                     if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   272                         printw("\rRemis accepted!");
   273                         clrtoeol();
   274                         refresh();
   275                         return 1;
   276                     } else {
   277                         remisrejected = TRUE;
   278                     }
   279                 }
   280             } else {
   281                 Move move;
   282                 int eval_result = eval_move(gamestate, movestr, &move);
   283                 switch (eval_result) {
   284                 case VALID_MOVE_SYNTAX:
   285                     net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
   286                     code = net_recieve_code(opponent);
   287                     move.check = code == NETCODE_CHECK;
   288                     gamestate->checkmate = code == NETCODE_CHECKMATE;
   289                     gamestate->stalemate = code == NETCODE_STALEMATE;
   290                     if (code == NETCODE_DECLINE) {
   291                         printw("Invalid move.");
   292                     } else {
   293                         apply_move(gamestate, &move);
   294                         if (gamestate->checkmate) {
   295                             printw("Checkmate!");
   296                             clrtoeol();
   297                             return 1;
   298                         } else if (gamestate->stalemate) {
   299                             printw("Stalemate!");
   300                             clrtoeol();
   301                             return 1;
   302                         } else {
   303                             return 0;
   304                         }
   305                     }
   306                     break;
   307                 default:
   308                     eval_move_failed_msg(eval_result);
   309                 }
   310                 clrtoeol();
   311             }
   312         }
   313     }
   314 }
   316 static int recvmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
   318     while (1) {
   319         timecontrol(gamestate, gameinfo);
   321         move(inputy, 0);
   322         printw("Awaiting opponent move...");
   323         clrtoeol();
   324         refresh();
   326         // TODO: nonblocking
   327         uint32_t code = net_recieve_code(opponent);
   329         Move move;
   330         switch (code) {
   331         case NETCODE_TIMEOVER:
   332             printw("\rYour opponent's time ran out - you win!");
   333             clrtoeol();
   334             return 1;
   335         case NETCODE_SURRENDER:
   336             printw("\rYour opponent surrendered!");
   337             clrtoeol();
   338             return 1;
   339         case NETCODE_REMIS:
   340             if (prompt_yesno(
   341                 "\rYour opponent offers remis - do you accept")) {
   342                 printw("\rRemis accepted!");
   343                 clrtoeol();
   344                 net_send_code(opponent, NETCODE_ACCEPT);
   345                 return 1;
   346             } else {
   347                 net_send_code(opponent, NETCODE_DECLINE);
   348             }
   349             break;
   350         case NETCODE_MOVE:
   351             net_recieve_data(opponent, &move, sizeof(Move));
   352             if (validate_move(gamestate, &move)) {
   353                 apply_move(gamestate, &move);
   354                 if (move.check) {
   355                     net_send_code(opponent, NETCODE_CHECK);
   356                 } else if (gamestate->checkmate) {
   357                     net_send_code(opponent, NETCODE_CHECKMATE);
   358                     printw("\rCheckmate!");
   359                     clrtoeol();
   360                     return 1;
   361                 } else if (gamestate->stalemate) {
   362                     net_send_code(opponent, NETCODE_STALEMATE);
   363                     printw("\rStalemate!");
   364                     clrtoeol();
   365                     return 1;
   366                 } else {
   367                     net_send_code(opponent, NETCODE_ACCEPT);
   368                 }
   369                 return 0;
   370             } else {
   371                 net_send_code(opponent, NETCODE_DECLINE);
   372             }
   373         }
   374     }
   375 }
   377 static void init_board(GameState *gamestate) {
   378     Board initboard = {
   379         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   380         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   381         {0,     0,       0,       0,      0,     0,       0,       0},
   382         {0,     0,       0,       0,      0,     0,       0,       0},
   383         {0,     0,       0,       0,      0,     0,       0,       0},
   384         {0,     0,       0,       0,      0,     0,       0,       0},
   385         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   386         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   387     };
   388     memcpy(gamestate->board, initboard, sizeof(Board));
   389 }
   391 void game_start_singlemachine(Settings *settings) {
   392     inputy = getmaxy(stdscr) - 6;
   394     GameState gamestate;
   395     memset(&gamestate, 0, sizeof(GameState));
   396     init_board(&gamestate);
   397     gamestate.mycolor = WHITE;
   399     _Bool running;
   400     do {
   401         clear();
   402         draw_board(&gamestate);
   403         running = !domove_singlemachine(&gamestate, &(settings->gameinfo));
   404         gamestate.mycolor = opponent_color(gamestate.mycolor);
   405     }  while (running);
   406     move(0,0);
   407     draw_board(&gamestate);
   409     gamestate_cleanup(&gamestate);
   411     mvaddstr(getmaxy(stdscr)-1, 0,
   412         "Game has ended. Press any key to leave...");
   413     refresh();
   414     getch();
   415 }
   417 void game_start(Settings *settings, int opponent) {
   418     inputy = getmaxy(stdscr) - 6;
   420     _Bool myturn = is_server(settings) ==
   421         (settings->gameinfo.servercolor == WHITE);
   423     GameState gamestate;
   424     memset(&gamestate, 0, sizeof(GameState));
   425     init_board(&gamestate);
   426     gamestate.mycolor = myturn ? WHITE:BLACK;
   428     _Bool running;
   429     do {
   430         clear();
   431         draw_board(&gamestate);
   432         if (myturn) {
   433             running = !sendmove(&gamestate, &(settings->gameinfo), opponent);
   434         } else {
   435             running = !recvmove(&gamestate, &(settings->gameinfo), opponent);
   436         }
   437         myturn ^= TRUE;
   438     }  while (running);
   440     move(0,0);
   441     draw_board(&gamestate);
   443     gamestate_cleanup(&gamestate);
   445     mvaddstr(getmaxy(stdscr)-1, 0,
   446         "Game has ended. Press any key to leave...");
   447     refresh();
   448     cbreak();
   449     flushinp();
   450     getch();
   451 }

mercurial