src/game.c

Tue, 08 Apr 2014 21:13:28 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 08 Apr 2014 21:13:28 +0200
changeset 31
ed440bcd9740
parent 30
a285ee393860
child 32
8a0b85303ee8
permissions
-rw-r--r--

fixed some type bugs, uninitialized memory and async input function

     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];
   157     movestr[0] = '\0';
   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, 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             movestr[0] = '\0'; /* reset string for next input */
   210         }
   211     }
   212 }
   214 static int sendmove(GameState *gamestate, int opponent) {
   216     const size_t buflen = 8;
   217     char movestr[buflen];
   218     _Bool remisrejected = FALSE;
   219     uint8_t code;
   221     int inputy = getmaxy(stdscr) - 6;
   222     while (1) {
   223         move(inputy, 0);
   224         if (remisrejected) {
   225             printw(
   226                 "Use chess notation to enter your move.\n"
   227                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   228                 "Type your move: ");
   229         } else {
   230             printw(
   231                 "Use chess notation to enter your move.\n"
   232                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   233                 "Type your move: ");
   234         }
   235         clrtoeol();
   236         refresh();
   237         getnstr(movestr, buflen);
   239         if (strncmp(movestr, "surr", buflen) == 0) {
   240             printw("You surrendered!");
   241             clrtoeol();
   242             refresh();
   243             net_send_code(opponent, NETCODE_SURRENDER);
   244             return 1;
   245         } else if (strncmp(movestr, "remis", buflen) == 0) {
   246             if (!remisrejected) {
   247                 net_send_code(opponent, NETCODE_REMIS);
   248                 printw("Remis offer sent - waiting for acceptance...");
   249                 refresh();
   250                 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   251                     printw("\rRemis accepted!");
   252                     clrtoeol();
   253                     refresh();
   254                     return 1;
   255                 } else {
   256                     remisrejected = TRUE;
   257                 }
   258             }
   259         } else {
   260             Move move;
   261             int eval_result = eval_move(gamestate, movestr, &move);
   262             switch (eval_result) {
   263             case VALID_MOVE_SYNTAX:
   264                 net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
   265                 code = net_recieve_code(opponent);
   266                 move.check = code == NETCODE_CHECK;
   267                 gamestate->checkmate = code == NETCODE_CHECKMATE;
   268                 gamestate->stalemate = code == NETCODE_STALEMATE;
   269                 if (code == NETCODE_DECLINE) {
   270                     printw("Invalid move.");
   271                 } else {
   272                     apply_move(gamestate, &move);
   273                     if (gamestate->checkmate) {
   274                         printw("Checkmate!");
   275                         clrtoeol();
   276                         return 1;
   277                     } else if (gamestate->stalemate) {
   278                         printw("Stalemate!");
   279                         clrtoeol();
   280                         return 1;
   281                     } else {
   282                         return 0;
   283                     }
   284                 }
   285                 break;
   286             default:
   287                 eval_move_failed_msg(eval_result);
   288             }
   289             clrtoeol();
   290         }
   291     }
   292 }
   294 static int recvmove(GameState *gamestate, int opponent) {
   296     int inputy = getmaxy(stdscr) - 6;
   297     while (1) {
   298         move(inputy, 0);
   299         printw("Awaiting opponent move...");
   300         clrtoeol();
   301         refresh();
   303         // TODO: nonblocking
   304         uint32_t code = net_recieve_code(opponent);
   306         Move move;
   307         switch (code) {
   308             case NETCODE_SURRENDER:
   309                 printw("\rYour opponent surrendered!");
   310                 clrtoeol();
   311                 return 1;
   312             case NETCODE_REMIS:
   313                 if (prompt_yesno(
   314                     "\rYour opponent offers remis - do you accept")) {
   315                     printw("\rRemis accepted!");
   316                     clrtoeol();
   317                     net_send_code(opponent, NETCODE_ACCEPT);
   318                     return 1;
   319                 } else {
   320                     net_send_code(opponent, NETCODE_DECLINE);
   321                 }
   322                 break;
   323             case NETCODE_MOVE:
   324                 net_recieve_data(opponent, &move, sizeof(Move));
   325                 if (validate_move(gamestate, &move)) {
   326                     apply_move(gamestate, &move);
   327                     if (move.check) {
   328                         net_send_code(opponent, NETCODE_CHECK);
   329                     } else if (gamestate->checkmate) {
   330                         net_send_code(opponent, NETCODE_CHECKMATE);
   331                         printw("\rCheckmate!");
   332                         clrtoeol();
   333                         return 1;
   334                     } else if (gamestate->stalemate) {
   335                         net_send_code(opponent, NETCODE_STALEMATE);
   336                         printw("\rStalemate!");
   337                         clrtoeol();
   338                         return 1;
   339                     } else {
   340                         net_send_code(opponent, NETCODE_ACCEPT);
   341                     }
   342                     return 0;
   343                 } else {
   344                     net_send_code(opponent, NETCODE_DECLINE);
   345                 }
   346         }
   347     }
   348 }
   350 static void init_board(GameState *gamestate) {
   351     Board initboard = {
   352         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   353         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   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         {0,     0,       0,       0,      0,     0,       0,       0},
   358         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   359         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   360     };
   361     memcpy(gamestate->board, initboard, sizeof(Board));
   362 }
   364 void game_start_singlemachine(Settings *settings) {
   365     GameState gamestate;
   366     memset(&gamestate, 0, sizeof(GameState));
   367     init_board(&gamestate);
   368     gamestate.mycolor = WHITE;
   370     _Bool running;
   371     do {
   372         clear();
   373         draw_board(&gamestate);
   374         running = !domove_singlemachine(&gamestate, &(settings->gameinfo));
   375         gamestate.mycolor = opponent_color(gamestate.mycolor);
   376     }  while (running);
   377     move(0,0);
   378     draw_board(&gamestate);
   380     gamestate_cleanup(&gamestate);
   382     mvaddstr(getmaxy(stdscr)-1, 0,
   383         "Game has ended. Press any key to leave...");
   384     refresh();
   385     getch();
   386 }
   388 void game_start(Settings *settings, int opponent) {
   389     _Bool myturn = is_server(settings) ==
   390         (settings->gameinfo.servercolor == WHITE);
   392     // TODO: time limit
   393     GameState gamestate;
   394     memset(&gamestate, 0, sizeof(GameState));
   395     init_board(&gamestate);
   396     gamestate.mycolor = myturn ? WHITE:BLACK;
   398     _Bool running;
   399     do {
   400         clear();
   401         draw_board(&gamestate);
   402         if (myturn) {
   403             running = !sendmove(&gamestate, opponent);
   404         } else {
   405             running = !recvmove(&gamestate, opponent);
   406             flushinp(); // flush any input the user hacked in while waiting
   407         }
   408         myturn ^= TRUE;
   409     }  while (running);
   411     move(0,0);
   412     draw_board(&gamestate);
   414     gamestate_cleanup(&gamestate);
   416     mvaddstr(getmaxy(stdscr)-1, 0,
   417         "Game has ended. Press any key to leave...");
   418     refresh();
   419     getch();
   420 }

mercurial