src/game.c

Sat, 29 Mar 2014 14:46:33 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 29 Mar 2014 14:46:33 +0100
changeset 17
2aed5418e142
parent 16
a298c6637c30
child 18
6008840b859e
permissions
-rw-r--r--

implemented bishop rules

universe@6 1 /*
universe@6 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@6 3 *
universe@6 4 * Copyright 2014 Mike Becker. All rights reserved.
universe@6 5 *
universe@6 6 * Redistribution and use in source and binary forms, with or without
universe@6 7 * modification, are permitted provided that the following conditions are met:
universe@6 8 *
universe@6 9 * 1. Redistributions of source code must retain the above copyright
universe@6 10 * notice, this list of conditions and the following disclaimer.
universe@6 11 *
universe@6 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@6 13 * notice, this list of conditions and the following disclaimer in the
universe@6 14 * documentation and/or other materials provided with the distribution.
universe@6 15 *
universe@6 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@6 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@6 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@6 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@6 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@6 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@6 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@6 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@6 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@6 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@6 26 * POSSIBILITY OF SUCH DAMAGE.
universe@6 27 *
universe@6 28 */
universe@6 29
universe@6 30 #include "game.h"
universe@7 31 #include "input.h"
universe@10 32 #include "rules/rules.h"
universe@7 33 #include <ncurses.h>
universe@7 34 #include <string.h>
universe@7 35
universe@7 36 static const uint8_t boardx = 10, boardy = 10;
universe@7 37
universe@8 38 static void draw_board(Board board, uint8_t mycolor) {
universe@7 39
universe@7 40 for (uint8_t y = 0 ; y < 8 ; y++) {
universe@7 41 for (uint8_t x = 0 ; x < 8 ; x++) {
universe@7 42 uint8_t col = board[y][x] & COLOR_MASK;
universe@7 43 uint8_t piece = board[y][x] & PIECE_MASK;
universe@7 44 char piecec = ' ';
universe@7 45 switch (piece) {
universe@7 46 case PAWN: piecec = 'P'; break;
universe@7 47 case ROOK: piecec = 'R'; break;
universe@7 48 case KNIGHT: piecec = 'N'; break;
universe@7 49 case BISHOP: piecec = 'B'; break;
universe@7 50 case QUEEN: piecec = 'Q'; break;
universe@7 51 case KING: piecec = 'K'; break;
universe@7 52 }
universe@7 53
universe@7 54 attrset((col == WHITE ? A_BOLD : A_DIM) |
universe@7 55 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW));
universe@7 56
universe@8 57 int cy = mycolor == WHITE ? boardy-y : boardy-7+y;
universe@8 58 int cx = mycolor == WHITE ? boardx+x*3 : boardx+21-x*3;
universe@8 59 mvaddch(cy, cx, ' ');
universe@8 60 mvaddch(cy, cx+1, piecec);
universe@8 61 mvaddch(cy, cx+2, ' ');
universe@7 62 }
universe@7 63 }
universe@7 64
universe@7 65 attrset(A_NORMAL);
universe@7 66 for (uint8_t i = 0 ; i < 8 ; i++) {
universe@8 67 int x = mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
universe@8 68 int y = mycolor == WHITE ? boardy-i : boardy-7+i;
universe@8 69 mvaddch(boardy+1, x, 'a'+i);
universe@8 70 mvaddch(y, boardx-2, '1'+i);
universe@7 71 }
universe@7 72 }
universe@7 73
universe@13 74 /**
universe@13 75 * Applies a move and deletes captured pieces.
universe@13 76 *
universe@13 77 * @param board the current board state
universe@13 78 * @param move the move to apply
universe@13 79 */
universe@8 80 static void apply_move(Board board, Move *move) {
universe@15 81 uint8_t piece = move->piece & PIECE_MASK;
universe@15 82 uint8_t color = move->piece & COLOR_MASK;
universe@15 83
universe@15 84 /* en passant capture */
universe@15 85 if (move->capture && piece == PAWN &&
universe@15 86 mdst(board, move) == 0) {
universe@15 87 board[move->fromrow][move->tofile] = 0;
universe@15 88 }
universe@15 89
universe@15 90 /* remove old en passant threats */
universe@15 91 for (uint8_t file = 0 ; file < 8 ; file++) {
universe@15 92 board[3][file] &= ~ENPASSANT_THREAT;
universe@15 93 board[4][file] &= ~ENPASSANT_THREAT;
universe@15 94 }
universe@15 95
universe@15 96 /* add new en passant threat */
universe@15 97 if (piece == PAWN && (
universe@15 98 (move->fromrow == 1 && move->torow == 3) ||
universe@15 99 (move->fromrow == 6 && move->torow == 4))) {
universe@15 100 move->piece |= ENPASSANT_THREAT;
universe@15 101 }
universe@15 102
universe@15 103 /* move (and maybe capture) */
universe@14 104 msrc(board, move) = 0;
universe@14 105 mdst(board, move) = move->piece;
universe@9 106
universe@9 107 /* castling */
universe@15 108 if (piece == KING &&
universe@9 109 move->fromfile == fileidx('e')) {
universe@9 110
universe@9 111 if (move->tofile == fileidx('g')) {
universe@9 112 board[move->torow][fileidx('h')] = 0;
universe@9 113 board[move->torow][fileidx('f')] = color|ROOK;
universe@9 114 } else if (move->tofile == fileidx('c')) {
universe@9 115 board[move->torow][fileidx('a')] = 0;
universe@9 116 board[move->torow][fileidx('d')] = color|ROOK;
universe@9 117 }
universe@9 118 }
universe@8 119 }
universe@8 120
universe@13 121 /**
universe@13 122 * Validates move by applying chess rules.
universe@13 123 * @param board the current board state
universe@13 124 * @param move the move to validate
universe@13 125 * @return TRUE, if the move complies to chess rules, FALSE otherwise
universe@13 126 */
universe@11 127 static _Bool validate_move(Board board, Move *move) {
universe@10 128 _Bool result;
universe@8 129
universe@13 130 /* validate indices (don't trust opponent) */
universe@13 131 if (!chkidx(move)) {
universe@13 132 return FALSE;
universe@13 133 }
universe@13 134
universe@8 135 /* does piece exist */
universe@14 136 result = msrc(board, move) == move->piece;
universe@8 137
universe@14 138 /* can't capture own pieces */
universe@14 139 if ((mdst(board, move) & COLOR_MASK) == (move->piece & COLOR_MASK)) {
universe@14 140 return FALSE;
universe@14 141 }
universe@14 142
universe@14 143 /* validate individual rules */
universe@10 144 switch (move->piece & PIECE_MASK) {
universe@10 145 case PAWN:
universe@10 146 result = result && pawn_chkrules(board, move);
universe@10 147 result = result && !pawn_isblocked(board, move);
universe@10 148 break;
universe@10 149 case ROOK:
universe@16 150 result = result && rook_chkrules(move);
universe@10 151 result = result && !rook_isblocked(board, move);
universe@10 152 break;
universe@10 153 case KNIGHT:
universe@16 154 result = result && knight_chkrules(move);
universe@10 155 result = result && !knight_isblocked(board, move);
universe@10 156 break;
universe@10 157 case BISHOP:
universe@16 158 result = result && bishop_chkrules(move);
universe@10 159 result = result && !bishop_isblocked(board, move);
universe@10 160 break;
universe@10 161 case QUEEN:
universe@16 162 result = result && queen_chkrules(move);
universe@10 163 result = result && !queen_isblocked(board, move);
universe@10 164 break;
universe@10 165 case KING:
universe@10 166 result = result && king_chkrules(board, move);
universe@10 167 result = result && !king_isblocked(board, move);
universe@10 168 break;
universe@10 169 default:
universe@10 170 result = FALSE;
universe@10 171 }
universe@8 172
universe@8 173 /* is piece pinned */
universe@8 174 // TODO: make it so
universe@8 175
universe@11 176 /* correct check and checkmate flags */
universe@11 177 // TODO: make it so
universe@11 178
universe@8 179 return result;
universe@8 180 }
universe@8 181
universe@12 182 /**
universe@12 183 * Maps a character to a piece.
universe@12 184 *
universe@12 185 * Does not work for pawns, since they don't have a character.
universe@12 186 *
universe@12 187 * @param c one of R,N,B,Q,K
universe@12 188 * @return numeric value for the specified piece
universe@12 189 */
universe@12 190 static uint8_t getpiece(char c) {
universe@12 191 switch (c) {
universe@12 192 case 'R': return ROOK;
universe@12 193 case 'N': return KNIGHT;
universe@12 194 case 'B': return BISHOP;
universe@12 195 case 'Q': return QUEEN;
universe@12 196 case 'K': return KING;
universe@12 197 default: return 0;
universe@12 198 }
universe@12 199 }
universe@12 200
universe@12 201 /**
universe@12 202 * Guesses the location of a piece for short algebraic notation.
universe@12 203 *
universe@12 204 * @param board the current state of the board
universe@12 205 * @param move the move date to operate on
universe@16 206 * @return status code (see rules/rules.h for the codes)
universe@12 207 */
universe@16 208 static int getlocation(Board board, Move *move) {
universe@12 209 uint8_t piece = move->piece & PIECE_MASK;
universe@12 210 switch (piece) {
universe@12 211 case PAWN: return pawn_getlocation(board, move);
universe@12 212 case ROOK: return rook_getlocation(board, move);
universe@12 213 case KNIGHT: return knight_getlocation(board, move);
universe@12 214 case BISHOP: return bishop_getlocation(board, move);
universe@12 215 case QUEEN: return queen_getlocation(board, move);
universe@12 216 case KING: return king_getlocation(board, move);
universe@16 217 default: return INVALID_MOVE_SYNTAX;
universe@12 218 }
universe@12 219 }
universe@12 220
universe@12 221 /**
universe@12 222 * Evaluates a move syntactically and stores the move data in the specified
universe@12 223 * object.
universe@12 224 *
universe@12 225 * @param board the current state of the board
universe@12 226 * @param mycolor the color of the current player
universe@12 227 * @param mstr the input string to parse
universe@12 228 * @param move a pointer to object where the move data shall be stored
universe@16 229 * @return status code (see rules/rules.h for the list of codes)
universe@12 230 */
universe@16 231 static int eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) {
universe@8 232 memset(move, 0, sizeof(Move));
universe@12 233 move->fromfile = POS_UNSPECIFIED;
universe@12 234 move->fromrow = POS_UNSPECIFIED;
universe@17 235 // TODO: promotion
universe@9 236 size_t len = strlen(mstr);
universe@8 237
universe@11 238 /* evaluate check/checkmate flags */
universe@9 239 if (mstr[len-1] == '+') {
universe@9 240 len--; mstr[len] = '\0';
universe@8 241 move->check = TRUE;
universe@11 242 } else if (mstr[len-1] == '#') {
universe@11 243 len--; mstr[len] = '\0';
universe@11 244 move->checkmate = TRUE;
universe@8 245 }
universe@8 246
universe@8 247 if (len == 2) {
universe@8 248 /* pawn move (e.g. "e4") */
universe@13 249 move->piece = PAWN;
universe@13 250 move->tofile = fileidx(mstr[0]);
universe@13 251 move->torow = rowidx(mstr[1]);
universe@8 252 } else if (len == 3) {
universe@9 253 if (strcmp(mstr, "O-O") == 0) {
universe@8 254 /* king side castling */
universe@12 255 move->piece = KING;
universe@8 256 move->fromfile = fileidx('e');
universe@9 257 move->tofile = fileidx('g');
universe@8 258 move->fromrow = move->torow = mycolor == WHITE ? 0 : 7;
universe@8 259 } else {
universe@13 260 /* move (e.g. "Nf3") */
universe@12 261 move->piece = getpiece(mstr[0]);
universe@13 262 move->tofile = fileidx(mstr[1]);
universe@13 263 move->torow = rowidx(mstr[2]);
universe@8 264 }
universe@8 265
universe@8 266 } else if (len == 4) {
universe@13 267 move->piece = getpiece(mstr[0]);
universe@15 268 if (!move->piece) {
universe@15 269 move->piece = PAWN;
universe@15 270 move->fromfile = fileidx(mstr[0]);
universe@15 271 }
universe@13 272 if (mstr[1] == 'x') {
universe@13 273 /* capture (e.g. "Nxf3", "dxe5") */
universe@13 274 move->capture = TRUE;
universe@15 275 } else {
universe@15 276 /* move (e.g. "Ndf3", "N2c3", "e2e4") */
universe@15 277 if (isfile(mstr[1])) {
universe@15 278 move->fromfile = fileidx(mstr[1]);
universe@15 279 if (move->piece == PAWN) {
universe@15 280 move->piece = 0;
universe@15 281 }
universe@15 282 } else {
universe@15 283 move->fromrow = rowidx(mstr[1]);
universe@13 284 }
universe@13 285 }
universe@13 286 move->tofile = fileidx(mstr[2]);
universe@13 287 move->torow = rowidx(mstr[3]);
universe@8 288 } else if (len == 5) {
universe@9 289 if (strcmp(mstr, "O-O-O") == 0) {
universe@9 290 /* queen side castling "O-O-O" */
universe@12 291 move->piece = KING;
universe@9 292 move->fromfile = fileidx('e');
universe@9 293 move->tofile = fileidx('c');
universe@9 294 move->fromrow = move->torow = mycolor == WHITE ? 0 : 7;
universe@9 295 } else {
universe@13 296 move->piece = getpiece(mstr[0]);
universe@13 297 if (mstr[2] == 'x') {
universe@13 298 move->capture = TRUE;
universe@13 299 if (move->piece) {
universe@13 300 /* capture (e.g. "Ndxf3") */
universe@13 301 move->fromfile = fileidx(mstr[1]);
universe@13 302 } else {
universe@13 303 /* long notation capture (e.g. "e5xf6") */
universe@13 304 move->piece = PAWN;
universe@13 305 move->fromfile = fileidx(mstr[0]);
universe@13 306 move->fromrow = rowidx(mstr[1]);
universe@13 307 }
universe@13 308 } else {
universe@13 309 /* long notation move (e.g. "Nc5a4") */
universe@13 310 move->fromfile = fileidx(mstr[1]);
universe@13 311 move->fromrow = rowidx(mstr[2]);
universe@13 312 }
universe@13 313 move->tofile = fileidx(mstr[3]);
universe@13 314 move->torow = rowidx(mstr[4]);
universe@9 315 }
universe@8 316 } else if (len == 6) {
universe@8 317 /* long notation capture (e.g. "Nc5xf3") */
universe@13 318 if (mstr[3] == 'x') {
universe@13 319 move->capture = TRUE;
universe@13 320 move->piece = getpiece(mstr[0]);
universe@13 321 move->fromfile = fileidx(mstr[1]);
universe@13 322 move->fromrow = rowidx(mstr[2]);
universe@13 323 move->tofile = fileidx(mstr[4]);
universe@13 324 move->torow = rowidx(mstr[5]);
universe@13 325 }
universe@8 326 }
universe@12 327
universe@13 328
universe@12 329 if (move->piece) {
universe@12 330 move->piece |= mycolor;
universe@13 331 if (move->fromfile == POS_UNSPECIFIED
universe@13 332 || move->fromrow == POS_UNSPECIFIED) {
universe@16 333 return getlocation(board, move);
universe@13 334 } else {
universe@16 335 return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
universe@13 336 }
universe@13 337 } else {
universe@16 338 return INVALID_MOVE_SYNTAX;
universe@12 339 }
universe@8 340 }
universe@8 341
universe@8 342 static int sendmove(Board board, uint8_t mycolor, int opponent) {
universe@7 343 const size_t buflen = 8;
universe@8 344 char movestr[buflen];
universe@7 345 _Bool remisrejected = FALSE;
universe@11 346 uint8_t code;
universe@7 347
universe@7 348 while (1) {
universe@7 349 move(boardy+3, 0);
universe@7 350 if (remisrejected) {
universe@7 351 printw(
universe@7 352 "Use chess notation to enter your move.\n"
universe@7 353 "Remis offer rejected - type 'surr' to surrender. \n\n"
universe@7 354 "Type your move: ");
universe@7 355 } else {
universe@7 356 printw(
universe@7 357 "Use chess notation to enter your move.\n"
universe@7 358 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
universe@7 359 "Type your move: ");
universe@7 360 }
universe@7 361 clrtoeol();
universe@7 362 refresh();
universe@8 363 getnstr(movestr, buflen);
universe@7 364
universe@8 365 if (strncmp(movestr, "surr", buflen) == 0) {
universe@7 366 printw("You surrendered!");
universe@16 367 clrtoeol();
universe@8 368 refresh();
universe@7 369 net_send_code(opponent, NETCODE_SURRENDER);
universe@7 370 return 1;
universe@8 371 } else if (strncmp(movestr, "remis", buflen) == 0) {
universe@7 372 if (!remisrejected) {
universe@7 373 net_send_code(opponent, NETCODE_REMIS);
universe@7 374 printw("Remis offer sent - waiting for acceptance...");
universe@7 375 refresh();
universe@7 376 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
universe@7 377 printw("\rRemis accepted!");
universe@7 378 clrtoeol();
universe@8 379 refresh();
universe@7 380 return 1;
universe@7 381 } else {
universe@7 382 remisrejected = TRUE;
universe@7 383 }
universe@7 384 }
universe@7 385 } else {
universe@8 386 Move move;
universe@16 387 int eval_result = eval_move(board, mycolor, movestr, &move);
universe@16 388 switch (eval_result) {
universe@16 389 case VALID_MOVE_SYNTAX:
universe@16 390 net_send_code(opponent, NETCODE_MOVE);
universe@16 391 net_send_data(opponent, &move, sizeof(Move));
universe@16 392 code = net_recieve_code(opponent);
universe@16 393 move.check = code == NETCODE_CHECK;
universe@16 394 move.checkmate = code == NETCODE_CHECKMATE;
universe@16 395 // TODO: record move
universe@16 396 if (code == NETCODE_DECLINE) {
universe@16 397 printw("Invalid move.");
universe@14 398 } else {
universe@16 399 apply_move(board, &move);
universe@16 400 if (move.checkmate) {
universe@16 401 printw("Checkmate!");
universe@16 402 clrtoeol();
universe@16 403 return 1;
universe@16 404 } else {
universe@16 405 return 0;
universe@16 406 }
universe@11 407 }
universe@16 408 break;
universe@16 409 case AMBIGUOUS_MOVE:
universe@16 410 printw("Ambiguous move - "
universe@16 411 "please specify the piece to move.");
universe@16 412 break;
universe@16 413 case INVALID_POSITION:
universe@16 414 printw("Cannot find the piece that shall be moved.");
universe@16 415 break;
universe@16 416 default:
universe@16 417 printw("Can't interpret move - "
universe@16 418 "please use algebraic notation.");
universe@8 419 }
universe@16 420 clrtoeol();
universe@7 421 }
universe@7 422 }
universe@7 423 }
universe@7 424
universe@11 425 static int recvmove(Board board, int opponent) {
universe@7 426
universe@7 427 while (1) {
universe@7 428 move(boardy+3, 0);
universe@7 429 printw("Awaiting opponent move...");
universe@7 430 clrtoeol();
universe@7 431 refresh();
universe@7 432
universe@7 433 // TODO: nonblocking
universe@7 434 uint32_t code = net_recieve_code(opponent);
universe@8 435
universe@8 436 Move move;
universe@7 437 switch (code) {
universe@7 438 case NETCODE_SURRENDER:
universe@7 439 printw("\rYour opponent surrendered!");
universe@7 440 clrtoeol();
universe@7 441 return 1;
universe@7 442 case NETCODE_REMIS:
universe@7 443 if (prompt_yesno(
universe@7 444 "\rYour opponent offers remis - do you accept")) {
universe@7 445 printw("\rRemis accepted!");
universe@7 446 clrtoeol();
universe@7 447 net_send_code(opponent, NETCODE_ACCEPT);
universe@7 448 return 1;
universe@7 449 } else {
universe@7 450 net_send_code(opponent, NETCODE_DECLINE);
universe@7 451 }
universe@7 452 break;
universe@7 453 case NETCODE_MOVE:
universe@8 454 net_recieve_data(opponent, &move, sizeof(Move));
universe@11 455 if (validate_move(board, &move)) {
universe@8 456 apply_move(board, &move);
universe@11 457 // TODO: record move
universe@11 458 if (move.check) {
universe@11 459 net_send_code(opponent, NETCODE_CHECK);
universe@11 460 } else if (move.checkmate) {
universe@11 461 net_send_code(opponent, NETCODE_CHECKMATE);
universe@11 462 } else {
universe@11 463 net_send_code(opponent, NETCODE_ACCEPT);
universe@11 464 }
universe@8 465 return 0;
universe@8 466 } else {
universe@8 467 net_send_code(opponent, NETCODE_DECLINE);
universe@8 468 }
universe@7 469 }
universe@7 470 }
universe@7 471 }
universe@6 472
universe@6 473 void game_start(Settings *settings, int opponent) {
universe@7 474 _Bool myturn = is_server(settings) ==
universe@7 475 (settings->gameinfo.servercolor == WHITE);
universe@8 476 uint8_t mycolor = myturn ? WHITE:BLACK;
universe@8 477
universe@7 478 _Bool running;
universe@6 479
universe@7 480 Board board = {
universe@7 481 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
universe@7 482 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN},
universe@7 483 {0, 0, 0, 0, 0, 0, 0, 0},
universe@7 484 {0, 0, 0, 0, 0, 0, 0, 0},
universe@7 485 {0, 0, 0, 0, 0, 0, 0, 0},
universe@7 486 {0, 0, 0, 0, 0, 0, 0, 0},
universe@7 487 {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN},
universe@7 488 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
universe@7 489 };
universe@7 490
universe@7 491 do {
universe@7 492 clear();
universe@8 493 draw_board(board, mycolor);
universe@7 494 if (myturn) {
universe@8 495 running = !sendmove(board, mycolor, opponent);
universe@7 496 } else {
universe@11 497 running = !recvmove(board, opponent);
universe@7 498 flushinp(); // flush any input the user hacked in while waiting
universe@7 499 }
universe@11 500 myturn ^= TRUE;
universe@7 501 } while (running);
universe@7 502
universe@7 503 mvaddstr(getmaxy(tchess_window)-1, 0,
universe@7 504 "Game has ended. Press any key to leave...");
universe@7 505 getch();
universe@6 506 }

mercurial