src/chess/rules.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 25
3ab0c2e1a4e2
child 28
0c1371488d87
permissions
-rw-r--r--

moved checkmate and stalemate flags to gamestate

universe@19 1 /*
universe@19 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@19 3 *
universe@19 4 * Copyright 2014 Mike Becker. All rights reserved.
universe@19 5 *
universe@19 6 * Redistribution and use in source and binary forms, with or without
universe@19 7 * modification, are permitted provided that the following conditions are met:
universe@19 8 *
universe@19 9 * 1. Redistributions of source code must retain the above copyright
universe@19 10 * notice, this list of conditions and the following disclaimer.
universe@19 11 *
universe@19 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@19 13 * notice, this list of conditions and the following disclaimer in the
universe@19 14 * documentation and/or other materials provided with the distribution.
universe@19 15 *
universe@19 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@19 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@19 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@19 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@19 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@19 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@19 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@19 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@19 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@19 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@19 26 * POSSIBILITY OF SUCH DAMAGE.
universe@19 27 *
universe@19 28 */
universe@19 29
universe@19 30 #include "rules.h"
universe@19 31 #include "chess.h"
universe@19 32 #include <string.h>
universe@23 33 #include <stdlib.h>
universe@23 34
universe@23 35 void gamestate_cleanup(GameState *gamestate) {
universe@23 36 MoveList *elem;
universe@23 37 elem = gamestate->movelist;
universe@23 38 while (elem) {
universe@23 39 MoveList *cur = elem;
universe@23 40 elem = elem->next;
universe@23 41 free(cur);
universe@23 42 };
universe@23 43 }
universe@23 44
universe@23 45 static void addmove(GameState* gamestate, Move *move) {
universe@23 46 MoveList *elem = malloc(sizeof(MoveList));
universe@23 47 elem->next = NULL;
universe@23 48 elem->move = *move;
universe@23 49
universe@23 50 if (gamestate->lastmove) {
universe@23 51 gamestate->lastmove->next = elem;
universe@23 52 gamestate->lastmove = elem;
universe@23 53 } else {
universe@23 54 gamestate->movelist = gamestate->lastmove = elem;
universe@23 55 }
universe@23 56 }
universe@19 57
universe@19 58 char getpiecechr(uint8_t piece) {
universe@19 59 switch (piece & PIECE_MASK) {
universe@19 60 case ROOK: return 'R';
universe@19 61 case KNIGHT: return 'N';
universe@19 62 case BISHOP: return 'B';
universe@19 63 case QUEEN: return 'Q';
universe@19 64 case KING: return 'K';
universe@19 65 default: return '\0';
universe@19 66 }
universe@19 67 }
universe@19 68
universe@19 69 uint8_t getpiece(char c) {
universe@19 70 switch (c) {
universe@19 71 case 'R': return ROOK;
universe@19 72 case 'N': return KNIGHT;
universe@19 73 case 'B': return BISHOP;
universe@19 74 case 'Q': return QUEEN;
universe@19 75 case 'K': return KING;
universe@19 76 default: return 0;
universe@19 77 }
universe@19 78 }
universe@19 79
universe@23 80 static int getlocation(GameState *gamestate, Move *move) {
universe@19 81 uint8_t piece = move->piece & PIECE_MASK;
universe@19 82 switch (piece) {
universe@23 83 case PAWN: return pawn_getlocation(gamestate, move);
universe@23 84 case ROOK: return rook_getlocation(gamestate, move);
universe@23 85 case KNIGHT: return knight_getlocation(gamestate, move);
universe@23 86 case BISHOP: return bishop_getlocation(gamestate, move);
universe@23 87 case QUEEN: return queen_getlocation(gamestate, move);
universe@23 88 case KING: return king_getlocation(gamestate, move);
universe@19 89 default: return INVALID_MOVE_SYNTAX;
universe@19 90 }
universe@19 91 }
universe@19 92
universe@25 93 _Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color) {
universe@25 94 Move threats[16];
universe@25 95 int threatcount = 0;
universe@25 96 for (uint8_t r = 0 ; r < 8 ; r++) {
universe@25 97 for (uint8_t f = 0 ; f < 8 ; f++) {
universe@25 98 if ((gamestate->board[r][f] & COLOR_MASK) == color) {
universe@25 99 threats[threatcount].piece = gamestate->board[r][f];
universe@25 100 threats[threatcount].fromrow = r;
universe@25 101 threats[threatcount].fromfile = f;
universe@25 102 threats[threatcount].torow = row;
universe@25 103 threats[threatcount].tofile = file;
universe@25 104 threatcount++;
universe@25 105 }
universe@25 106 }
universe@25 107 }
universe@25 108
universe@25 109 for (int i = 0 ; i < threatcount ; i++) {
universe@25 110 if (validate_move(gamestate, &(threats[i]))) {
universe@25 111 return 1;
universe@25 112 }
universe@25 113 }
universe@25 114
universe@25 115 return 0;
universe@25 116 }
universe@19 117
universe@23 118 void apply_move(GameState *gamestate, Move *move) {
universe@19 119 uint8_t piece = move->piece & PIECE_MASK;
universe@19 120 uint8_t color = move->piece & COLOR_MASK;
universe@19 121
universe@19 122 /* en passant capture */
universe@19 123 if (move->capture && piece == PAWN &&
universe@23 124 mdst(gamestate->board, move) == 0) {
universe@23 125 gamestate->board[move->fromrow][move->tofile] = 0;
universe@19 126 }
universe@19 127
universe@19 128 /* remove old en passant threats */
universe@19 129 for (uint8_t file = 0 ; file < 8 ; file++) {
universe@23 130 gamestate->board[3][file] &= ~ENPASSANT_THREAT;
universe@23 131 gamestate->board[4][file] &= ~ENPASSANT_THREAT;
universe@19 132 }
universe@19 133
universe@19 134 /* add new en passant threat */
universe@19 135 if (piece == PAWN && (
universe@19 136 (move->fromrow == 1 && move->torow == 3) ||
universe@19 137 (move->fromrow == 6 && move->torow == 4))) {
universe@19 138 move->piece |= ENPASSANT_THREAT;
universe@19 139 }
universe@19 140
universe@19 141 /* move (and maybe capture or promote) */
universe@23 142 msrc(gamestate->board, move) = 0;
universe@19 143 if (move->promotion) {
universe@23 144 mdst(gamestate->board, move) = move->promotion;
universe@19 145 } else {
universe@23 146 mdst(gamestate->board, move) = move->piece;
universe@19 147 }
universe@19 148
universe@19 149 /* castling */
universe@19 150 if (piece == KING &&
universe@19 151 move->fromfile == fileidx('e')) {
universe@19 152
universe@19 153 if (move->tofile == fileidx('g')) {
universe@23 154 gamestate->board[move->torow][fileidx('h')] = 0;
universe@23 155 gamestate->board[move->torow][fileidx('f')] = color|ROOK;
universe@19 156 } else if (move->tofile == fileidx('c')) {
universe@23 157 gamestate->board[move->torow][fileidx('a')] = 0;
universe@23 158 gamestate->board[move->torow][fileidx('d')] = color|ROOK;
universe@19 159 }
universe@19 160 }
universe@23 161
universe@23 162 addmove(gamestate, move);
universe@19 163 }
universe@19 164
universe@23 165 _Bool validate_move(GameState *gamestate, Move *move) {
universe@19 166 _Bool result;
universe@19 167
universe@19 168 /* validate indices (don't trust opponent) */
universe@19 169 if (!chkidx(move)) {
universe@19 170 return 0;
universe@19 171 }
universe@19 172
universe@21 173 /* must move */
universe@21 174 if (move->fromfile == move->tofile && move->fromrow == move->torow) {
universe@21 175 return 0;
universe@21 176 }
universe@21 177
universe@19 178 /* does piece exist */
universe@23 179 result = msrc(gamestate->board, move) == move->piece;
universe@19 180
universe@19 181 /* can't capture own pieces */
universe@23 182 if ((mdst(gamestate->board, move) & COLOR_MASK)
universe@23 183 == (move->piece & COLOR_MASK)) {
universe@19 184 return 0;
universe@19 185 }
universe@19 186
universe@19 187 /* validate individual rules */
universe@19 188 switch (move->piece & PIECE_MASK) {
universe@19 189 case PAWN:
universe@23 190 result = result && pawn_chkrules(gamestate, move);
universe@23 191 result = result && !pawn_isblocked(gamestate, move);
universe@19 192 break;
universe@19 193 case ROOK:
universe@19 194 result = result && rook_chkrules(move);
universe@23 195 result = result && !rook_isblocked(gamestate, move);
universe@19 196 break;
universe@19 197 case KNIGHT:
universe@19 198 result = result && knight_chkrules(move);
universe@23 199 result = result && !knight_isblocked(gamestate, move);
universe@19 200 break;
universe@19 201 case BISHOP:
universe@19 202 result = result && bishop_chkrules(move);
universe@23 203 result = result && !bishop_isblocked(gamestate, move);
universe@19 204 break;
universe@19 205 case QUEEN:
universe@19 206 result = result && queen_chkrules(move);
universe@23 207 result = result && !queen_isblocked(gamestate, move);
universe@19 208 break;
universe@19 209 case KING:
universe@23 210 result = result && king_chkrules(gamestate, move);
universe@23 211 result = result && !king_isblocked(gamestate, move);
universe@19 212 break;
universe@19 213 default:
universe@19 214 result = 0;
universe@19 215 }
universe@19 216
universe@25 217 /* cancel processing to avoid recursion overflow with is_covered() */
universe@25 218 if (!result) {
universe@25 219 return 0;
universe@25 220 }
universe@25 221
universe@19 222 /* is piece pinned */
universe@19 223 // TODO: make it so
universe@19 224
universe@25 225 /* correct check and checkmate flags (move is still valid) */
universe@19 226 // TODO: make it so
universe@19 227
universe@19 228 return result;
universe@19 229 }
universe@19 230
universe@23 231 int eval_move(GameState *gamestate, char *mstr, Move *move) {
universe@19 232 memset(move, 0, sizeof(Move));
universe@19 233 move->fromfile = POS_UNSPECIFIED;
universe@19 234 move->fromrow = POS_UNSPECIFIED;
universe@19 235
universe@19 236 size_t len = strlen(mstr);
universe@19 237
universe@19 238 /* evaluate check/checkmate flags */
universe@19 239 if (mstr[len-1] == '+') {
universe@19 240 len--; mstr[len] = '\0';
universe@19 241 move->check = 1;
universe@19 242 } else if (mstr[len-1] == '#') {
universe@19 243 len--; mstr[len] = '\0';
universe@27 244 /* ignore - validation should set game state */
universe@19 245 }
universe@19 246
universe@19 247 /* evaluate promotion */
universe@19 248 if (len > 3 && mstr[len-2] == '=') {
universe@19 249 move->promotion = getpiece(mstr[len-1]);
universe@19 250 if (!move->promotion) {
universe@19 251 return INVALID_MOVE_SYNTAX;
universe@19 252 } else {
universe@23 253 move->promotion |= gamestate->mycolor;
universe@19 254 len -= 2;
universe@19 255 mstr[len] = 0;
universe@19 256 }
universe@19 257 }
universe@19 258
universe@19 259 if (len == 2) {
universe@19 260 /* pawn move (e.g. "e4") */
universe@19 261 move->piece = PAWN;
universe@19 262 move->tofile = fileidx(mstr[0]);
universe@19 263 move->torow = rowidx(mstr[1]);
universe@19 264 } else if (len == 3) {
universe@19 265 if (strcmp(mstr, "O-O") == 0) {
universe@19 266 /* king side castling */
universe@19 267 move->piece = KING;
universe@19 268 move->fromfile = fileidx('e');
universe@19 269 move->tofile = fileidx('g');
universe@23 270 move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7;
universe@19 271 } else {
universe@19 272 /* move (e.g. "Nf3") */
universe@19 273 move->piece = getpiece(mstr[0]);
universe@19 274 move->tofile = fileidx(mstr[1]);
universe@19 275 move->torow = rowidx(mstr[2]);
universe@19 276 }
universe@19 277
universe@19 278 } else if (len == 4) {
universe@19 279 move->piece = getpiece(mstr[0]);
universe@19 280 if (!move->piece) {
universe@19 281 move->piece = PAWN;
universe@19 282 move->fromfile = fileidx(mstr[0]);
universe@19 283 }
universe@19 284 if (mstr[1] == 'x') {
universe@19 285 /* capture (e.g. "Nxf3", "dxe5") */
universe@19 286 move->capture = 1;
universe@19 287 } else {
universe@19 288 /* move (e.g. "Ndf3", "N2c3", "e2e4") */
universe@19 289 if (isfile(mstr[1])) {
universe@19 290 move->fromfile = fileidx(mstr[1]);
universe@19 291 if (move->piece == PAWN) {
universe@19 292 move->piece = 0;
universe@19 293 }
universe@19 294 } else {
universe@19 295 move->fromrow = rowidx(mstr[1]);
universe@19 296 }
universe@19 297 }
universe@19 298 move->tofile = fileidx(mstr[2]);
universe@19 299 move->torow = rowidx(mstr[3]);
universe@19 300 } else if (len == 5) {
universe@19 301 if (strcmp(mstr, "O-O-O") == 0) {
universe@19 302 /* queen side castling "O-O-O" */
universe@19 303 move->piece = KING;
universe@19 304 move->fromfile = fileidx('e');
universe@19 305 move->tofile = fileidx('c');
universe@23 306 move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7;
universe@19 307 } else {
universe@19 308 move->piece = getpiece(mstr[0]);
universe@19 309 if (mstr[2] == 'x') {
universe@19 310 move->capture = 1;
universe@19 311 if (move->piece) {
universe@19 312 /* capture (e.g. "Ndxf3") */
universe@19 313 move->fromfile = fileidx(mstr[1]);
universe@19 314 } else {
universe@19 315 /* long notation capture (e.g. "e5xf6") */
universe@19 316 move->piece = PAWN;
universe@19 317 move->fromfile = fileidx(mstr[0]);
universe@19 318 move->fromrow = rowidx(mstr[1]);
universe@19 319 }
universe@19 320 } else {
universe@19 321 /* long notation move (e.g. "Nc5a4") */
universe@19 322 move->fromfile = fileidx(mstr[1]);
universe@19 323 move->fromrow = rowidx(mstr[2]);
universe@19 324 }
universe@19 325 move->tofile = fileidx(mstr[3]);
universe@19 326 move->torow = rowidx(mstr[4]);
universe@19 327 }
universe@19 328 } else if (len == 6) {
universe@19 329 /* long notation capture (e.g. "Nc5xf3") */
universe@19 330 if (mstr[3] == 'x') {
universe@19 331 move->capture = 1;
universe@19 332 move->piece = getpiece(mstr[0]);
universe@19 333 move->fromfile = fileidx(mstr[1]);
universe@19 334 move->fromrow = rowidx(mstr[2]);
universe@19 335 move->tofile = fileidx(mstr[4]);
universe@19 336 move->torow = rowidx(mstr[5]);
universe@19 337 }
universe@19 338 }
universe@19 339
universe@19 340
universe@19 341 if (move->piece) {
universe@23 342 if (move->piece == PAWN
universe@23 343 && move->torow == (gamestate->mycolor==WHITE?7:0)
universe@19 344 && !move->promotion) {
universe@19 345 return NEED_PROMOTION;
universe@19 346 }
universe@19 347
universe@23 348 move->piece |= gamestate->mycolor;
universe@19 349 if (move->fromfile == POS_UNSPECIFIED
universe@19 350 || move->fromrow == POS_UNSPECIFIED) {
universe@23 351 return getlocation(gamestate, move);
universe@19 352 } else {
universe@19 353 return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
universe@19 354 }
universe@19 355 } else {
universe@19 356 return INVALID_MOVE_SYNTAX;
universe@19 357 }
universe@19 358 }

mercurial