src/game.c

Thu, 03 Apr 2014 16:07:04 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 03 Apr 2014 16:07:04 +0200
changeset 27
efeb98bc69c9
parent 26
e0a76ee1bb2b
child 28
0c1371488d87
permissions
-rw-r--r--

moved checkmate and stalemate flags to gamestate

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

mercurial