src/game.c

Wed, 26 Mar 2014 14:12:59 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 26 Mar 2014 14:12:59 +0100
changeset 13
faec61c4901f
parent 12
84880c7e1ea6
child 14
970748b9a73b
permissions
-rw-r--r--

completed eval_move

     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 /**
    75  * Applies a move and deletes captured pieces.
    76  * 
    77  * @param board the current board state
    78  * @param move the move to apply
    79  */
    80 static void apply_move(Board board, Move *move) {
    81     board[move->fromrow][move->fromfile] = 0;
    82     // TODO: care for en passant capture
    83     board[move->torow][move->tofile] = move->piece;
    85     /* castling */
    86     if ((move->piece & PIECE_MASK) == KING &&
    87         move->fromfile == fileidx('e')) {
    88         uint8_t color = move->piece & COLOR_MASK;
    90         if (move->tofile == fileidx('g')) {
    91             board[move->torow][fileidx('h')] = 0;
    92             board[move->torow][fileidx('f')] = color|ROOK;
    93         } else if (move->tofile == fileidx('c')) {
    94             board[move->torow][fileidx('a')] = 0;
    95             board[move->torow][fileidx('d')] = color|ROOK;
    96         }
    97     }
    98 }
   100 /**
   101  * Validates move by applying chess rules.
   102  * @param board the current board state
   103  * @param move the move to validate
   104  * @return TRUE, if the move complies to chess rules, FALSE otherwise
   105  */
   106 static _Bool validate_move(Board board, Move *move) {
   107     _Bool result;
   109     /* validate indices (don't trust opponent) */
   110     if (!chkidx(move)) {
   111         return FALSE;
   112     }
   114     /* does piece exist */
   115     result = board[move->fromrow][move->fromfile] == move->piece;
   117     switch (move->piece & PIECE_MASK) {
   118     case PAWN:
   119         result = result && pawn_chkrules(board, move);
   120         result = result && !pawn_isblocked(board, move);
   121         break;
   122     case ROOK:
   123         result = result && rook_chkrules(board, move);
   124         result = result && !rook_isblocked(board, move);
   125         break;
   126     case KNIGHT:
   127         result = result && knight_chkrules(board, move);
   128         result = result && !knight_isblocked(board, move);
   129         break;
   130     case BISHOP:
   131         result = result && bishop_chkrules(board, move);
   132         result = result && !bishop_isblocked(board, move);
   133         break;
   134     case QUEEN:
   135         result = result && queen_chkrules(board, move);
   136         result = result && !queen_isblocked(board, move);
   137         break;
   138     case KING:
   139         result = result && king_chkrules(board, move);
   140         result = result && !king_isblocked(board, move);
   141         break;
   142     default:
   143         result = FALSE;
   144     }
   146     /* is piece pinned */
   147     // TODO: make it so
   149     /* correct check and checkmate flags */
   150     // TODO: make it so
   152     return result;
   153 }
   155 /**
   156  * Maps a character to a piece.
   157  * 
   158  * Does not work for pawns, since they don't have a character.
   159  * 
   160  * @param c one of R,N,B,Q,K
   161  * @return numeric value for the specified piece
   162  */
   163 static uint8_t getpiece(char c) {
   164     switch (c) {
   165         case 'R': return ROOK;
   166         case 'N': return KNIGHT;
   167         case 'B': return BISHOP;
   168         case 'Q': return QUEEN;
   169         case 'K': return KING;
   170         default: return 0;
   171     }
   172 }
   174 /**
   175  * Guesses the location of a piece for short algebraic notation.
   176  * 
   177  * @param board the current state of the board
   178  * @param move the move date to operate on
   179  * @return TRUE if the location could be retrieved, FALSE if the location is
   180  * ambiguous
   181  */
   182 static _Bool getlocation(Board board, Move *move) {   
   183     uint8_t piece = move->piece & PIECE_MASK;
   184     switch (piece) {
   185         case PAWN: return pawn_getlocation(board, move);
   186         case ROOK: return rook_getlocation(board, move);
   187         case KNIGHT: return knight_getlocation(board, move);
   188         case BISHOP: return bishop_getlocation(board, move);
   189         case QUEEN: return queen_getlocation(board, move);
   190         case KING: return king_getlocation(board, move);
   191         default: return FALSE;
   192     }
   193 }
   195 /**
   196  * Evaluates a move syntactically and stores the move data in the specified
   197  * object.
   198  * 
   199  * @param board the current state of the board
   200  * @param mycolor the color of the current player
   201  * @param mstr the input string to parse
   202  * @param move a pointer to object where the move data shall be stored
   203  * @return TRUE, if the move is syntactically valid, FALSE otherwise
   204  */
   205 static _Bool eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) {
   206     memset(move, 0, sizeof(Move));
   207     move->fromfile = POS_UNSPECIFIED;
   208     move->fromrow = POS_UNSPECIFIED;
   210     size_t len = strlen(mstr);
   212     /* evaluate check/checkmate flags */
   213     if (mstr[len-1] == '+') {
   214         len--; mstr[len] = '\0';
   215         move->check = TRUE;
   216     } else if (mstr[len-1] == '#') {
   217         len--; mstr[len] = '\0';
   218         move->checkmate = TRUE;
   219     }
   221     if (len == 2) {
   222         /* pawn move (e.g. "e4") */
   223         move->piece = PAWN;
   224         move->tofile = fileidx(mstr[0]);
   225         move->torow = rowidx(mstr[1]);
   226     } else if (len == 3) {
   227         if (strcmp(mstr, "O-O") == 0) {
   228             /* king side castling */
   229             move->piece = KING;
   230             move->fromfile = fileidx('e');
   231             move->tofile = fileidx('g');
   232             move->fromrow = move->torow = mycolor == WHITE ? 0 : 7;
   233         } else {
   234             /* move (e.g. "Nf3") */
   235             move->piece = getpiece(mstr[0]);
   236             move->tofile = fileidx(mstr[1]);
   237             move->torow = rowidx(mstr[2]);
   238         }
   240     } else if (len == 4) {
   241         move->piece = getpiece(mstr[0]);
   242         if (mstr[1] == 'x') {
   243             /* capture (e.g. "Nxf3", "dxe5") */
   244             move->capture = TRUE;
   245             if (!move->piece) {
   246                 move->piece = PAWN;
   247                 move->fromfile = fileidx(mstr[0]);
   248             }
   249         } else {
   250             /* move (e.g. "Ndf3") */
   251             move->fromfile = fileidx(mstr[1]);
   252         }
   253         move->tofile = fileidx(mstr[2]);
   254         move->torow = rowidx(mstr[3]);
   255     } else if (len == 5) {
   256         if (strcmp(mstr, "O-O-O") == 0) {
   257             /* queen side castling "O-O-O" */
   258             move->piece = KING;
   259             move->fromfile = fileidx('e');
   260             move->tofile = fileidx('c');
   261             move->fromrow = move->torow = mycolor == WHITE ? 0 : 7;
   262         } else {
   263             move->piece = getpiece(mstr[0]);
   264             if (mstr[2] == 'x') {
   265                 move->capture = TRUE;
   266                 if (move->piece) {
   267                     /* capture (e.g. "Ndxf3") */
   268                     move->fromfile = fileidx(mstr[1]);
   269                 } else {
   270                     /* long notation capture (e.g. "e5xf6") */
   271                     move->piece = PAWN;
   272                     move->fromfile = fileidx(mstr[0]);
   273                     move->fromrow = rowidx(mstr[1]);
   274                 }
   275             } else {
   276                 /* long notation move (e.g. "Nc5a4") */
   277                 move->fromfile = fileidx(mstr[1]);
   278                 move->fromrow = rowidx(mstr[2]);
   279             }
   280             move->tofile = fileidx(mstr[3]);
   281             move->torow = rowidx(mstr[4]);
   282         }
   283     } else if (len == 6) {
   284         /* long notation capture (e.g. "Nc5xf3") */
   285         if (mstr[3] == 'x') {
   286             move->capture = TRUE;
   287             move->piece = getpiece(mstr[0]);
   288             move->fromfile = fileidx(mstr[1]);
   289             move->fromrow = rowidx(mstr[2]);
   290             move->tofile = fileidx(mstr[4]);
   291             move->torow = rowidx(mstr[5]);
   292         }
   293     }
   296     if (move->piece) {
   297         move->piece |= mycolor;
   298         if (move->fromfile == POS_UNSPECIFIED
   299             || move->fromrow == POS_UNSPECIFIED) {
   300             return getlocation(board, move) && chkidx(move);
   301         } else {
   302             return chkidx(move);
   303         }
   304     } else {
   305         return FALSE;
   306     }
   307     // TODO: return status code to indicate the error type
   308 }
   310 static int sendmove(Board board, uint8_t mycolor, int opponent) {
   311     const size_t buflen = 8;
   312     char movestr[buflen];
   313     _Bool remisrejected = FALSE;
   314     uint8_t code;
   316     while (1) {
   317         move(boardy+3, 0);
   318         if (remisrejected) {
   319             printw(
   320                 "Use chess notation to enter your move.\n"
   321                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   322                 "Type your move: ");
   323         } else {
   324             printw(
   325                 "Use chess notation to enter your move.\n"
   326                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   327                 "Type your move: ");
   328         }
   329         clrtoeol();
   330         refresh();
   331         getnstr(movestr, buflen);
   333         if (strncmp(movestr, "surr", buflen) == 0) {
   334             printw("You surrendered!");
   335             refresh();
   336             net_send_code(opponent, NETCODE_SURRENDER);
   337             return 1;
   338         } else if (strncmp(movestr, "remis", buflen) == 0) {
   339             if (!remisrejected) {
   340                 net_send_code(opponent, NETCODE_REMIS);
   341                 printw("Remis offer sent - waiting for acceptance...");
   342                 refresh();
   343                 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   344                     printw("\rRemis accepted!");
   345                     clrtoeol();
   346                     refresh();
   347                     return 1;
   348                 } else {
   349                     remisrejected = TRUE;
   350                 }
   351             }
   352         } else {
   353             Move move;
   354             if (eval_move(board, mycolor, movestr, &move)) {
   355                 net_send_code(opponent, NETCODE_MOVE);
   356                 net_send_data(opponent, &move, sizeof(Move));
   357                 code = net_recieve_code(opponent);
   358                 move.check = code == NETCODE_CHECK;
   359                 move.checkmate = code == NETCODE_CHECKMATE;
   360                 // TODO: record move
   361                 if (code == NETCODE_DECLINE) {
   362                     printw("Invalid move.");
   363                     clrtoeol();
   364                 } else {
   365                     apply_move(board, &move);
   366                     if (move.checkmate) {
   367                         printw("Checkmate!");
   368                         return 1;
   369                     }
   370                 }
   371             } else {
   372                 printw("Can't interpret move - please use algebraic notation.");
   373             }
   374         }
   375     }
   376 }
   378 static int recvmove(Board board, int opponent) {
   380     while (1) {
   381         move(boardy+3, 0);
   382         printw("Awaiting opponent move...");
   383         clrtoeol();
   384         refresh();
   386         // TODO: nonblocking
   387         uint32_t code = net_recieve_code(opponent);
   389         Move move;
   390         switch (code) {
   391             case NETCODE_SURRENDER:
   392                 printw("\rYour opponent surrendered!");
   393                 clrtoeol();
   394                 return 1;
   395             case NETCODE_REMIS:
   396                 if (prompt_yesno(
   397                     "\rYour opponent offers remis - do you accept")) {
   398                     printw("\rRemis accepted!");
   399                     clrtoeol();
   400                     net_send_code(opponent, NETCODE_ACCEPT);
   401                     return 1;
   402                 } else {
   403                     net_send_code(opponent, NETCODE_DECLINE);
   404                 }
   405                 break;
   406             case NETCODE_MOVE:
   407                 net_recieve_data(opponent, &move, sizeof(Move));
   408                 if (validate_move(board, &move)) {
   409                     apply_move(board, &move);
   410                     // TODO: record move
   411                     if (move.check) {
   412                         net_send_code(opponent, NETCODE_CHECK);
   413                     } else if (move.checkmate) {
   414                         net_send_code(opponent, NETCODE_CHECKMATE);
   415                     } else {
   416                         net_send_code(opponent, NETCODE_ACCEPT);
   417                     }
   418                     return 0;
   419                 } else {
   420                     net_send_code(opponent, NETCODE_DECLINE);
   421                 }
   422         }
   423     }
   424 }
   426 void game_start(Settings *settings, int opponent) {
   427     _Bool myturn = is_server(settings) ==
   428         (settings->gameinfo.servercolor == WHITE);
   429     uint8_t mycolor = myturn ? WHITE:BLACK;
   431     _Bool running;
   433     Board board = {
   434         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   435         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   436         {0,     0,       0,       0,      0,     0,       0,       0},
   437         {0,     0,       0,       0,      0,     0,       0,       0},
   438         {0,     0,       0,       0,      0,     0,       0,       0},
   439         {0,     0,       0,       0,      0,     0,       0,       0},
   440         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   441         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   442     };
   444     do {
   445         clear();
   446         draw_board(board, mycolor);
   447         if (myturn) {
   448             running = !sendmove(board, mycolor, opponent);
   449         } else {
   450             running = !recvmove(board, opponent);
   451             flushinp(); // flush any input the user hacked in while waiting
   452         }
   453         myturn ^= TRUE;
   454     }  while (running);
   456     mvaddstr(getmaxy(tchess_window)-1, 0,
   457         "Game has ended. Press any key to leave...");
   458     getch();
   459 }

mercurial