src/game.c

Wed, 09 Apr 2014 09:34:07 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 09 Apr 2014 09:34:07 +0200
changeset 32
8a0b85303ee8
parent 31
ed440bcd9740
child 33
866025982aa9
permissions
-rw-r--r--

improved async input + improved build system + added time values to move struct

     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     size_t bufpos = 0;
   157     char movestr[buflen];
   159     int inputy = getmaxy(stdscr) - 6;
   160     while (1) {
   161         draw_time(gamestate, gameinfo);
   162         move(inputy, 0);
   163         printw(
   164             "Use chess notation to enter your move.\n"
   165             "Or type 'surr' to surrender or 'remis' to end with remis.\n\n"
   166             "Type your move: ");
   167         clrtoeol();
   168         refresh();
   170         if (asyncgetnstr(movestr, &bufpos, buflen)) {
   171             if (strncmp(movestr, "surr", buflen) == 0) {
   172                 printw("%s surrendered!",
   173                     gamestate->mycolor==WHITE?"White":"Black");
   174                 clrtoeol();
   175                 refresh();
   176                 return 1;
   177             } else if (strncmp(movestr, "remis", buflen) == 0) {
   178                 printw("Game ends remis.");
   179                 clrtoeol();
   180                 refresh();
   181                 return 1;
   182             } else {
   183                 Move move;
   184                 int eval_result = eval_move(gamestate, movestr, &move);
   185                 switch (eval_result) {
   186                 case VALID_MOVE_SYNTAX:
   187                     if (validate_move(gamestate, &move)) {
   188                         apply_move(gamestate, &move);
   189                         if (gamestate->checkmate) {
   190                             printw("Checkmate!");
   191                             clrtoeol();
   192                             return 1;
   193                         } else if (gamestate->stalemate) {
   194                             printw("Stalemate!");
   195                             clrtoeol();
   196                             return 1;
   197                         } else {
   198                             return 0;
   199                         }
   200                     } else {
   201                         printw("Invalid move.");
   202                     }
   203                     break;
   204                 default:
   205                     eval_move_failed_msg(eval_result);
   206                 }
   207                 clrtoeol();
   208             }
   209         }
   210     }
   211 }
   213 static int sendmove(GameState *gamestate, int opponent) {
   215     const size_t buflen = 8;
   216     char movestr[buflen];
   217     _Bool remisrejected = FALSE;
   218     uint8_t code;
   220     int inputy = getmaxy(stdscr) - 6;
   221     while (1) {
   222         move(inputy, 0);
   223         if (remisrejected) {
   224             printw(
   225                 "Use chess notation to enter your move.\n"
   226                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   227                 "Type your move: ");
   228         } else {
   229             printw(
   230                 "Use chess notation to enter your move.\n"
   231                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   232                 "Type your move: ");
   233         }
   234         clrtoeol();
   235         refresh();
   236         getnstr(movestr, buflen);
   238         if (strncmp(movestr, "surr", buflen) == 0) {
   239             printw("You surrendered!");
   240             clrtoeol();
   241             refresh();
   242             net_send_code(opponent, NETCODE_SURRENDER);
   243             return 1;
   244         } else if (strncmp(movestr, "remis", buflen) == 0) {
   245             if (!remisrejected) {
   246                 net_send_code(opponent, NETCODE_REMIS);
   247                 printw("Remis offer sent - waiting for acceptance...");
   248                 refresh();
   249                 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   250                     printw("\rRemis accepted!");
   251                     clrtoeol();
   252                     refresh();
   253                     return 1;
   254                 } else {
   255                     remisrejected = TRUE;
   256                 }
   257             }
   258         } else {
   259             Move move;
   260             int eval_result = eval_move(gamestate, movestr, &move);
   261             switch (eval_result) {
   262             case VALID_MOVE_SYNTAX:
   263                 net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
   264                 code = net_recieve_code(opponent);
   265                 move.check = code == NETCODE_CHECK;
   266                 gamestate->checkmate = code == NETCODE_CHECKMATE;
   267                 gamestate->stalemate = code == NETCODE_STALEMATE;
   268                 if (code == NETCODE_DECLINE) {
   269                     printw("Invalid move.");
   270                 } else {
   271                     apply_move(gamestate, &move);
   272                     if (gamestate->checkmate) {
   273                         printw("Checkmate!");
   274                         clrtoeol();
   275                         return 1;
   276                     } else if (gamestate->stalemate) {
   277                         printw("Stalemate!");
   278                         clrtoeol();
   279                         return 1;
   280                     } else {
   281                         return 0;
   282                     }
   283                 }
   284                 break;
   285             default:
   286                 eval_move_failed_msg(eval_result);
   287             }
   288             clrtoeol();
   289         }
   290     }
   291 }
   293 static int recvmove(GameState *gamestate, int opponent) {
   295     int inputy = getmaxy(stdscr) - 6;
   296     while (1) {
   297         move(inputy, 0);
   298         printw("Awaiting opponent move...");
   299         clrtoeol();
   300         refresh();
   302         // TODO: nonblocking
   303         uint32_t code = net_recieve_code(opponent);
   305         Move move;
   306         switch (code) {
   307             case NETCODE_SURRENDER:
   308                 printw("\rYour opponent surrendered!");
   309                 clrtoeol();
   310                 return 1;
   311             case NETCODE_REMIS:
   312                 if (prompt_yesno(
   313                     "\rYour opponent offers remis - do you accept")) {
   314                     printw("\rRemis accepted!");
   315                     clrtoeol();
   316                     net_send_code(opponent, NETCODE_ACCEPT);
   317                     return 1;
   318                 } else {
   319                     net_send_code(opponent, NETCODE_DECLINE);
   320                 }
   321                 break;
   322             case NETCODE_MOVE:
   323                 net_recieve_data(opponent, &move, sizeof(Move));
   324                 if (validate_move(gamestate, &move)) {
   325                     apply_move(gamestate, &move);
   326                     if (move.check) {
   327                         net_send_code(opponent, NETCODE_CHECK);
   328                     } else if (gamestate->checkmate) {
   329                         net_send_code(opponent, NETCODE_CHECKMATE);
   330                         printw("\rCheckmate!");
   331                         clrtoeol();
   332                         return 1;
   333                     } else if (gamestate->stalemate) {
   334                         net_send_code(opponent, NETCODE_STALEMATE);
   335                         printw("\rStalemate!");
   336                         clrtoeol();
   337                         return 1;
   338                     } else {
   339                         net_send_code(opponent, NETCODE_ACCEPT);
   340                     }
   341                     return 0;
   342                 } else {
   343                     net_send_code(opponent, NETCODE_DECLINE);
   344                 }
   345         }
   346     }
   347 }
   349 static void init_board(GameState *gamestate) {
   350     Board initboard = {
   351         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   352         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   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         {0,     0,       0,       0,      0,     0,       0,       0},
   357         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   358         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   359     };
   360     memcpy(gamestate->board, initboard, sizeof(Board));
   361 }
   363 void game_start_singlemachine(Settings *settings) {
   364     GameState gamestate;
   365     memset(&gamestate, 0, sizeof(GameState));
   366     init_board(&gamestate);
   367     gamestate.mycolor = WHITE;
   369     _Bool running;
   370     do {
   371         clear();
   372         draw_board(&gamestate);
   373         running = !domove_singlemachine(&gamestate, &(settings->gameinfo));
   374         gamestate.mycolor = opponent_color(gamestate.mycolor);
   375     }  while (running);
   376     move(0,0);
   377     draw_board(&gamestate);
   379     gamestate_cleanup(&gamestate);
   381     mvaddstr(getmaxy(stdscr)-1, 0,
   382         "Game has ended. Press any key to leave...");
   383     refresh();
   384     getch();
   385 }
   387 void game_start(Settings *settings, int opponent) {
   388     _Bool myturn = is_server(settings) ==
   389         (settings->gameinfo.servercolor == WHITE);
   391     // TODO: time limit
   392     GameState gamestate;
   393     memset(&gamestate, 0, sizeof(GameState));
   394     init_board(&gamestate);
   395     gamestate.mycolor = myturn ? WHITE:BLACK;
   397     _Bool running;
   398     do {
   399         clear();
   400         draw_board(&gamestate);
   401         if (myturn) {
   402             running = !sendmove(&gamestate, opponent);
   403         } else {
   404             running = !recvmove(&gamestate, opponent);
   405             flushinp(); // flush any input the user hacked in while waiting
   406         }
   407         myturn ^= TRUE;
   408     }  while (running);
   410     move(0,0);
   411     draw_board(&gamestate);
   413     gamestate_cleanup(&gamestate);
   415     mvaddstr(getmaxy(stdscr)-1, 0,
   416         "Game has ended. Press any key to leave...");
   417     refresh();
   418     getch();
   419 }

mercurial