1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/bigtestfile.c Tue Apr 21 09:47:52 2015 +0200 1.3 @@ -0,0 +1,808 @@ 1.4 +/* 1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 1.6 + * 1.7 + * Copyright 2014 Mike Becker. All rights reserved. 1.8 + * 1.9 + * Redistribution and use in source and binary forms, with or without 1.10 + * modification, are permitted provided that the following conditions are met: 1.11 + * 1.12 + * 1. Redistributions of source code must retain the above copyright 1.13 + * notice, this list of conditions and the following disclaimer. 1.14 + * 1.15 + * 2. Redistributions in binary form must reproduce the above copyright 1.16 + * notice, this list of conditions and the following disclaimer in the 1.17 + * documentation and/or other materials provided with the distribution. 1.18 + * 1.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 1.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1.29 + * POSSIBILITY OF SUCH DAMAGE. 1.30 + * 1.31 + */ 1.32 + 1.33 +#include "rules.h" 1.34 +#include "chess.h" 1.35 +#include <string.h> 1.36 +#include <stdlib.h> 1.37 +#include <sys/time.h> 1.38 + 1.39 +static GameState gamestate_copy_sim(GameState *gamestate) { 1.40 + GameState simulation = *gamestate; 1.41 + if (simulation.lastmove) { 1.42 + MoveList *lastmovecopy = malloc(sizeof(MoveList)); 1.43 + *lastmovecopy = *(simulation.lastmove); 1.44 + simulation.movelist = simulation.lastmove = lastmovecopy; 1.45 + } 1.46 + 1.47 + return simulation; 1.48 +} 1.49 + 1.50 +void gamestate_init(GameState *gamestate) { 1.51 + memset(gamestate, 0, sizeof(GameState)); 1.52 + 1.53 + Board initboard = { 1.54 + {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 1.55 + {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 1.56 + {0, 0, 0, 0, 0, 0, 0, 0}, 1.57 + {0, 0, 0, 0, 0, 0, 0, 0}, 1.58 + {0, 0, 0, 0, 0, 0, 0, 0}, 1.59 + {0, 0, 0, 0, 0, 0, 0, 0}, 1.60 + {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 1.61 + {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 1.62 + }; 1.63 + memcpy(gamestate->board, initboard, sizeof(Board)); 1.64 +} 1.65 + 1.66 +void gamestate_cleanup(GameState *gamestate) { 1.67 + MoveList *elem; 1.68 + elem = gamestate->movelist; 1.69 + while (elem) { 1.70 + MoveList *cur = elem; 1.71 + elem = elem->next; 1.72 + free(cur); 1.73 + }; 1.74 +} 1.75 + 1.76 +/* MUST be called IMMEDIATLY after applying a move to work correctly */ 1.77 +static void format_move(GameState *gamestate, Move *move) { 1.78 + char *string = move->string; 1.79 + 1.80 + /* at least 8 characters should be available, wipe them out */ 1.81 + memset(string, 0, 8); 1.82 + 1.83 + /* special formats for castling */ 1.84 + if ((move->piece&PIECE_MASK) == KING && 1.85 + abs(move->tofile-move->fromfile) == 2) { 1.86 + if (move->tofile==fileidx('c')) { 1.87 + memcpy(string, "O-O-O", 5); 1.88 + } else { 1.89 + memcpy(string, "O-O", 3); 1.90 + } 1.91 + } 1.92 + 1.93 + /* start by notating the piece character */ 1.94 + string[0] = getpiecechr(move->piece); 1.95 + int idx = string[0] ? 1 : 0; 1.96 + 1.97 + /* find out how many source information we do need */ 1.98 + uint8_t piece = move->piece & PIECE_MASK; 1.99 + if (piece == PAWN) { 1.100 + if (move->capture) { 1.101 + string[idx++] = filechr(move->fromfile); 1.102 + } 1.103 + } else if (piece != KING) { 1.104 + Move threats[16]; 1.105 + uint8_t threatcount; 1.106 + get_real_threats(gamestate, move->torow, move->tofile, 1.107 + move->piece&COLOR_MASK, threats, &threatcount); 1.108 + if (threatcount > 1) { 1.109 + int ambrows = 0, ambfiles = 0; 1.110 + for (uint8_t i = 0 ; i < threatcount ; i++) { 1.111 + if (threats[i].fromrow == move->fromrow) { 1.112 + ambrows++; 1.113 + } 1.114 + if (threats[i].fromfile == move->fromfile) { 1.115 + ambfiles++; 1.116 + } 1.117 + } 1.118 + /* ambiguous row, name file */ 1.119 + if (ambrows > 1) { 1.120 + string[idx++] = filechr(move->fromfile); 1.121 + } 1.122 + /* ambiguous file, name row */ 1.123 + if (ambfiles > 1) { 1.124 + string[idx++] = filechr(move->fromrow); 1.125 + } 1.126 + } 1.127 + } 1.128 + 1.129 + /* capturing? */ 1.130 + if (move->capture) { 1.131 + string[idx++] = 'x'; 1.132 + } 1.133 + 1.134 + /* destination */ 1.135 + string[idx++] = filechr(move->tofile); 1.136 + string[idx++] = rowchr(move->torow); 1.137 + 1.138 + /* promotion? */ 1.139 + if (move->promotion) { 1.140 + string[idx++] = '='; 1.141 + string[idx++] = getpiecechr(move->promotion); 1.142 + } 1.143 + 1.144 + /* check? */ 1.145 + if (move->check) { 1.146 + /* works only, if this function is called when applying the move */ 1.147 + string[idx++] = gamestate->checkmate?'#':'+'; 1.148 + } 1.149 +} 1.150 + 1.151 +static void addmove(GameState* gamestate, Move *move) { 1.152 + MoveList *elem = malloc(sizeof(MoveList)); 1.153 + elem->next = NULL; 1.154 + elem->move = *move; 1.155 + 1.156 + struct timeval curtimestamp; 1.157 + gettimeofday(&curtimestamp, NULL); 1.158 + elem->move.timestamp.tv_sec = curtimestamp.tv_sec; 1.159 + elem->move.timestamp.tv_usec = curtimestamp.tv_usec; 1.160 + 1.161 + if (gamestate->lastmove) { 1.162 + struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp); 1.163 + uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec; 1.164 + suseconds_t micros; 1.165 + if (curtimestamp.tv_usec < lasttstamp->tv_usec) { 1.166 + micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec); 1.167 + sec--; 1.168 + } else { 1.169 + micros = curtimestamp.tv_usec - lasttstamp->tv_usec; 1.170 + } 1.171 + 1.172 + elem->move.movetime.tv_sec = sec; 1.173 + elem->move.movetime.tv_usec = micros; 1.174 + 1.175 + gamestate->lastmove->next = elem; 1.176 + gamestate->lastmove = elem; 1.177 + } else { 1.178 + elem->move.movetime.tv_usec = 0; 1.179 + elem->move.movetime.tv_sec = 0; 1.180 + gamestate->movelist = gamestate->lastmove = elem; 1.181 + } 1.182 +} 1.183 + 1.184 +char getpiecechr(uint8_t piece) { 1.185 + switch (piece & PIECE_MASK) { 1.186 + case ROOK: return 'R'; 1.187 + case KNIGHT: return 'N'; 1.188 + case BISHOP: return 'B'; 1.189 + case QUEEN: return 'Q'; 1.190 + case KING: return 'K'; 1.191 + default: return '\0'; 1.192 + } 1.193 +} 1.194 + 1.195 +uint8_t getpiece(char c) { 1.196 + switch (c) { 1.197 + case 'R': return ROOK; 1.198 + case 'N': return KNIGHT; 1.199 + case 'B': return BISHOP; 1.200 + case 'Q': return QUEEN; 1.201 + case 'K': return KING; 1.202 + default: return 0; 1.203 + } 1.204 +} 1.205 + 1.206 +static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { 1.207 + uint8_t piece = move->piece & PIECE_MASK; 1.208 + uint8_t color = move->piece & COLOR_MASK; 1.209 + 1.210 + /* en passant capture */ 1.211 + if (move->capture && piece == PAWN && 1.212 + mdst(gamestate->board, move) == 0) { 1.213 + gamestate->board[move->fromrow][move->tofile] = 0; 1.214 + } 1.215 + 1.216 + /* remove old en passant threats */ 1.217 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.218 + gamestate->board[3][file] &= ~ENPASSANT_THREAT; 1.219 + gamestate->board[4][file] &= ~ENPASSANT_THREAT; 1.220 + } 1.221 + 1.222 + /* add new en passant threat */ 1.223 + if (piece == PAWN && ( 1.224 + (move->fromrow == 1 && move->torow == 3) || 1.225 + (move->fromrow == 6 && move->torow == 4))) { 1.226 + move->piece |= ENPASSANT_THREAT; 1.227 + } 1.228 + 1.229 + /* move (and maybe capture or promote) */ 1.230 + msrc(gamestate->board, move) = 0; 1.231 + if (move->promotion) { 1.232 + mdst(gamestate->board, move) = move->promotion; 1.233 + } else { 1.234 + mdst(gamestate->board, move) = move->piece; 1.235 + } 1.236 + 1.237 + /* castling */ 1.238 + if (piece == KING && move->fromfile == fileidx('e')) { 1.239 + 1.240 + if (move->tofile == fileidx('g')) { 1.241 + gamestate->board[move->torow][fileidx('h')] = 0; 1.242 + gamestate->board[move->torow][fileidx('f')] = color|ROOK; 1.243 + } else if (move->tofile == fileidx('c')) { 1.244 + gamestate->board[move->torow][fileidx('a')] = 0; 1.245 + gamestate->board[move->torow][fileidx('d')] = color|ROOK; 1.246 + } 1.247 + } 1.248 + 1.249 + if (!simulate) { 1.250 + if (!move->string[0]) { 1.251 + format_move(gamestate, move); 1.252 + } 1.253 + } 1.254 + /* add move, even in simulation (checkmate test needs it) */ 1.255 + addmove(gamestate, move); 1.256 +} 1.257 + 1.258 +void apply_move(GameState *gamestate, Move *move) { 1.259 + apply_move_impl(gamestate, move, 0); 1.260 +} 1.261 + 1.262 +static int validate_move_rules(GameState *gamestate, Move *move) { 1.263 + /* validate indices (don't trust opponent) */ 1.264 + if (!chkidx(move)) { 1.265 + return INVALID_POSITION; 1.266 + } 1.267 + 1.268 + /* must move */ 1.269 + if (move->fromfile == move->tofile && move->fromrow == move->torow) { 1.270 + return INVALID_MOVE_SYNTAX; 1.271 + } 1.272 + 1.273 + /* does piece exist */ 1.274 + if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 1.275 + != (move->piece&(PIECE_MASK|COLOR_MASK))) { 1.276 + return INVALID_POSITION; 1.277 + } 1.278 + 1.279 + /* can't capture own pieces */ 1.280 + if ((mdst(gamestate->board, move) & COLOR_MASK) 1.281 + == (move->piece & COLOR_MASK)) { 1.282 + return RULES_VIOLATED; 1.283 + } 1.284 + 1.285 + /* must capture, if and only if destination is occupied */ 1.286 + if ((mdst(gamestate->board, move) == 0 && move->capture) || 1.287 + (mdst(gamestate->board, move) != 0 && !move->capture)) { 1.288 + return INVALID_MOVE_SYNTAX; 1.289 + } 1.290 + 1.291 + /* validate individual rules */ 1.292 + _Bool chkrules; 1.293 + switch (move->piece & PIECE_MASK) { 1.294 + case PAWN: 1.295 + chkrules = pawn_chkrules(gamestate, move) && 1.296 + !pawn_isblocked(gamestate, move); 1.297 + break; 1.298 + case ROOK: 1.299 + chkrules = rook_chkrules(move) && 1.300 + !rook_isblocked(gamestate, move); 1.301 + break; 1.302 + case KNIGHT: 1.303 + chkrules = knight_chkrules(move); /* knight is never blocked */ 1.304 + break; 1.305 + case BISHOP: 1.306 + chkrules = bishop_chkrules(move) && 1.307 + !bishop_isblocked(gamestate, move); 1.308 + break; 1.309 + case QUEEN: 1.310 + chkrules = queen_chkrules(move) && 1.311 + !queen_isblocked(gamestate, move); 1.312 + break; 1.313 + case KING: 1.314 + chkrules = king_chkrules(gamestate, move) && 1.315 + !king_isblocked(gamestate, move); 1.316 + break; 1.317 + default: 1.318 + return INVALID_MOVE_SYNTAX; 1.319 + } 1.320 + 1.321 + return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; 1.322 +} 1.323 + 1.324 +int validate_move(GameState *gamestate, Move *move) { 1.325 + 1.326 + int result = validate_move_rules(gamestate, move); 1.327 + 1.328 + /* cancel processing to save resources */ 1.329 + if (result != VALID_MOVE_SEMANTICS) { 1.330 + return result; 1.331 + } 1.332 + 1.333 + /* find kings for check validation */ 1.334 + uint8_t piececolor = (move->piece & COLOR_MASK); 1.335 + 1.336 + uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 1.337 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.338 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.339 + if (gamestate->board[row][file] == 1.340 + (piececolor == WHITE?WKING:BKING)) { 1.341 + mykingfile = file; 1.342 + mykingrow = row; 1.343 + } else if (gamestate->board[row][file] == 1.344 + (piececolor == WHITE?BKING:WKING)) { 1.345 + opkingfile = file; 1.346 + opkingrow = row; 1.347 + } 1.348 + } 1.349 + } 1.350 + 1.351 + /* simulate move for check validation */ 1.352 + GameState simulation = gamestate_copy_sim(gamestate); 1.353 + Move simmove = *move; 1.354 + apply_move_impl(&simulation, &simmove, 1); 1.355 + 1.356 + /* don't move into or stay in check position */ 1.357 + if (is_covered(&simulation, mykingrow, mykingfile, 1.358 + opponent_color(piececolor))) { 1.359 + 1.360 + gamestate_cleanup(&simulation); 1.361 + if ((move->piece & PIECE_MASK) == KING) { 1.362 + return KING_MOVES_INTO_CHECK; 1.363 + } else { 1.364 + /* last move is always not null in this case */ 1.365 + return gamestate->lastmove->move.check ? 1.366 + KING_IN_CHECK : PIECE_PINNED; 1.367 + } 1.368 + } 1.369 + 1.370 + /* correct check and checkmate flags (move is still valid) */ 1.371 + Move threats[16]; 1.372 + uint8_t threatcount; 1.373 + move->check = get_threats(&simulation, opkingrow, opkingfile, 1.374 + piececolor, threats, &threatcount); 1.375 + 1.376 + if (move->check) { 1.377 + /* determine possible escape fields */ 1.378 + _Bool canescape = 0; 1.379 + for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 1.380 + for (int df = -1 ; df <= 1 && !canescape ; df++) { 1.381 + if (!(dr == 0 && df == 0) && 1.382 + isidx(opkingrow + dr) && isidx(opkingfile + df)) { 1.383 + 1.384 + /* escape field neither blocked nor covered */ 1.385 + if ((simulation.board[opkingrow + dr][opkingfile + df] 1.386 + & COLOR_MASK) != opponent_color(piececolor)) { 1.387 + canescape |= !is_covered(&simulation, 1.388 + opkingrow + dr, opkingfile + df, piececolor); 1.389 + } 1.390 + } 1.391 + } 1.392 + } 1.393 + /* can't escape, can he capture? */ 1.394 + if (!canescape && threatcount == 1) { 1.395 + canescape = is_attacked(&simulation, threats[0].fromrow, 1.396 + threats[0].fromfile, opponent_color(piececolor)); 1.397 + } 1.398 + 1.399 + /* can't capture, can he block? */ 1.400 + if (!canescape && threatcount == 1) { 1.401 + Move *threat = &(threats[0]); 1.402 + uint8_t threatpiece = threat->piece & PIECE_MASK; 1.403 + 1.404 + /* knight, pawns and the king cannot be blocked */ 1.405 + if (threatpiece == BISHOP || threatpiece == ROOK 1.406 + || threatpiece == QUEEN) { 1.407 + if (threat->fromrow == threat->torow) { 1.408 + /* rook aspect (on row) */ 1.409 + int d = threat->tofile > threat->fromfile ? 1 : -1; 1.410 + uint8_t file = threat->fromfile; 1.411 + while (!canescape && file != threat->tofile - d) { 1.412 + file += d; 1.413 + canescape |= is_protected(&simulation, 1.414 + threat->torow, file, opponent_color(piececolor)); 1.415 + } 1.416 + } else if (threat->fromfile == threat->tofile) { 1.417 + /* rook aspect (on file) */ 1.418 + int d = threat->torow > threat->fromrow ? 1 : -1; 1.419 + uint8_t row = threat->fromrow; 1.420 + while (!canescape && row != threat->torow - d) { 1.421 + row += d; 1.422 + canescape |= is_protected(&simulation, 1.423 + row, threat->tofile, opponent_color(piececolor)); 1.424 + } 1.425 + } else { 1.426 + /* bishop aspect */ 1.427 + int dr = threat->torow > threat->fromrow ? 1 : -1; 1.428 + int df = threat->tofile > threat->fromfile ? 1 : -1; 1.429 + 1.430 + uint8_t row = threat->fromrow; 1.431 + uint8_t file = threat->fromfile; 1.432 + while (!canescape && file != threat->tofile - df 1.433 + && row != threat->torow - dr) { 1.434 + row += dr; 1.435 + file += df; 1.436 + canescape |= is_protected(&simulation, row, file, 1.437 + opponent_color(piececolor)); 1.438 + } 1.439 + } 1.440 + } 1.441 + } 1.442 + 1.443 + if (!canescape) { 1.444 + gamestate->checkmate = 1; 1.445 + } 1.446 + } 1.447 + 1.448 + gamestate_cleanup(&simulation); 1.449 + 1.450 + return VALID_MOVE_SEMANTICS; 1.451 +} 1.452 + 1.453 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.454 + uint8_t color, Move *threats, uint8_t *threatcount) { 1.455 + Move candidates[32]; 1.456 + int candidatecount = 0; 1.457 + for (uint8_t r = 0 ; r < 8 ; r++) { 1.458 + for (uint8_t f = 0 ; f < 8 ; f++) { 1.459 + if ((gamestate->board[r][f] & COLOR_MASK) == color) { 1.460 + // non-capturing move 1.461 + memset(&(candidates[candidatecount]), 0, sizeof(Move)); 1.462 + candidates[candidatecount].piece = gamestate->board[r][f]; 1.463 + candidates[candidatecount].fromrow = r; 1.464 + candidates[candidatecount].fromfile = f; 1.465 + candidates[candidatecount].torow = row; 1.466 + candidates[candidatecount].tofile = file; 1.467 + candidatecount++; 1.468 + 1.469 + // capturing move 1.470 + memcpy(&(candidates[candidatecount]), 1.471 + &(candidates[candidatecount-1]), sizeof(Move)); 1.472 + candidates[candidatecount].capture = 1; 1.473 + candidatecount++; 1.474 + } 1.475 + } 1.476 + } 1.477 + 1.478 + if (threatcount) { 1.479 + *threatcount = 0; 1.480 + } 1.481 + 1.482 + 1.483 + _Bool result = 0; 1.484 + 1.485 + for (int i = 0 ; i < candidatecount ; i++) { 1.486 + if (validate_move_rules(gamestate, &(candidates[i])) 1.487 + == VALID_MOVE_SEMANTICS) { 1.488 + result = 1; 1.489 + if (threats && threatcount) { 1.490 + threats[(*threatcount)++] = candidates[i]; 1.491 + } 1.492 + } 1.493 + } 1.494 + 1.495 + return result; 1.496 +} 1.497 + 1.498 +_Bool is_pinned(GameState *gamestate, Move *move) { 1.499 + uint8_t color = move->piece & COLOR_MASK; 1.500 + 1.501 + uint8_t kingfile = 0, kingrow = 0; 1.502 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.503 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.504 + if (gamestate->board[row][file] == (color|KING)) { 1.505 + kingfile = file; 1.506 + kingrow = row; 1.507 + } 1.508 + } 1.509 + } 1.510 + 1.511 + GameState simulation = gamestate_copy_sim(gamestate); 1.512 + Move simmove = *move; 1.513 + apply_move(&simulation, &simmove); 1.514 + _Bool covered = is_covered(&simulation, 1.515 + kingrow, kingfile, opponent_color(color)); 1.516 + gamestate_cleanup(&simulation); 1.517 + 1.518 + return covered; 1.519 +} 1.520 + 1.521 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.522 + uint8_t color, Move *threats, uint8_t *threatcount) { 1.523 + 1.524 + if (threatcount) { 1.525 + *threatcount = 0; 1.526 + } 1.527 + 1.528 + Move candidates[16]; 1.529 + uint8_t candidatecount; 1.530 + if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 1.531 + 1.532 + _Bool result = 0; 1.533 + uint8_t kingfile = 0, kingrow = 0; 1.534 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.535 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.536 + if (gamestate->board[row][file] == (color|KING)) { 1.537 + kingfile = file; 1.538 + kingrow = row; 1.539 + } 1.540 + } 1.541 + } 1.542 + 1.543 + for (uint8_t i = 0 ; i < candidatecount ; i++) { 1.544 + GameState simulation = gamestate_copy_sim(gamestate); 1.545 + Move simmove = candidates[i]; 1.546 + apply_move(&simulation, &simmove); 1.547 + if (!is_covered(&simulation, kingrow, kingfile, 1.548 + opponent_color(color))) { 1.549 + result = 1; 1.550 + if (threats && threatcount) { 1.551 + threats[(*threatcount)++] = candidates[i]; 1.552 + } 1.553 + } 1.554 + } 1.555 + 1.556 + return result; 1.557 + } else { 1.558 + return 0; 1.559 + } 1.560 +} 1.561 + 1.562 +static int getlocation(GameState *gamestate, Move *move) { 1.563 + 1.564 + uint8_t color = move->piece & COLOR_MASK; 1.565 + _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 1.566 + 1.567 + Move threats[16], *threat = NULL; 1.568 + uint8_t threatcount; 1.569 + 1.570 + if (get_threats(gamestate, move->torow, move->tofile, color, 1.571 + threats, &threatcount)) { 1.572 + 1.573 + int reason = INVALID_POSITION; 1.574 + 1.575 + // find threats for the specified position 1.576 + for (uint8_t i = 0 ; i < threatcount ; i++) { 1.577 + if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 1.578 + == move->piece && 1.579 + (move->fromrow == POS_UNSPECIFIED || 1.580 + move->fromrow == threats[i].fromrow) && 1.581 + (move->fromfile == POS_UNSPECIFIED || 1.582 + move->fromfile == threats[i].fromfile)) { 1.583 + 1.584 + if (threat) { 1.585 + return AMBIGUOUS_MOVE; 1.586 + } else { 1.587 + // found threat is no real threat 1.588 + if (is_pinned(gamestate, &(threats[i]))) { 1.589 + reason = incheck?KING_IN_CHECK:PIECE_PINNED; 1.590 + } else { 1.591 + threat = &(threats[i]); 1.592 + } 1.593 + } 1.594 + } 1.595 + } 1.596 + 1.597 + // can't threaten specified position 1.598 + if (!threat) { 1.599 + return reason; 1.600 + } 1.601 + 1.602 + memcpy(move, threat, sizeof(Move)); 1.603 + return VALID_MOVE_SYNTAX; 1.604 + } else { 1.605 + return INVALID_POSITION; 1.606 + } 1.607 +} 1.608 + 1.609 +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { 1.610 + memset(move, 0, sizeof(Move)); 1.611 + move->fromfile = POS_UNSPECIFIED; 1.612 + move->fromrow = POS_UNSPECIFIED; 1.613 + 1.614 + size_t len = strlen(mstr); 1.615 + if (len < 1 || len > 6) { 1.616 + return INVALID_MOVE_SYNTAX; 1.617 + } 1.618 + 1.619 + /* evaluate check/checkmate flags */ 1.620 + if (mstr[len-1] == '+') { 1.621 + len--; mstr[len] = '\0'; 1.622 + move->check = 1; 1.623 + } else if (mstr[len-1] == '#') { 1.624 + len--; mstr[len] = '\0'; 1.625 + /* ignore - validation should set game state */ 1.626 + } 1.627 + 1.628 + /* evaluate promotion */ 1.629 + if (len > 3 && mstr[len-2] == '=') { 1.630 + move->promotion = getpiece(mstr[len-1]); 1.631 + if (!move->promotion) { 1.632 + return INVALID_MOVE_SYNTAX; 1.633 + } else { 1.634 + move->promotion |= color; 1.635 + len -= 2; 1.636 + mstr[len] = 0; 1.637 + } 1.638 + } 1.639 + 1.640 + if (len == 2) { 1.641 + /* pawn move (e.g. "e4") */ 1.642 + move->piece = PAWN; 1.643 + move->tofile = fileidx(mstr[0]); 1.644 + move->torow = rowidx(mstr[1]); 1.645 + } else if (len == 3) { 1.646 + if (strcmp(mstr, "O-O") == 0) { 1.647 + /* king side castling */ 1.648 + move->piece = KING; 1.649 + move->fromfile = fileidx('e'); 1.650 + move->tofile = fileidx('g'); 1.651 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 1.652 + } else { 1.653 + /* move (e.g. "Nf3") */ 1.654 + move->piece = getpiece(mstr[0]); 1.655 + move->tofile = fileidx(mstr[1]); 1.656 + move->torow = rowidx(mstr[2]); 1.657 + } 1.658 + } else if (len == 4) { 1.659 + move->piece = getpiece(mstr[0]); 1.660 + if (!move->piece) { 1.661 + move->piece = PAWN; 1.662 + move->fromfile = fileidx(mstr[0]); 1.663 + } 1.664 + if (mstr[1] == 'x') { 1.665 + /* capture (e.g. "Nxf3", "dxe5") */ 1.666 + move->capture = 1; 1.667 + } else { 1.668 + /* move (e.g. "Ndf3", "N2c3", "e2e4") */ 1.669 + if (isfile(mstr[1])) { 1.670 + move->fromfile = fileidx(mstr[1]); 1.671 + if (move->piece == PAWN) { 1.672 + move->piece = 0; 1.673 + } 1.674 + } else { 1.675 + move->fromrow = rowidx(mstr[1]); 1.676 + } 1.677 + } 1.678 + move->tofile = fileidx(mstr[2]); 1.679 + move->torow = rowidx(mstr[3]); 1.680 + } else if (len == 5) { 1.681 + if (strcmp(mstr, "O-O-O") == 0) { 1.682 + /* queen side castling "O-O-O" */ 1.683 + move->piece = KING; 1.684 + move->fromfile = fileidx('e'); 1.685 + move->tofile = fileidx('c'); 1.686 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 1.687 + } else { 1.688 + move->piece = getpiece(mstr[0]); 1.689 + if (mstr[2] == 'x') { 1.690 + move->capture = 1; 1.691 + if (move->piece) { 1.692 + /* capture (e.g. "Ndxf3") */ 1.693 + move->fromfile = fileidx(mstr[1]); 1.694 + } else { 1.695 + /* long notation capture (e.g. "e5xf6") */ 1.696 + move->piece = PAWN; 1.697 + move->fromfile = fileidx(mstr[0]); 1.698 + move->fromrow = rowidx(mstr[1]); 1.699 + } 1.700 + } else { 1.701 + /* long notation move (e.g. "Nc5a4") */ 1.702 + move->fromfile = fileidx(mstr[1]); 1.703 + move->fromrow = rowidx(mstr[2]); 1.704 + } 1.705 + move->tofile = fileidx(mstr[3]); 1.706 + move->torow = rowidx(mstr[4]); 1.707 + } 1.708 + } else if (len == 6) { 1.709 + /* long notation capture (e.g. "Nc5xf3") */ 1.710 + if (mstr[3] == 'x') { 1.711 + move->capture = 1; 1.712 + move->piece = getpiece(mstr[0]); 1.713 + move->fromfile = fileidx(mstr[1]); 1.714 + move->fromrow = rowidx(mstr[2]); 1.715 + move->tofile = fileidx(mstr[4]); 1.716 + move->torow = rowidx(mstr[5]); 1.717 + } 1.718 + } 1.719 + 1.720 + 1.721 + if (move->piece) { 1.722 + if (move->piece == PAWN 1.723 + && move->torow == (color==WHITE?7:0) 1.724 + && !move->promotion) { 1.725 + return NEED_PROMOTION; 1.726 + } 1.727 + 1.728 + move->piece |= color; 1.729 + if (move->fromfile == POS_UNSPECIFIED 1.730 + || move->fromrow == POS_UNSPECIFIED) { 1.731 + return getlocation(gamestate, move); 1.732 + } else { 1.733 + return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; 1.734 + } 1.735 + } else { 1.736 + return INVALID_MOVE_SYNTAX; 1.737 + } 1.738 +} 1.739 + 1.740 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 1.741 + uint8_t color) { 1.742 + 1.743 + Move threats[16]; 1.744 + uint8_t threatcount; 1.745 + if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { 1.746 + for (int i = 0 ; i < threatcount ; i++) { 1.747 + if (threats[i].piece != (color|KING)) { 1.748 + return 1; 1.749 + } 1.750 + } 1.751 + return 0; 1.752 + } else { 1.753 + return 0; 1.754 + } 1.755 +} 1.756 + 1.757 +uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, 1.758 + uint8_t color) { 1.759 + if (!gameinfo->timecontrol) { 1.760 + return 0; 1.761 + } 1.762 + 1.763 + if (gamestate->movelist) { 1.764 + uint16_t time = gameinfo->time; 1.765 + suseconds_t micros = 0; 1.766 + 1.767 + MoveList *movelist = color == WHITE ? 1.768 + gamestate->movelist : gamestate->movelist->next; 1.769 + 1.770 + while (movelist) { 1.771 + time += gameinfo->addtime; 1.772 + 1.773 + struct movetimeval *movetime = &(movelist->move.movetime); 1.774 + if (movetime->tv_sec >= time) { 1.775 + return 0; 1.776 + } 1.777 + 1.778 + time -= movetime->tv_sec; 1.779 + micros += movetime->tv_usec; 1.780 + 1.781 + movelist = movelist->next ? movelist->next->next : NULL; 1.782 + } 1.783 + 1.784 + time_t sec; 1.785 + movelist = gamestate->lastmove; 1.786 + if ((movelist->move.piece & COLOR_MASK) != color) { 1.787 + struct movetimeval *lastmovetstamp = &(movelist->move.timestamp); 1.788 + struct timeval currenttstamp; 1.789 + gettimeofday(¤ttstamp, NULL); 1.790 + micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec; 1.791 + sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec; 1.792 + if (sec >= time) { 1.793 + return 0; 1.794 + } 1.795 + 1.796 + time -= sec; 1.797 + } 1.798 + 1.799 + sec = micros / 1e6L; 1.800 + 1.801 + if (sec >= time) { 1.802 + return 0; 1.803 + } 1.804 + 1.805 + time -= sec; 1.806 + 1.807 + return time; 1.808 + } else { 1.809 + return gameinfo->time; 1.810 + } 1.811 +}