src/game.c

Sat, 22 Mar 2014 18:56:52 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 22 Mar 2014 18:56:52 +0100
changeset 11
08d7a6e3ec31
parent 10
1347e4dabac0
child 12
84880c7e1ea6
permissions
-rw-r--r--

added network messages for check and checkmate

     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 "input.h"
    32 #include "rules/rules.h"
    33 #include <ncurses.h>
    34 #include <string.h>
    36 static const uint8_t boardx = 10, boardy = 10;
    38 static void draw_board(Board board, uint8_t mycolor) {
    40     for (uint8_t y = 0 ; y < 8 ; y++) {
    41         for (uint8_t x = 0 ; x < 8 ; x++) {
    42             uint8_t col = board[y][x] & COLOR_MASK;
    43             uint8_t piece = board[y][x] & PIECE_MASK;
    44             char piecec = ' ';
    45             switch (piece) {
    46                 case PAWN: piecec = 'P'; break;
    47                 case ROOK: piecec = 'R'; break;
    48                 case KNIGHT: piecec = 'N'; break;
    49                 case BISHOP: piecec = 'B'; break;
    50                 case QUEEN: piecec = 'Q'; break;
    51                 case KING: piecec = 'K'; break;
    52             }
    54             attrset((col == WHITE ? A_BOLD : A_DIM) |
    55                 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW));
    57             int cy = mycolor == WHITE ? boardy-y : boardy-7+y;
    58             int cx = mycolor == WHITE ? boardx+x*3 : boardx+21-x*3;
    59             mvaddch(cy, cx, ' ');
    60             mvaddch(cy, cx+1, piecec);
    61             mvaddch(cy, cx+2, ' ');
    62         }
    63     }
    65     attrset(A_NORMAL);
    66     for (uint8_t i = 0 ; i < 8 ; i++) {
    67         int x = mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
    68         int y = mycolor == WHITE ? boardy-i : boardy-7+i;
    69         mvaddch(boardy+1, x, 'a'+i);
    70         mvaddch(y, boardx-2, '1'+i);
    71     }
    72 }
    74 static void apply_move(Board board, Move *move) {
    75     board[move->fromrow][move->fromfile] = 0;
    76     // TODO: care for en passant capture
    77     board[move->torow][move->tofile] = move->piece;
    79     /* castling */
    80     if ((move->piece & PIECE_MASK) == KING &&
    81         move->fromfile == fileidx('e')) {
    82         uint8_t color = move->piece & COLOR_MASK;
    84         if (move->tofile == fileidx('g')) {
    85             board[move->torow][fileidx('h')] = 0;
    86             board[move->torow][fileidx('f')] = color|ROOK;
    87         } else if (move->tofile == fileidx('c')) {
    88             board[move->torow][fileidx('a')] = 0;
    89             board[move->torow][fileidx('d')] = color|ROOK;
    90         }
    91     }
    92 }
    94 static _Bool validate_move(Board board, Move *move) {
    95     _Bool result;
    97     /* does piece exist */
    98     result = board[move->fromrow][move->fromfile] == move->piece;
   100     switch (move->piece & PIECE_MASK) {
   101     case PAWN:
   102         result = result && pawn_chkrules(board, move);
   103         result = result && !pawn_isblocked(board, move);
   104         break;
   105     case ROOK:
   106         result = result && rook_chkrules(board, move);
   107         result = result && !rook_isblocked(board, move);
   108         break;
   109     case KNIGHT:
   110         result = result && knight_chkrules(board, move);
   111         result = result && !knight_isblocked(board, move);
   112         break;
   113     case BISHOP:
   114         result = result && bishop_chkrules(board, move);
   115         result = result && !bishop_isblocked(board, move);
   116         break;
   117     case QUEEN:
   118         result = result && queen_chkrules(board, move);
   119         result = result && !queen_isblocked(board, move);
   120         break;
   121     case KING:
   122         result = result && king_chkrules(board, move);
   123         result = result && !king_isblocked(board, move);
   124         break;
   125     default:
   126         result = FALSE;
   127     }
   129     /* is piece pinned */
   130     // TODO: make it so
   132     /* correct check and checkmate flags */
   133     // TODO: make it so
   135     return result;
   136 }
   138 static _Bool eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) {
   139     memset(move, 0, sizeof(Move));
   141     size_t len = strlen(mstr);
   143     /* evaluate check/checkmate flags */
   144     if (mstr[len-1] == '+') {
   145         len--; mstr[len] = '\0';
   146         move->check = TRUE;
   147     } else if (mstr[len-1] == '#') {
   148         len--; mstr[len] = '\0';
   149         move->checkmate = TRUE;
   150     }
   152     if (len == 2) {
   153         /* pawn move (e.g. "e4") */
   154         if (isfile(mstr[0]) && isrow(mstr[1])) {
   155             move->piece = PAWN|mycolor;
   156             move->tofile = fileidx(mstr[0]);
   157             move->torow = rowidx(mstr[1]);
   158             if (!pawn_getlocation(board, move)) {
   159                 move->piece = 0;
   160             }
   161         }
   162     } else if (len == 3) {
   163         if (strcmp(mstr, "O-O") == 0) {
   164             /* king side castling */
   165             move->piece = KING|mycolor;
   166             move->fromfile = fileidx('e');
   167             move->tofile = fileidx('g');
   168             move->fromrow = move->torow = mycolor == WHITE ? 0 : 7;
   169         } else {
   170             /* unambiguous move (e.g. "Nf3") */
   171         }
   173     } else if (len == 4) {
   174         /* ambiguous move (e.g. "Ndf3") */
   176         /* unambiguous capture (e.g. "Nxf3", "dxe5") */
   178     } else if (len == 5) {
   179         if (strcmp(mstr, "O-O-O") == 0) {
   180             /* queen side castling "O-O-O" */
   181             move->piece = KING|mycolor;
   182             move->fromfile = fileidx('e');
   183             move->tofile = fileidx('c');
   184             move->fromrow = move->torow = mycolor == WHITE ? 0 : 7;
   185         } else {
   186             /* ambiguous capture (e.g. "Ndxf3") */
   188             /* long notation move (e.g. "Nc5a4") */
   190             /* long notation capture (e.g. "e5xf6") */
   191         }
   192     } else if (len == 6) {
   193         /* long notation capture (e.g. "Nc5xf3") */
   194     }
   196     return move->piece != 0;
   197 }
   199 static int sendmove(Board board, uint8_t mycolor, int opponent) {
   200     const size_t buflen = 8;
   201     char movestr[buflen];
   202     _Bool remisrejected = FALSE;
   203     uint8_t code;
   205     while (1) {
   206         move(boardy+3, 0);
   207         if (remisrejected) {
   208             printw(
   209                 "Use chess notation to enter your move.\n"
   210                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   211                 "Type your move: ");
   212         } else {
   213             printw(
   214                 "Use chess notation to enter your move.\n"
   215                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   216                 "Type your move: ");
   217         }
   218         clrtoeol();
   219         refresh();
   220         getnstr(movestr, buflen);
   222         if (strncmp(movestr, "surr", buflen) == 0) {
   223             printw("You surrendered!");
   224             refresh();
   225             net_send_code(opponent, NETCODE_SURRENDER);
   226             return 1;
   227         } else if (strncmp(movestr, "remis", buflen) == 0) {
   228             if (!remisrejected) {
   229                 net_send_code(opponent, NETCODE_REMIS);
   230                 printw("Remis offer sent - waiting for acceptance...");
   231                 refresh();
   232                 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   233                     printw("\rRemis accepted!");
   234                     clrtoeol();
   235                     refresh();
   236                     return 1;
   237                 } else {
   238                     remisrejected = TRUE;
   239                 }
   240             }
   241         } else {
   242             Move move;
   243             if (eval_move(board, mycolor, movestr, &move)) {
   244                 net_send_code(opponent, NETCODE_MOVE);
   245                 net_send_data(opponent, &move, sizeof(Move));
   246                 code = net_recieve_code(opponent);
   247                 move.check = code == NETCODE_CHECK;
   248                 move.checkmate = code == NETCODE_CHECKMATE;
   249                 // TODO: record move
   250                 if (code == NETCODE_DECLINE) {
   251                     printw("Invalid move.");
   252                     clrtoeol();
   253                 } else {
   254                     apply_move(board, &move);
   255                     if (move.checkmate) {
   256                         printw("Checkmate!");
   257                         return 1;
   258                     }
   259                 }
   260             } else {
   261                 printw("Can't interpret move - please use algebraic notation.");
   262             }
   263         }
   264     }
   265 }
   267 static int recvmove(Board board, int opponent) {
   269     while (1) {
   270         move(boardy+3, 0);
   271         printw("Awaiting opponent move...");
   272         clrtoeol();
   273         refresh();
   275         // TODO: nonblocking
   276         uint32_t code = net_recieve_code(opponent);
   278         Move move;
   279         switch (code) {
   280             case NETCODE_SURRENDER:
   281                 printw("\rYour opponent surrendered!");
   282                 clrtoeol();
   283                 return 1;
   284             case NETCODE_REMIS:
   285                 if (prompt_yesno(
   286                     "\rYour opponent offers remis - do you accept")) {
   287                     printw("\rRemis accepted!");
   288                     clrtoeol();
   289                     net_send_code(opponent, NETCODE_ACCEPT);
   290                     return 1;
   291                 } else {
   292                     net_send_code(opponent, NETCODE_DECLINE);
   293                 }
   294                 break;
   295             case NETCODE_MOVE:
   296                 net_recieve_data(opponent, &move, sizeof(Move));
   297                 if (validate_move(board, &move)) {
   298                     apply_move(board, &move);
   299                     // TODO: record move
   300                     if (move.check) {
   301                         net_send_code(opponent, NETCODE_CHECK);
   302                     } else if (move.checkmate) {
   303                         net_send_code(opponent, NETCODE_CHECKMATE);
   304                     } else {
   305                         net_send_code(opponent, NETCODE_ACCEPT);
   306                     }
   307                     return 0;
   308                 } else {
   309                     net_send_code(opponent, NETCODE_DECLINE);
   310                 }
   311         }
   312     }
   313 }
   315 void game_start(Settings *settings, int opponent) {
   316     _Bool myturn = is_server(settings) ==
   317         (settings->gameinfo.servercolor == WHITE);
   318     uint8_t mycolor = myturn ? WHITE:BLACK;
   320     _Bool running;
   322     Board board = {
   323         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   324         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   325         {0,     0,       0,       0,      0,     0,       0,       0},
   326         {0,     0,       0,       0,      0,     0,       0,       0},
   327         {0,     0,       0,       0,      0,     0,       0,       0},
   328         {0,     0,       0,       0,      0,     0,       0,       0},
   329         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   330         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   331     };
   333     do {
   334         clear();
   335         draw_board(board, mycolor);
   336         if (myturn) {
   337             running = !sendmove(board, mycolor, opponent);
   338         } else {
   339             running = !recvmove(board, opponent);
   340             flushinp(); // flush any input the user hacked in while waiting
   341         }
   342         myturn ^= TRUE;
   343     }  while (running);
   345     mvaddstr(getmaxy(tchess_window)-1, 0,
   346         "Game has ended. Press any key to leave...");
   347     getch();
   348 }

mercurial