test/bigtestfile.c

Mon, 13 Nov 2017 13:52:00 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 13 Nov 2017 13:52:00 +0100
changeset 61
47a5fc33590a
parent 25
f82aa7afe872
permissions
-rw-r--r--

ucx is now used as external library

universe@25 1 /*
universe@25 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@25 3 *
universe@25 4 * Copyright 2014 Mike Becker. All rights reserved.
universe@25 5 *
universe@25 6 * Redistribution and use in source and binary forms, with or without
universe@25 7 * modification, are permitted provided that the following conditions are met:
universe@25 8 *
universe@25 9 * 1. Redistributions of source code must retain the above copyright
universe@25 10 * notice, this list of conditions and the following disclaimer.
universe@25 11 *
universe@25 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@25 13 * notice, this list of conditions and the following disclaimer in the
universe@25 14 * documentation and/or other materials provided with the distribution.
universe@25 15 *
universe@25 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@25 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@25 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@25 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@25 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@25 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@25 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@25 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@25 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@25 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@25 26 * POSSIBILITY OF SUCH DAMAGE.
universe@25 27 *
universe@25 28 */
universe@25 29
universe@25 30 #include "rules.h"
universe@25 31 #include "chess.h"
universe@25 32 #include <string.h>
universe@25 33 #include <stdlib.h>
universe@25 34 #include <sys/time.h>
universe@25 35
universe@25 36 static GameState gamestate_copy_sim(GameState *gamestate) {
universe@25 37 GameState simulation = *gamestate;
universe@25 38 if (simulation.lastmove) {
universe@25 39 MoveList *lastmovecopy = malloc(sizeof(MoveList));
universe@25 40 *lastmovecopy = *(simulation.lastmove);
universe@25 41 simulation.movelist = simulation.lastmove = lastmovecopy;
universe@25 42 }
universe@25 43
universe@25 44 return simulation;
universe@25 45 }
universe@25 46
universe@25 47 void gamestate_init(GameState *gamestate) {
universe@25 48 memset(gamestate, 0, sizeof(GameState));
universe@25 49
universe@25 50 Board initboard = {
universe@25 51 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
universe@25 52 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN},
universe@25 53 {0, 0, 0, 0, 0, 0, 0, 0},
universe@25 54 {0, 0, 0, 0, 0, 0, 0, 0},
universe@25 55 {0, 0, 0, 0, 0, 0, 0, 0},
universe@25 56 {0, 0, 0, 0, 0, 0, 0, 0},
universe@25 57 {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN},
universe@25 58 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
universe@25 59 };
universe@25 60 memcpy(gamestate->board, initboard, sizeof(Board));
universe@25 61 }
universe@25 62
universe@25 63 void gamestate_cleanup(GameState *gamestate) {
universe@25 64 MoveList *elem;
universe@25 65 elem = gamestate->movelist;
universe@25 66 while (elem) {
universe@25 67 MoveList *cur = elem;
universe@25 68 elem = elem->next;
universe@25 69 free(cur);
universe@25 70 };
universe@25 71 }
universe@25 72
universe@25 73 /* MUST be called IMMEDIATLY after applying a move to work correctly */
universe@25 74 static void format_move(GameState *gamestate, Move *move) {
universe@25 75 char *string = move->string;
universe@25 76
universe@25 77 /* at least 8 characters should be available, wipe them out */
universe@25 78 memset(string, 0, 8);
universe@25 79
universe@25 80 /* special formats for castling */
universe@25 81 if ((move->piece&PIECE_MASK) == KING &&
universe@25 82 abs(move->tofile-move->fromfile) == 2) {
universe@25 83 if (move->tofile==fileidx('c')) {
universe@25 84 memcpy(string, "O-O-O", 5);
universe@25 85 } else {
universe@25 86 memcpy(string, "O-O", 3);
universe@25 87 }
universe@25 88 }
universe@25 89
universe@25 90 /* start by notating the piece character */
universe@25 91 string[0] = getpiecechr(move->piece);
universe@25 92 int idx = string[0] ? 1 : 0;
universe@25 93
universe@25 94 /* find out how many source information we do need */
universe@25 95 uint8_t piece = move->piece & PIECE_MASK;
universe@25 96 if (piece == PAWN) {
universe@25 97 if (move->capture) {
universe@25 98 string[idx++] = filechr(move->fromfile);
universe@25 99 }
universe@25 100 } else if (piece != KING) {
universe@25 101 Move threats[16];
universe@25 102 uint8_t threatcount;
universe@25 103 get_real_threats(gamestate, move->torow, move->tofile,
universe@25 104 move->piece&COLOR_MASK, threats, &threatcount);
universe@25 105 if (threatcount > 1) {
universe@25 106 int ambrows = 0, ambfiles = 0;
universe@25 107 for (uint8_t i = 0 ; i < threatcount ; i++) {
universe@25 108 if (threats[i].fromrow == move->fromrow) {
universe@25 109 ambrows++;
universe@25 110 }
universe@25 111 if (threats[i].fromfile == move->fromfile) {
universe@25 112 ambfiles++;
universe@25 113 }
universe@25 114 }
universe@25 115 /* ambiguous row, name file */
universe@25 116 if (ambrows > 1) {
universe@25 117 string[idx++] = filechr(move->fromfile);
universe@25 118 }
universe@25 119 /* ambiguous file, name row */
universe@25 120 if (ambfiles > 1) {
universe@25 121 string[idx++] = filechr(move->fromrow);
universe@25 122 }
universe@25 123 }
universe@25 124 }
universe@25 125
universe@25 126 /* capturing? */
universe@25 127 if (move->capture) {
universe@25 128 string[idx++] = 'x';
universe@25 129 }
universe@25 130
universe@25 131 /* destination */
universe@25 132 string[idx++] = filechr(move->tofile);
universe@25 133 string[idx++] = rowchr(move->torow);
universe@25 134
universe@25 135 /* promotion? */
universe@25 136 if (move->promotion) {
universe@25 137 string[idx++] = '=';
universe@25 138 string[idx++] = getpiecechr(move->promotion);
universe@25 139 }
universe@25 140
universe@25 141 /* check? */
universe@25 142 if (move->check) {
universe@25 143 /* works only, if this function is called when applying the move */
universe@25 144 string[idx++] = gamestate->checkmate?'#':'+';
universe@25 145 }
universe@25 146 }
universe@25 147
universe@25 148 static void addmove(GameState* gamestate, Move *move) {
universe@25 149 MoveList *elem = malloc(sizeof(MoveList));
universe@25 150 elem->next = NULL;
universe@25 151 elem->move = *move;
universe@25 152
universe@25 153 struct timeval curtimestamp;
universe@25 154 gettimeofday(&curtimestamp, NULL);
universe@25 155 elem->move.timestamp.tv_sec = curtimestamp.tv_sec;
universe@25 156 elem->move.timestamp.tv_usec = curtimestamp.tv_usec;
universe@25 157
universe@25 158 if (gamestate->lastmove) {
universe@25 159 struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp);
universe@25 160 uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec;
universe@25 161 suseconds_t micros;
universe@25 162 if (curtimestamp.tv_usec < lasttstamp->tv_usec) {
universe@25 163 micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec);
universe@25 164 sec--;
universe@25 165 } else {
universe@25 166 micros = curtimestamp.tv_usec - lasttstamp->tv_usec;
universe@25 167 }
universe@25 168
universe@25 169 elem->move.movetime.tv_sec = sec;
universe@25 170 elem->move.movetime.tv_usec = micros;
universe@25 171
universe@25 172 gamestate->lastmove->next = elem;
universe@25 173 gamestate->lastmove = elem;
universe@25 174 } else {
universe@25 175 elem->move.movetime.tv_usec = 0;
universe@25 176 elem->move.movetime.tv_sec = 0;
universe@25 177 gamestate->movelist = gamestate->lastmove = elem;
universe@25 178 }
universe@25 179 }
universe@25 180
universe@25 181 char getpiecechr(uint8_t piece) {
universe@25 182 switch (piece & PIECE_MASK) {
universe@25 183 case ROOK: return 'R';
universe@25 184 case KNIGHT: return 'N';
universe@25 185 case BISHOP: return 'B';
universe@25 186 case QUEEN: return 'Q';
universe@25 187 case KING: return 'K';
universe@25 188 default: return '\0';
universe@25 189 }
universe@25 190 }
universe@25 191
universe@25 192 uint8_t getpiece(char c) {
universe@25 193 switch (c) {
universe@25 194 case 'R': return ROOK;
universe@25 195 case 'N': return KNIGHT;
universe@25 196 case 'B': return BISHOP;
universe@25 197 case 'Q': return QUEEN;
universe@25 198 case 'K': return KING;
universe@25 199 default: return 0;
universe@25 200 }
universe@25 201 }
universe@25 202
universe@25 203 static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) {
universe@25 204 uint8_t piece = move->piece & PIECE_MASK;
universe@25 205 uint8_t color = move->piece & COLOR_MASK;
universe@25 206
universe@25 207 /* en passant capture */
universe@25 208 if (move->capture && piece == PAWN &&
universe@25 209 mdst(gamestate->board, move) == 0) {
universe@25 210 gamestate->board[move->fromrow][move->tofile] = 0;
universe@25 211 }
universe@25 212
universe@25 213 /* remove old en passant threats */
universe@25 214 for (uint8_t file = 0 ; file < 8 ; file++) {
universe@25 215 gamestate->board[3][file] &= ~ENPASSANT_THREAT;
universe@25 216 gamestate->board[4][file] &= ~ENPASSANT_THREAT;
universe@25 217 }
universe@25 218
universe@25 219 /* add new en passant threat */
universe@25 220 if (piece == PAWN && (
universe@25 221 (move->fromrow == 1 && move->torow == 3) ||
universe@25 222 (move->fromrow == 6 && move->torow == 4))) {
universe@25 223 move->piece |= ENPASSANT_THREAT;
universe@25 224 }
universe@25 225
universe@25 226 /* move (and maybe capture or promote) */
universe@25 227 msrc(gamestate->board, move) = 0;
universe@25 228 if (move->promotion) {
universe@25 229 mdst(gamestate->board, move) = move->promotion;
universe@25 230 } else {
universe@25 231 mdst(gamestate->board, move) = move->piece;
universe@25 232 }
universe@25 233
universe@25 234 /* castling */
universe@25 235 if (piece == KING && move->fromfile == fileidx('e')) {
universe@25 236
universe@25 237 if (move->tofile == fileidx('g')) {
universe@25 238 gamestate->board[move->torow][fileidx('h')] = 0;
universe@25 239 gamestate->board[move->torow][fileidx('f')] = color|ROOK;
universe@25 240 } else if (move->tofile == fileidx('c')) {
universe@25 241 gamestate->board[move->torow][fileidx('a')] = 0;
universe@25 242 gamestate->board[move->torow][fileidx('d')] = color|ROOK;
universe@25 243 }
universe@25 244 }
universe@25 245
universe@25 246 if (!simulate) {
universe@25 247 if (!move->string[0]) {
universe@25 248 format_move(gamestate, move);
universe@25 249 }
universe@25 250 }
universe@25 251 /* add move, even in simulation (checkmate test needs it) */
universe@25 252 addmove(gamestate, move);
universe@25 253 }
universe@25 254
universe@25 255 void apply_move(GameState *gamestate, Move *move) {
universe@25 256 apply_move_impl(gamestate, move, 0);
universe@25 257 }
universe@25 258
universe@25 259 static int validate_move_rules(GameState *gamestate, Move *move) {
universe@25 260 /* validate indices (don't trust opponent) */
universe@25 261 if (!chkidx(move)) {
universe@25 262 return INVALID_POSITION;
universe@25 263 }
universe@25 264
universe@25 265 /* must move */
universe@25 266 if (move->fromfile == move->tofile && move->fromrow == move->torow) {
universe@25 267 return INVALID_MOVE_SYNTAX;
universe@25 268 }
universe@25 269
universe@25 270 /* does piece exist */
universe@25 271 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
universe@25 272 != (move->piece&(PIECE_MASK|COLOR_MASK))) {
universe@25 273 return INVALID_POSITION;
universe@25 274 }
universe@25 275
universe@25 276 /* can't capture own pieces */
universe@25 277 if ((mdst(gamestate->board, move) & COLOR_MASK)
universe@25 278 == (move->piece & COLOR_MASK)) {
universe@25 279 return RULES_VIOLATED;
universe@25 280 }
universe@25 281
universe@25 282 /* must capture, if and only if destination is occupied */
universe@25 283 if ((mdst(gamestate->board, move) == 0 && move->capture) ||
universe@25 284 (mdst(gamestate->board, move) != 0 && !move->capture)) {
universe@25 285 return INVALID_MOVE_SYNTAX;
universe@25 286 }
universe@25 287
universe@25 288 /* validate individual rules */
universe@25 289 _Bool chkrules;
universe@25 290 switch (move->piece & PIECE_MASK) {
universe@25 291 case PAWN:
universe@25 292 chkrules = pawn_chkrules(gamestate, move) &&
universe@25 293 !pawn_isblocked(gamestate, move);
universe@25 294 break;
universe@25 295 case ROOK:
universe@25 296 chkrules = rook_chkrules(move) &&
universe@25 297 !rook_isblocked(gamestate, move);
universe@25 298 break;
universe@25 299 case KNIGHT:
universe@25 300 chkrules = knight_chkrules(move); /* knight is never blocked */
universe@25 301 break;
universe@25 302 case BISHOP:
universe@25 303 chkrules = bishop_chkrules(move) &&
universe@25 304 !bishop_isblocked(gamestate, move);
universe@25 305 break;
universe@25 306 case QUEEN:
universe@25 307 chkrules = queen_chkrules(move) &&
universe@25 308 !queen_isblocked(gamestate, move);
universe@25 309 break;
universe@25 310 case KING:
universe@25 311 chkrules = king_chkrules(gamestate, move) &&
universe@25 312 !king_isblocked(gamestate, move);
universe@25 313 break;
universe@25 314 default:
universe@25 315 return INVALID_MOVE_SYNTAX;
universe@25 316 }
universe@25 317
universe@25 318 return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
universe@25 319 }
universe@25 320
universe@25 321 int validate_move(GameState *gamestate, Move *move) {
universe@25 322
universe@25 323 int result = validate_move_rules(gamestate, move);
universe@25 324
universe@25 325 /* cancel processing to save resources */
universe@25 326 if (result != VALID_MOVE_SEMANTICS) {
universe@25 327 return result;
universe@25 328 }
universe@25 329
universe@25 330 /* find kings for check validation */
universe@25 331 uint8_t piececolor = (move->piece & COLOR_MASK);
universe@25 332
universe@25 333 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
universe@25 334 for (uint8_t row = 0 ; row < 8 ; row++) {
universe@25 335 for (uint8_t file = 0 ; file < 8 ; file++) {
universe@25 336 if (gamestate->board[row][file] ==
universe@25 337 (piececolor == WHITE?WKING:BKING)) {
universe@25 338 mykingfile = file;
universe@25 339 mykingrow = row;
universe@25 340 } else if (gamestate->board[row][file] ==
universe@25 341 (piececolor == WHITE?BKING:WKING)) {
universe@25 342 opkingfile = file;
universe@25 343 opkingrow = row;
universe@25 344 }
universe@25 345 }
universe@25 346 }
universe@25 347
universe@25 348 /* simulate move for check validation */
universe@25 349 GameState simulation = gamestate_copy_sim(gamestate);
universe@25 350 Move simmove = *move;
universe@25 351 apply_move_impl(&simulation, &simmove, 1);
universe@25 352
universe@25 353 /* don't move into or stay in check position */
universe@25 354 if (is_covered(&simulation, mykingrow, mykingfile,
universe@25 355 opponent_color(piececolor))) {
universe@25 356
universe@25 357 gamestate_cleanup(&simulation);
universe@25 358 if ((move->piece & PIECE_MASK) == KING) {
universe@25 359 return KING_MOVES_INTO_CHECK;
universe@25 360 } else {
universe@25 361 /* last move is always not null in this case */
universe@25 362 return gamestate->lastmove->move.check ?
universe@25 363 KING_IN_CHECK : PIECE_PINNED;
universe@25 364 }
universe@25 365 }
universe@25 366
universe@25 367 /* correct check and checkmate flags (move is still valid) */
universe@25 368 Move threats[16];
universe@25 369 uint8_t threatcount;
universe@25 370 move->check = get_threats(&simulation, opkingrow, opkingfile,
universe@25 371 piececolor, threats, &threatcount);
universe@25 372
universe@25 373 if (move->check) {
universe@25 374 /* determine possible escape fields */
universe@25 375 _Bool canescape = 0;
universe@25 376 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
universe@25 377 for (int df = -1 ; df <= 1 && !canescape ; df++) {
universe@25 378 if (!(dr == 0 && df == 0) &&
universe@25 379 isidx(opkingrow + dr) && isidx(opkingfile + df)) {
universe@25 380
universe@25 381 /* escape field neither blocked nor covered */
universe@25 382 if ((simulation.board[opkingrow + dr][opkingfile + df]
universe@25 383 & COLOR_MASK) != opponent_color(piececolor)) {
universe@25 384 canescape |= !is_covered(&simulation,
universe@25 385 opkingrow + dr, opkingfile + df, piececolor);
universe@25 386 }
universe@25 387 }
universe@25 388 }
universe@25 389 }
universe@25 390 /* can't escape, can he capture? */
universe@25 391 if (!canescape && threatcount == 1) {
universe@25 392 canescape = is_attacked(&simulation, threats[0].fromrow,
universe@25 393 threats[0].fromfile, opponent_color(piececolor));
universe@25 394 }
universe@25 395
universe@25 396 /* can't capture, can he block? */
universe@25 397 if (!canescape && threatcount == 1) {
universe@25 398 Move *threat = &(threats[0]);
universe@25 399 uint8_t threatpiece = threat->piece & PIECE_MASK;
universe@25 400
universe@25 401 /* knight, pawns and the king cannot be blocked */
universe@25 402 if (threatpiece == BISHOP || threatpiece == ROOK
universe@25 403 || threatpiece == QUEEN) {
universe@25 404 if (threat->fromrow == threat->torow) {
universe@25 405 /* rook aspect (on row) */
universe@25 406 int d = threat->tofile > threat->fromfile ? 1 : -1;
universe@25 407 uint8_t file = threat->fromfile;
universe@25 408 while (!canescape && file != threat->tofile - d) {
universe@25 409 file += d;
universe@25 410 canescape |= is_protected(&simulation,
universe@25 411 threat->torow, file, opponent_color(piececolor));
universe@25 412 }
universe@25 413 } else if (threat->fromfile == threat->tofile) {
universe@25 414 /* rook aspect (on file) */
universe@25 415 int d = threat->torow > threat->fromrow ? 1 : -1;
universe@25 416 uint8_t row = threat->fromrow;
universe@25 417 while (!canescape && row != threat->torow - d) {
universe@25 418 row += d;
universe@25 419 canescape |= is_protected(&simulation,
universe@25 420 row, threat->tofile, opponent_color(piececolor));
universe@25 421 }
universe@25 422 } else {
universe@25 423 /* bishop aspect */
universe@25 424 int dr = threat->torow > threat->fromrow ? 1 : -1;
universe@25 425 int df = threat->tofile > threat->fromfile ? 1 : -1;
universe@25 426
universe@25 427 uint8_t row = threat->fromrow;
universe@25 428 uint8_t file = threat->fromfile;
universe@25 429 while (!canescape && file != threat->tofile - df
universe@25 430 && row != threat->torow - dr) {
universe@25 431 row += dr;
universe@25 432 file += df;
universe@25 433 canescape |= is_protected(&simulation, row, file,
universe@25 434 opponent_color(piececolor));
universe@25 435 }
universe@25 436 }
universe@25 437 }
universe@25 438 }
universe@25 439
universe@25 440 if (!canescape) {
universe@25 441 gamestate->checkmate = 1;
universe@25 442 }
universe@25 443 }
universe@25 444
universe@25 445 gamestate_cleanup(&simulation);
universe@25 446
universe@25 447 return VALID_MOVE_SEMANTICS;
universe@25 448 }
universe@25 449
universe@25 450 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
universe@25 451 uint8_t color, Move *threats, uint8_t *threatcount) {
universe@25 452 Move candidates[32];
universe@25 453 int candidatecount = 0;
universe@25 454 for (uint8_t r = 0 ; r < 8 ; r++) {
universe@25 455 for (uint8_t f = 0 ; f < 8 ; f++) {
universe@25 456 if ((gamestate->board[r][f] & COLOR_MASK) == color) {
universe@25 457 // non-capturing move
universe@25 458 memset(&(candidates[candidatecount]), 0, sizeof(Move));
universe@25 459 candidates[candidatecount].piece = gamestate->board[r][f];
universe@25 460 candidates[candidatecount].fromrow = r;
universe@25 461 candidates[candidatecount].fromfile = f;
universe@25 462 candidates[candidatecount].torow = row;
universe@25 463 candidates[candidatecount].tofile = file;
universe@25 464 candidatecount++;
universe@25 465
universe@25 466 // capturing move
universe@25 467 memcpy(&(candidates[candidatecount]),
universe@25 468 &(candidates[candidatecount-1]), sizeof(Move));
universe@25 469 candidates[candidatecount].capture = 1;
universe@25 470 candidatecount++;
universe@25 471 }
universe@25 472 }
universe@25 473 }
universe@25 474
universe@25 475 if (threatcount) {
universe@25 476 *threatcount = 0;
universe@25 477 }
universe@25 478
universe@25 479
universe@25 480 _Bool result = 0;
universe@25 481
universe@25 482 for (int i = 0 ; i < candidatecount ; i++) {
universe@25 483 if (validate_move_rules(gamestate, &(candidates[i]))
universe@25 484 == VALID_MOVE_SEMANTICS) {
universe@25 485 result = 1;
universe@25 486 if (threats && threatcount) {
universe@25 487 threats[(*threatcount)++] = candidates[i];
universe@25 488 }
universe@25 489 }
universe@25 490 }
universe@25 491
universe@25 492 return result;
universe@25 493 }
universe@25 494
universe@25 495 _Bool is_pinned(GameState *gamestate, Move *move) {
universe@25 496 uint8_t color = move->piece & COLOR_MASK;
universe@25 497
universe@25 498 uint8_t kingfile = 0, kingrow = 0;
universe@25 499 for (uint8_t row = 0 ; row < 8 ; row++) {
universe@25 500 for (uint8_t file = 0 ; file < 8 ; file++) {
universe@25 501 if (gamestate->board[row][file] == (color|KING)) {
universe@25 502 kingfile = file;
universe@25 503 kingrow = row;
universe@25 504 }
universe@25 505 }
universe@25 506 }
universe@25 507
universe@25 508 GameState simulation = gamestate_copy_sim(gamestate);
universe@25 509 Move simmove = *move;
universe@25 510 apply_move(&simulation, &simmove);
universe@25 511 _Bool covered = is_covered(&simulation,
universe@25 512 kingrow, kingfile, opponent_color(color));
universe@25 513 gamestate_cleanup(&simulation);
universe@25 514
universe@25 515 return covered;
universe@25 516 }
universe@25 517
universe@25 518 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
universe@25 519 uint8_t color, Move *threats, uint8_t *threatcount) {
universe@25 520
universe@25 521 if (threatcount) {
universe@25 522 *threatcount = 0;
universe@25 523 }
universe@25 524
universe@25 525 Move candidates[16];
universe@25 526 uint8_t candidatecount;
universe@25 527 if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
universe@25 528
universe@25 529 _Bool result = 0;
universe@25 530 uint8_t kingfile = 0, kingrow = 0;
universe@25 531 for (uint8_t row = 0 ; row < 8 ; row++) {
universe@25 532 for (uint8_t file = 0 ; file < 8 ; file++) {
universe@25 533 if (gamestate->board[row][file] == (color|KING)) {
universe@25 534 kingfile = file;
universe@25 535 kingrow = row;
universe@25 536 }
universe@25 537 }
universe@25 538 }
universe@25 539
universe@25 540 for (uint8_t i = 0 ; i < candidatecount ; i++) {
universe@25 541 GameState simulation = gamestate_copy_sim(gamestate);
universe@25 542 Move simmove = candidates[i];
universe@25 543 apply_move(&simulation, &simmove);
universe@25 544 if (!is_covered(&simulation, kingrow, kingfile,
universe@25 545 opponent_color(color))) {
universe@25 546 result = 1;
universe@25 547 if (threats && threatcount) {
universe@25 548 threats[(*threatcount)++] = candidates[i];
universe@25 549 }
universe@25 550 }
universe@25 551 }
universe@25 552
universe@25 553 return result;
universe@25 554 } else {
universe@25 555 return 0;
universe@25 556 }
universe@25 557 }
universe@25 558
universe@25 559 static int getlocation(GameState *gamestate, Move *move) {
universe@25 560
universe@25 561 uint8_t color = move->piece & COLOR_MASK;
universe@25 562 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
universe@25 563
universe@25 564 Move threats[16], *threat = NULL;
universe@25 565 uint8_t threatcount;
universe@25 566
universe@25 567 if (get_threats(gamestate, move->torow, move->tofile, color,
universe@25 568 threats, &threatcount)) {
universe@25 569
universe@25 570 int reason = INVALID_POSITION;
universe@25 571
universe@25 572 // find threats for the specified position
universe@25 573 for (uint8_t i = 0 ; i < threatcount ; i++) {
universe@25 574 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
universe@25 575 == move->piece &&
universe@25 576 (move->fromrow == POS_UNSPECIFIED ||
universe@25 577 move->fromrow == threats[i].fromrow) &&
universe@25 578 (move->fromfile == POS_UNSPECIFIED ||
universe@25 579 move->fromfile == threats[i].fromfile)) {
universe@25 580
universe@25 581 if (threat) {
universe@25 582 return AMBIGUOUS_MOVE;
universe@25 583 } else {
universe@25 584 // found threat is no real threat
universe@25 585 if (is_pinned(gamestate, &(threats[i]))) {
universe@25 586 reason = incheck?KING_IN_CHECK:PIECE_PINNED;
universe@25 587 } else {
universe@25 588 threat = &(threats[i]);
universe@25 589 }
universe@25 590 }
universe@25 591 }
universe@25 592 }
universe@25 593
universe@25 594 // can't threaten specified position
universe@25 595 if (!threat) {
universe@25 596 return reason;
universe@25 597 }
universe@25 598
universe@25 599 memcpy(move, threat, sizeof(Move));
universe@25 600 return VALID_MOVE_SYNTAX;
universe@25 601 } else {
universe@25 602 return INVALID_POSITION;
universe@25 603 }
universe@25 604 }
universe@25 605
universe@25 606 int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) {
universe@25 607 memset(move, 0, sizeof(Move));
universe@25 608 move->fromfile = POS_UNSPECIFIED;
universe@25 609 move->fromrow = POS_UNSPECIFIED;
universe@25 610
universe@25 611 size_t len = strlen(mstr);
universe@25 612 if (len < 1 || len > 6) {
universe@25 613 return INVALID_MOVE_SYNTAX;
universe@25 614 }
universe@25 615
universe@25 616 /* evaluate check/checkmate flags */
universe@25 617 if (mstr[len-1] == '+') {
universe@25 618 len--; mstr[len] = '\0';
universe@25 619 move->check = 1;
universe@25 620 } else if (mstr[len-1] == '#') {
universe@25 621 len--; mstr[len] = '\0';
universe@25 622 /* ignore - validation should set game state */
universe@25 623 }
universe@25 624
universe@25 625 /* evaluate promotion */
universe@25 626 if (len > 3 && mstr[len-2] == '=') {
universe@25 627 move->promotion = getpiece(mstr[len-1]);
universe@25 628 if (!move->promotion) {
universe@25 629 return INVALID_MOVE_SYNTAX;
universe@25 630 } else {
universe@25 631 move->promotion |= color;
universe@25 632 len -= 2;
universe@25 633 mstr[len] = 0;
universe@25 634 }
universe@25 635 }
universe@25 636
universe@25 637 if (len == 2) {
universe@25 638 /* pawn move (e.g. "e4") */
universe@25 639 move->piece = PAWN;
universe@25 640 move->tofile = fileidx(mstr[0]);
universe@25 641 move->torow = rowidx(mstr[1]);
universe@25 642 } else if (len == 3) {
universe@25 643 if (strcmp(mstr, "O-O") == 0) {
universe@25 644 /* king side castling */
universe@25 645 move->piece = KING;
universe@25 646 move->fromfile = fileidx('e');
universe@25 647 move->tofile = fileidx('g');
universe@25 648 move->fromrow = move->torow = color == WHITE ? 0 : 7;
universe@25 649 } else {
universe@25 650 /* move (e.g. "Nf3") */
universe@25 651 move->piece = getpiece(mstr[0]);
universe@25 652 move->tofile = fileidx(mstr[1]);
universe@25 653 move->torow = rowidx(mstr[2]);
universe@25 654 }
universe@25 655 } else if (len == 4) {
universe@25 656 move->piece = getpiece(mstr[0]);
universe@25 657 if (!move->piece) {
universe@25 658 move->piece = PAWN;
universe@25 659 move->fromfile = fileidx(mstr[0]);
universe@25 660 }
universe@25 661 if (mstr[1] == 'x') {
universe@25 662 /* capture (e.g. "Nxf3", "dxe5") */
universe@25 663 move->capture = 1;
universe@25 664 } else {
universe@25 665 /* move (e.g. "Ndf3", "N2c3", "e2e4") */
universe@25 666 if (isfile(mstr[1])) {
universe@25 667 move->fromfile = fileidx(mstr[1]);
universe@25 668 if (move->piece == PAWN) {
universe@25 669 move->piece = 0;
universe@25 670 }
universe@25 671 } else {
universe@25 672 move->fromrow = rowidx(mstr[1]);
universe@25 673 }
universe@25 674 }
universe@25 675 move->tofile = fileidx(mstr[2]);
universe@25 676 move->torow = rowidx(mstr[3]);
universe@25 677 } else if (len == 5) {
universe@25 678 if (strcmp(mstr, "O-O-O") == 0) {
universe@25 679 /* queen side castling "O-O-O" */
universe@25 680 move->piece = KING;
universe@25 681 move->fromfile = fileidx('e');
universe@25 682 move->tofile = fileidx('c');
universe@25 683 move->fromrow = move->torow = color == WHITE ? 0 : 7;
universe@25 684 } else {
universe@25 685 move->piece = getpiece(mstr[0]);
universe@25 686 if (mstr[2] == 'x') {
universe@25 687 move->capture = 1;
universe@25 688 if (move->piece) {
universe@25 689 /* capture (e.g. "Ndxf3") */
universe@25 690 move->fromfile = fileidx(mstr[1]);
universe@25 691 } else {
universe@25 692 /* long notation capture (e.g. "e5xf6") */
universe@25 693 move->piece = PAWN;
universe@25 694 move->fromfile = fileidx(mstr[0]);
universe@25 695 move->fromrow = rowidx(mstr[1]);
universe@25 696 }
universe@25 697 } else {
universe@25 698 /* long notation move (e.g. "Nc5a4") */
universe@25 699 move->fromfile = fileidx(mstr[1]);
universe@25 700 move->fromrow = rowidx(mstr[2]);
universe@25 701 }
universe@25 702 move->tofile = fileidx(mstr[3]);
universe@25 703 move->torow = rowidx(mstr[4]);
universe@25 704 }
universe@25 705 } else if (len == 6) {
universe@25 706 /* long notation capture (e.g. "Nc5xf3") */
universe@25 707 if (mstr[3] == 'x') {
universe@25 708 move->capture = 1;
universe@25 709 move->piece = getpiece(mstr[0]);
universe@25 710 move->fromfile = fileidx(mstr[1]);
universe@25 711 move->fromrow = rowidx(mstr[2]);
universe@25 712 move->tofile = fileidx(mstr[4]);
universe@25 713 move->torow = rowidx(mstr[5]);
universe@25 714 }
universe@25 715 }
universe@25 716
universe@25 717
universe@25 718 if (move->piece) {
universe@25 719 if (move->piece == PAWN
universe@25 720 && move->torow == (color==WHITE?7:0)
universe@25 721 && !move->promotion) {
universe@25 722 return NEED_PROMOTION;
universe@25 723 }
universe@25 724
universe@25 725 move->piece |= color;
universe@25 726 if (move->fromfile == POS_UNSPECIFIED
universe@25 727 || move->fromrow == POS_UNSPECIFIED) {
universe@25 728 return getlocation(gamestate, move);
universe@25 729 } else {
universe@25 730 return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
universe@25 731 }
universe@25 732 } else {
universe@25 733 return INVALID_MOVE_SYNTAX;
universe@25 734 }
universe@25 735 }
universe@25 736
universe@25 737 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
universe@25 738 uint8_t color) {
universe@25 739
universe@25 740 Move threats[16];
universe@25 741 uint8_t threatcount;
universe@25 742 if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
universe@25 743 for (int i = 0 ; i < threatcount ; i++) {
universe@25 744 if (threats[i].piece != (color|KING)) {
universe@25 745 return 1;
universe@25 746 }
universe@25 747 }
universe@25 748 return 0;
universe@25 749 } else {
universe@25 750 return 0;
universe@25 751 }
universe@25 752 }
universe@25 753
universe@25 754 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
universe@25 755 uint8_t color) {
universe@25 756 if (!gameinfo->timecontrol) {
universe@25 757 return 0;
universe@25 758 }
universe@25 759
universe@25 760 if (gamestate->movelist) {
universe@25 761 uint16_t time = gameinfo->time;
universe@25 762 suseconds_t micros = 0;
universe@25 763
universe@25 764 MoveList *movelist = color == WHITE ?
universe@25 765 gamestate->movelist : gamestate->movelist->next;
universe@25 766
universe@25 767 while (movelist) {
universe@25 768 time += gameinfo->addtime;
universe@25 769
universe@25 770 struct movetimeval *movetime = &(movelist->move.movetime);
universe@25 771 if (movetime->tv_sec >= time) {
universe@25 772 return 0;
universe@25 773 }
universe@25 774
universe@25 775 time -= movetime->tv_sec;
universe@25 776 micros += movetime->tv_usec;
universe@25 777
universe@25 778 movelist = movelist->next ? movelist->next->next : NULL;
universe@25 779 }
universe@25 780
universe@25 781 time_t sec;
universe@25 782 movelist = gamestate->lastmove;
universe@25 783 if ((movelist->move.piece & COLOR_MASK) != color) {
universe@25 784 struct movetimeval *lastmovetstamp = &(movelist->move.timestamp);
universe@25 785 struct timeval currenttstamp;
universe@25 786 gettimeofday(&currenttstamp, NULL);
universe@25 787 micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec;
universe@25 788 sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
universe@25 789 if (sec >= time) {
universe@25 790 return 0;
universe@25 791 }
universe@25 792
universe@25 793 time -= sec;
universe@25 794 }
universe@25 795
universe@25 796 sec = micros / 1e6L;
universe@25 797
universe@25 798 if (sec >= time) {
universe@25 799 return 0;
universe@25 800 }
universe@25 801
universe@25 802 time -= sec;
universe@25 803
universe@25 804 return time;
universe@25 805 } else {
universe@25 806 return gameinfo->time;
universe@25 807 }
universe@25 808 }

mercurial