src/game.c

Mon, 07 Apr 2014 17:39:46 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 07 Apr 2014 17:39:46 +0200
changeset 30
a285ee393860
parent 28
0c1371488d87
child 31
ed440bcd9740
permissions
-rw-r--r--

experimental async input for single machine mode

     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;
    39 static void draw_time(GameState *gamestate, GameInfo *gameinfo) {
    40     if (gameinfo->timecontrol) {
    41         // TODO: correct time display
    43         uint16_t whitem = gameinfo->time / 60;
    44         uint16_t whites = gameinfo->time % 60;
    45         uint16_t blackm = gameinfo->time / 60;
    46         uint16_t blacks = gameinfo->time % 60;
    48         mvprintw(boardy+4, boardx-1,
    49             "White time: %4" PRIu16 ":%02" PRIu16, whitem, whites);
    50         mvprintw(boardy+5, boardx-1,
    51             "Black time: %4" PRIu16 ":%02" PRIu16, blackm, blacks);
    52     }
    53 }
    55 static void draw_board(GameState *gamestate) {
    56     for (uint8_t y = 0 ; y < 8 ; y++) {
    57         for (uint8_t x = 0 ; x < 8 ; x++) {
    58             uint8_t col = gamestate->board[y][x] & COLOR_MASK;
    59             uint8_t piece = gamestate->board[y][x] & PIECE_MASK;
    60             char piecec;
    61             if (piece) {
    62                 piecec = piece == PAWN ? 'P' : getpiecechr(piece);
    63             } else {
    64                 piecec = ' ';
    65             }
    67             attrset((col == WHITE ? A_BOLD : A_DIM) |
    68                 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW));
    70             int cy = gamestate->mycolor == WHITE ? boardy-y : boardy-7+y;
    71             int cx = gamestate->mycolor == WHITE ? boardx+x*3 : boardx+21-x*3;
    72             mvaddch(cy, cx, ' ');
    73             mvaddch(cy, cx+1, piecec);
    74             mvaddch(cy, cx+2, ' ');
    75         }
    76     }
    78     attrset(A_NORMAL);
    79     for (uint8_t i = 0 ; i < 8 ; i++) {
    80         int x = gamestate->mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
    81         int y = gamestate->mycolor == WHITE ? boardy-i : boardy-7+i;
    82         mvaddch(boardy+1, x, 'a'+i);
    83         mvaddch(y, boardx-2, '1'+i);
    84     }
    86     /* move log */
    87     // TODO: introduce window to avoid bugs with a long move log
    88     uint8_t logy = 0;
    89     const uint8_t logx = boardx + 30;
    90     int logi = 1;
    91     MoveList *logelem = gamestate->movelist;
    93     while (logelem) {
    94         logi++;
    95         if (logi % 2 == 0) {
    96             if ((logi - 2) % 4 == 0) {
    97                 logy++;
    98                 move(logy, logx);
    99             }
   100             printw("%d. ", logi / 2);
   101         }
   103         if (logelem) {
   104             Move move = logelem->move;
   105             if ((move.piece&PIECE_MASK) == KING &&
   106                 abs(move.tofile-move.fromfile) == 2) {
   107                 addstr(move.tofile==fileidx('c')?"O-O-O":"O-O");
   108             } else {
   109                 char logstr[] = {
   110                     getpiecechr(move.piece),
   111                     filechr(move.fromfile), rowchr(move.fromrow),
   112                     move.capture ? 'x':'\0',
   113                     filechr(move.tofile), rowchr(move.torow),
   114                     move.check ? '+' : (move.promotion ? '=' : '\0'),
   115                     move.promotion ? getpiecechr(move.promotion) : '\0'
   116                 };
   117                 for (int stri = 0 ; stri < sizeof(logstr) ; stri++) {
   118                     if (logstr[stri]) {
   119                         addch(logstr[stri]);
   120                     }
   121                 }
   122             }
   123             if (!logelem->next) {
   124                 if (gamestate->checkmate) {
   125                     addstr("\b#");
   126                 } else if (gamestate->stalemate) {
   127                     addstr(" stalemate");
   128                 }
   129             }
   130             addch(' ');
   132             logelem = logelem->next;
   133         }
   134     }
   135 }
   137 static void eval_move_failed_msg(int code) {
   138     switch (code) {
   139     case AMBIGUOUS_MOVE:
   140         printw("Ambiguous move - please specify the piece to move.");
   141         break;
   142     case INVALID_POSITION:
   143         printw("Cannot find the piece that shall be moved.");
   144         break;
   145     case NEED_PROMOTION:
   146         printw("You need to promote the pawn (append \"=Q\" e.g.)!");
   147         break;
   148     default:
   149         printw("Can't interpret move - please use algebraic notation.");
   150     }
   151 }
   153 static int domove_singlemachine(GameState *gamestate, GameInfo *gameinfo) {
   155     const size_t buflen = 8;
   156     char movestr[buflen];
   158     int inputy = getmaxy(stdscr) - 6;
   159     while (1) {
   160         draw_time(gamestate, gameinfo);
   161         move(inputy, 0);
   162         printw(
   163             "Use chess notation to enter your move.\n"
   164             "Or type 'surr' to surrender or 'remis' to end with remis.\n\n"
   165             "Type your move: ");
   166         clrtoeol();
   167         refresh();
   169         if (asyncgetnstr(movestr, buflen)) {
   170             if (strncmp(movestr, "surr", buflen) == 0) {
   171                 printw("%s surrendered!",
   172                     gamestate->mycolor==WHITE?"White":"Black");
   173                 clrtoeol();
   174                 refresh();
   175                 return 1;
   176             } else if (strncmp(movestr, "remis", buflen) == 0) {
   177                 printw("Game ends remis.");
   178                 clrtoeol();
   179                 refresh();
   180                 return 1;
   181             } else {
   182                 Move move;
   183                 int eval_result = eval_move(gamestate, movestr, &move);
   184                 switch (eval_result) {
   185                 case VALID_MOVE_SYNTAX:
   186                     if (validate_move(gamestate, &move)) {
   187                         apply_move(gamestate, &move);
   188                         if (gamestate->checkmate) {
   189                             printw("Checkmate!");
   190                             clrtoeol();
   191                             return 1;
   192                         } else if (gamestate->stalemate) {
   193                             printw("Stalemate!");
   194                             clrtoeol();
   195                             return 1;
   196                         } else {
   197                             return 0;
   198                         }
   199                     } else {
   200                         printw("Invalid move.");
   201                     }
   202                     break;
   203                 default:
   204                     eval_move_failed_msg(eval_result);
   205                 }
   206                 clrtoeol();
   207             }
   208         }
   209     }
   210 }
   212 static int sendmove(GameState *gamestate, int opponent) {
   214     const size_t buflen = 8;
   215     char movestr[buflen];
   216     _Bool remisrejected = FALSE;
   217     uint8_t code;
   219     int inputy = getmaxy(stdscr) - 6;
   220     while (1) {
   221         move(inputy, 0);
   222         if (remisrejected) {
   223             printw(
   224                 "Use chess notation to enter your move.\n"
   225                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   226                 "Type your move: ");
   227         } else {
   228             printw(
   229                 "Use chess notation to enter your move.\n"
   230                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   231                 "Type your move: ");
   232         }
   233         clrtoeol();
   234         refresh();
   235         getnstr(movestr, buflen);
   237         if (strncmp(movestr, "surr", buflen) == 0) {
   238             printw("You surrendered!");
   239             clrtoeol();
   240             refresh();
   241             net_send_code(opponent, NETCODE_SURRENDER);
   242             return 1;
   243         } else if (strncmp(movestr, "remis", buflen) == 0) {
   244             if (!remisrejected) {
   245                 net_send_code(opponent, NETCODE_REMIS);
   246                 printw("Remis offer sent - waiting for acceptance...");
   247                 refresh();
   248                 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   249                     printw("\rRemis accepted!");
   250                     clrtoeol();
   251                     refresh();
   252                     return 1;
   253                 } else {
   254                     remisrejected = TRUE;
   255                 }
   256             }
   257         } else {
   258             Move move;
   259             int eval_result = eval_move(gamestate, movestr, &move);
   260             switch (eval_result) {
   261             case VALID_MOVE_SYNTAX:
   262                 net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
   263                 code = net_recieve_code(opponent);
   264                 move.check = code == NETCODE_CHECK;
   265                 gamestate->checkmate = code == NETCODE_CHECKMATE;
   266                 gamestate->stalemate = code == NETCODE_STALEMATE;
   267                 if (code == NETCODE_DECLINE) {
   268                     printw("Invalid move.");
   269                 } else {
   270                     apply_move(gamestate, &move);
   271                     if (gamestate->checkmate) {
   272                         printw("Checkmate!");
   273                         clrtoeol();
   274                         return 1;
   275                     } else if (gamestate->stalemate) {
   276                         printw("Stalemate!");
   277                         clrtoeol();
   278                         return 1;
   279                     } else {
   280                         return 0;
   281                     }
   282                 }
   283                 break;
   284             default:
   285                 eval_move_failed_msg(eval_result);
   286             }
   287             clrtoeol();
   288         }
   289     }
   290 }
   292 static int recvmove(GameState *gamestate, int opponent) {
   294     int inputy = getmaxy(stdscr) - 6;
   295     while (1) {
   296         move(inputy, 0);
   297         printw("Awaiting opponent move...");
   298         clrtoeol();
   299         refresh();
   301         // TODO: nonblocking
   302         uint32_t code = net_recieve_code(opponent);
   304         Move move;
   305         switch (code) {
   306             case NETCODE_SURRENDER:
   307                 printw("\rYour opponent surrendered!");
   308                 clrtoeol();
   309                 return 1;
   310             case NETCODE_REMIS:
   311                 if (prompt_yesno(
   312                     "\rYour opponent offers remis - do you accept")) {
   313                     printw("\rRemis accepted!");
   314                     clrtoeol();
   315                     net_send_code(opponent, NETCODE_ACCEPT);
   316                     return 1;
   317                 } else {
   318                     net_send_code(opponent, NETCODE_DECLINE);
   319                 }
   320                 break;
   321             case NETCODE_MOVE:
   322                 net_recieve_data(opponent, &move, sizeof(Move));
   323                 if (validate_move(gamestate, &move)) {
   324                     apply_move(gamestate, &move);
   325                     if (move.check) {
   326                         net_send_code(opponent, NETCODE_CHECK);
   327                     } else if (gamestate->checkmate) {
   328                         net_send_code(opponent, NETCODE_CHECKMATE);
   329                         printw("\rCheckmate!");
   330                         clrtoeol();
   331                         return 1;
   332                     } else if (gamestate->stalemate) {
   333                         net_send_code(opponent, NETCODE_STALEMATE);
   334                         printw("\rStalemate!");
   335                         clrtoeol();
   336                         return 1;
   337                     } else {
   338                         net_send_code(opponent, NETCODE_ACCEPT);
   339                     }
   340                     return 0;
   341                 } else {
   342                     net_send_code(opponent, NETCODE_DECLINE);
   343                 }
   344         }
   345     }
   346 }
   348 static void init_board(GameState *gamestate) {
   349     Board initboard = {
   350         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   351         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   352         {0,     0,       0,       0,      0,     0,       0,       0},
   353         {0,     0,       0,       0,      0,     0,       0,       0},
   354         {0,     0,       0,       0,      0,     0,       0,       0},
   355         {0,     0,       0,       0,      0,     0,       0,       0},
   356         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   357         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   358     };
   359     memcpy(gamestate->board, initboard, sizeof(Board));
   360 }
   362 void game_start_singlemachine(Settings *settings) {
   363     GameState gamestate;
   364     memset(&gamestate, 0, sizeof(GameState));
   365     init_board(&gamestate);
   366     gamestate.mycolor = WHITE;
   368     _Bool running;
   369     do {
   370         clear();
   371         draw_board(&gamestate);
   372         running = !domove_singlemachine(&gamestate, &(settings->gameinfo));
   373         gamestate.mycolor = opponent_color(gamestate.mycolor);
   374     }  while (running);
   375     move(0,0);
   376     draw_board(&gamestate);
   378     gamestate_cleanup(&gamestate);
   380     mvaddstr(getmaxy(stdscr)-1, 0,
   381         "Game has ended. Press any key to leave...");
   382     refresh();
   383     getch();
   384 }
   386 void game_start(Settings *settings, int opponent) {
   387     _Bool myturn = is_server(settings) ==
   388         (settings->gameinfo.servercolor == WHITE);
   390     // TODO: time limit
   391     GameState gamestate;
   392     memset(&gamestate, 0, sizeof(GameState));
   393     init_board(&gamestate);
   394     gamestate.mycolor = myturn ? WHITE:BLACK;
   396     _Bool running;
   397     do {
   398         clear();
   399         draw_board(&gamestate);
   400         if (myturn) {
   401             running = !sendmove(&gamestate, opponent);
   402         } else {
   403             running = !recvmove(&gamestate, opponent);
   404             flushinp(); // flush any input the user hacked in while waiting
   405         }
   406         myturn ^= TRUE;
   407     }  while (running);
   409     move(0,0);
   410     draw_board(&gamestate);
   412     gamestate_cleanup(&gamestate);
   414     mvaddstr(getmaxy(stdscr)-1, 0,
   415         "Game has ended. Press any key to leave...");
   416     refresh();
   417     getch();
   418 }

mercurial