src/game.c

Mon, 31 Mar 2014 11:16:32 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 31 Mar 2014 11:16:32 +0200
changeset 19
6a26114297a1
parent 18
6008840b859e
child 21
2e5846019b4f
permissions
-rw-r--r--

moved chess rules to separate lib

     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(Board board, MoveListRoot *movelist, 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             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 = mycolor == WHITE ? boardy-y : boardy-7+y;
    55             int cx = 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 = mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
    65         int y = 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 = movelist->first;
    77     while (logelem) {
    78         logi++;
    79         if (logi % 2 == 0) {
    80             if ((logi - 2) % 4 == 0) {
    81                 logy++;
    82                 wmove(tchess_window, logy, logx);
    83             }
    84             printw("%d. ", logi / 2);
    85         }
    87         if (logelem) {
    88             Move move = logelem->move;
    89             char logstr[] = {
    90                 getpiecechr(move.piece),
    91                 filechr(move.fromfile), rowchr(move.fromrow),
    92                 move.capture ? 'x':'\0',
    93                 filechr(move.tofile), rowchr(move.torow),
    94                 move.check ? '+' : (move.checkmate ? '#' : 
    95                     (move.promotion ? '=' : '\0')),
    96                 move.promotion ? getpiecechr(move.promotion) : '\0',
    97                 ' '
    98             };
    99             for (int stri = 0 ; stri < sizeof(logstr) ; stri++) {
   100                 if (logstr[stri]) {
   101                     addch(logstr[stri]);
   102                 }
   103             }
   105             logelem = logelem->next;
   106         }
   107     }
   108 }
   111 static int sendmove(Board board, MoveListRoot *movelist,
   112     uint8_t mycolor, int opponent) {
   114     const size_t buflen = 8;
   115     char movestr[buflen];
   116     _Bool remisrejected = FALSE;
   117     uint8_t code;
   119     int inputy = getmaxy(tchess_window) - 6;
   120     while (1) {
   121         move(inputy, 0);
   122         if (remisrejected) {
   123             printw(
   124                 "Use chess notation to enter your move.\n"
   125                 "Remis offer rejected - type 'surr' to surrender.      \n\n"
   126                 "Type your move: ");
   127         } else {
   128             printw(
   129                 "Use chess notation to enter your move.\n"
   130                 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
   131                 "Type your move: ");
   132         }
   133         clrtoeol();
   134         refresh();
   135         getnstr(movestr, buflen);
   137         if (strncmp(movestr, "surr", buflen) == 0) {
   138             printw("You surrendered!");
   139             clrtoeol();
   140             refresh();
   141             net_send_code(opponent, NETCODE_SURRENDER);
   142             return 1;
   143         } else if (strncmp(movestr, "remis", buflen) == 0) {
   144             if (!remisrejected) {
   145                 net_send_code(opponent, NETCODE_REMIS);
   146                 printw("Remis offer sent - waiting for acceptance...");
   147                 refresh();
   148                 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
   149                     printw("\rRemis accepted!");
   150                     clrtoeol();
   151                     refresh();
   152                     return 1;
   153                 } else {
   154                     remisrejected = TRUE;
   155                 }
   156             }
   157         } else {
   158             Move move;
   159             int eval_result = eval_move(board, mycolor, movestr, &move);
   160             switch (eval_result) {
   161             case VALID_MOVE_SYNTAX:
   162                 net_send_code(opponent, NETCODE_MOVE);
   163                 net_send_data(opponent, &move, sizeof(Move));
   164                 code = net_recieve_code(opponent);
   165                 move.check = code == NETCODE_CHECK;
   166                 move.checkmate = code == NETCODE_CHECKMATE;
   167                 addmove(movelist, &move);
   168                 if (code == NETCODE_DECLINE) {
   169                     printw("Invalid move.");
   170                 } else {
   171                     apply_move(board, &move);
   172                     if (move.checkmate) {
   173                         printw("Checkmate!");
   174                         clrtoeol();
   175                         return 1;
   176                     } else {
   177                         return 0;
   178                     }
   179                 }
   180                 break;
   181             case AMBIGUOUS_MOVE:
   182                 printw("Ambiguous move - please specify the piece to move.");
   183                 break;
   184             case INVALID_POSITION:
   185                 printw("Cannot find the piece that shall be moved.");
   186                 break;
   187             case NEED_PROMOTION:
   188                 printw("You need to promote the pawn (append \"=Q\" e.g.)!");
   189                 break;
   190             default:
   191                 printw("Can't interpret move - please use algebraic notation.");
   192             }
   193             clrtoeol();
   194         }
   195     }
   196 }
   198 static int recvmove(Board board, MoveListRoot *movelist, int opponent) {
   200     int inputy = getmaxy(tchess_window) - 6;
   201     while (1) {
   202         move(inputy, 0);
   203         printw("Awaiting opponent move...");
   204         clrtoeol();
   205         refresh();
   207         // TODO: nonblocking
   208         uint32_t code = net_recieve_code(opponent);
   210         Move move;
   211         switch (code) {
   212             case NETCODE_SURRENDER:
   213                 printw("\rYour opponent surrendered!");
   214                 clrtoeol();
   215                 return 1;
   216             case NETCODE_REMIS:
   217                 if (prompt_yesno(
   218                     "\rYour opponent offers remis - do you accept")) {
   219                     printw("\rRemis accepted!");
   220                     clrtoeol();
   221                     net_send_code(opponent, NETCODE_ACCEPT);
   222                     return 1;
   223                 } else {
   224                     net_send_code(opponent, NETCODE_DECLINE);
   225                 }
   226                 break;
   227             case NETCODE_MOVE:
   228                 net_recieve_data(opponent, &move, sizeof(Move));
   229                 if (validate_move(board, &move)) {
   230                     apply_move(board, &move);
   231                     addmove(movelist, &move);
   232                     if (move.check) {
   233                         net_send_code(opponent, NETCODE_CHECK);
   234                     } else if (move.checkmate) {
   235                         net_send_code(opponent, NETCODE_CHECKMATE);
   236                     } else {
   237                         net_send_code(opponent, NETCODE_ACCEPT);
   238                     }
   239                     return 0;
   240                 } else {
   241                     net_send_code(opponent, NETCODE_DECLINE);
   242                 }
   243         }
   244     }
   245 }
   247 void freemovelist(MoveListRoot* list) {
   248     MoveList *elem;
   249     elem = list->first;
   250     while (elem) {
   251         MoveList *cur = elem;
   252         elem = elem->next;
   253         free(cur);
   254     };
   255     free(list);
   256 }
   258 void addmove(MoveListRoot* list, Move *move) {
   259     MoveList *elem = malloc(sizeof(MoveList));
   260     elem->next = NULL;
   261     elem->move = *move;
   263     if (list->last) {
   264         list->last->next = elem;
   265         list->last = elem;
   266     } else {
   267         list->first = list->last = elem;
   268     }
   269 }
   271 void game_start(Settings *settings, int opponent) {
   272     _Bool myturn = is_server(settings) ==
   273         (settings->gameinfo.servercolor == WHITE);
   274     uint8_t mycolor = myturn ? WHITE:BLACK;
   276     _Bool running;
   278     MoveListRoot* movelist = calloc(1, sizeof(MoveListRoot));
   280     Board board = {
   281         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   282         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   283         {0,     0,       0,       0,      0,     0,       0,       0},
   284         {0,     0,       0,       0,      0,     0,       0,       0},
   285         {0,     0,       0,       0,      0,     0,       0,       0},
   286         {0,     0,       0,       0,      0,     0,       0,       0},
   287         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   288         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   289     };
   291     do {
   292         clear();
   293         draw_board(board, movelist, mycolor);
   294         if (myturn) {
   295             running = !sendmove(board, movelist, mycolor, opponent);
   296         } else {
   297             running = !recvmove(board, movelist, opponent);
   298             flushinp(); // flush any input the user hacked in while waiting
   299         }
   300         myturn ^= TRUE;
   301     }  while (running);
   303     freemovelist(movelist);
   305     mvaddstr(getmaxy(tchess_window)-1, 0,
   306         "Game has ended. Press any key to leave...");
   307     getch();
   308 }

mercurial